~~SPAM~~ Wx.Timer Main Thread Problem Help !

Hi WxPython Experts !
I have written a caller application,
it sits in the sys tray and waits for the tapi client to say there is an
incoming call.
When a call comes in

  1. the remote_call function (in the
    TwistedClient Class) takes the details
  2. it passes this on to the onIncomingCall
    function (in the MailFrame class)
  3. this function is supopsed to run
    the Fader() class which in turn pops up the fading window !
    I tested it by setting one of the menu
    items to trigger the fade window, which worked just fine. But when I call
    my phone it gives me the error "timercan
    only be started from the main thread"

I suspect this is because the TAPI client
is running on its own thread but i’ve no idea how to fix it ? is there
any way around this problem. My wxpython knowledge is not great ! Here
is the relevant code from the app (sorry its not the best of code as I
was trying to get it working before tidying it up !)

#-STARTOFCODE

SERVER = “marvin”

PORT = 8800

ICON = “working.ico”

APPNAME = “tapiclient”

RECEPTIONPCNAME = “optiplex989”

ID_ICON_TIMER=wx.NewId()

OPEN_VOICEMAIL=wx.NewId()

OPEN_PHONEBOOK=wx.NewId()

OPEN_FADETEST=wx.NewId()

class TwistedClient(pb.Referenceable,Thread):

def __init__(self, parent):

    self.parentApp

= parent

    Thread.__init__(self)

    self.setDaemon(True)

   

    self.start()

   

def remote_call(self,

details):

    detail =

details.split(",")

    call  

= detail[0]

    caller =

detail[1]

    company

= detail[2]

    print "remote_call"

    self.parentApp.onIncomingCall('abc')

    #self.app.onIncomingCall(call,

caller, company)

def makeCall(self,number):

    c = self.perspective.callRemote("call",number)

    c.addErrback(self.makeCallErr)

def makeCallErr(self,failure):

    try:

raise failure.trap(tapiserver.NoSuchLineError,

               tapiserver.LineInUseException,

       
               tapiserver.CallCreationError)

    except tapiserver.LineInUseException:

getWxAppsModule().showError(“Sorry”,“Couldn’t make call:
Your line appears to be in use.”)

    except tapiserver.NoSuchLineError:

getWxAppsModule().showError(‘Sorry’,“Couldn’t find you phone line.\n\nPicking
up your phone and putting it down again should fix this”)

    except tapiserver.CallCreationError:

getWxAppsModule().showError(‘Sorry’,“Couldn’t create the call, are
you sure the number’s valid?”)

def run(self):

    self.connect()

   

def connect(self):

    factory

= pb.PBClientFactory()

    global username

    try:

reactor.connectTCP(SERVER, PORT, factory)

    except:

logging.error(“Couldn’t connect to server %s:%s”%(SERVER,PORT))

    if socket.gethostname()

== RECEPTIONPCNAME: username = ‘main’

    else: username

= os.environ[‘username’]

    logging.info(os.environ['username'])

    def1 = factory.login(credentials.UsernamePassword(username,

‘’),client=self)

    def1.addCallbacks(self.connected,self.failure)

    reactor.run(installSignalHandlers=0)

def connected(self, perspective):

    # this perspective

is a reference to our User object

    self.perspective

= perspective

    perspective.callRemote("register")

def failure(self,_):

    logging.debug("Connection

to server failed")

    self.shutdown()

    self.app.quit()

def shutdown(self):

    reactor.stop()

class MailTaskBarIcon(wx.TaskBarIcon):

def __init__(self, parent):

    wx.TaskBarIcon.__init__(self)


    self.parentApp

= parent

    self.noMailIcon

= wx.Icon(“test.png”,wx.BITMAP_TYPE_PNG)

    self.youHaveMailIcon

= wx.Icon(“test.png”,wx.BITMAP_TYPE_PNG)

    self.CreateMenu()


    self.SetIconImage()


def CreateMenu(self):

    self.Bind(wx.EVT_TASKBAR_RIGHT_UP,

self.ShowMenu)

    self.Bind(wx.EVT_MENU,

self.parentApp.onIncomingCall, id=OPEN_VOICEMAIL)

    self.Bind(wx.EVT_MENU,

self.parentApp.OpenPhoneBook, id=OPEN_PHONEBOOK)

    self.Bind(wx.EVT_MENU,

self.parentApp.FadeTest, id=OPEN_FADETEST)

    self.menu=wx.Menu()


    self.menu.Append(OPEN_FADETEST,

“FADETEST”)

    self.menu.AppendSeparator()

    self.menu.Append(OPEN_VOICEMAIL,

“Check VoiceMail”)

    self.menu.Append(OPEN_PHONEBOOK,

“Crummock Phonebook”)

    self.menu.AppendSeparator()


    self.menu.Append(wx.ID_EXIT,

“Quit”)

def ShowMenu(self,event):


    self.PopupMenu(self.menu)


def SetIconImage(self,

mail=False):

    self.SetIcon(self.noMailIcon,

“Crumomck CallerID V2.0”)

class MailFrame(wx.Frame):

def __init__(self, parent,

id, title):

    self.tapiManager

= TwistedClient(self)

    comserver.loadServer(self.tapiManager)

    wx.Frame.__init__(self,

parent, -1, title, size = (1, 1),

style=wx.FRAME_NO_TASKBAR|wx.NO_FULL_REPAINT_ON_RESIZE)

    self.tbicon

= MailTaskBarIcon(self)

    self.tbicon.Bind(wx.EVT_MENU,

self.exitApp, id=wx.ID_EXIT)

    self.Show(True)


def voiceMail(self,app=None):

    print "Voicemail

dialling …"

    self.tapiManager.makeCall("*17")

   

def callNumber(self,number,app=None):

    self.tapiManager.makeCall(number)

def onIncomingCall(self,

event):

    print "onIncomingCall"

    self.FadeTest('abc')

   

def FadeTest(self, abc):

    print "running

FadeTest"

    frm = Fader()

    frm.Show()

def exitApp(self,event):


    self.tbicon.RemoveIcon()


    self.tbicon.Destroy()


    sys.exit()


def OpenPhoneBook(self,event):

    self.frame

= wx.Frame(self, wx.ID_ANY, “Phonebook”, size=(200, 350))

    self.listbox

= wx.ListBox(self.frame, wx.ID_ANY, choices=[], size=wx.Size(195, 300),
style=0)

    self.listbox.Bind(wx.EVT_LISTBOX,

self.OnListBox1Listbox)

    self.button

= wx.Button(self.frame, id=wx.ID_ANY, label=u’Select person to call’,

name=‘button’, pos=wx.Point(0, 300),

size=wx.Size(193, 24), style=0)

    self.button.Bind(wx.EVT_BUTTON,

self.MakeCall, id=wx.ID_ANY)

    global numbers

    numbers

= {}

    filepath

= r’\leo\c$\tools\dat\agent_out.txt’

    file = open(filepath,

‘r’)

    for line

in file.readlines():

mywords = line.split(’,’)

if (mywords[0][:9] == ‘901875825’):

numbers[mywords[1]] = mywords[0][9:]

    for item

in sorted(numbers):

self.listbox.Append(item)

    self.frame.Center()

    self.frame.Show(True)

    return True

def OnListBox1Listbox(self,

event):

    '''

    click list

item and display the selected string in frame’s title

    '''

    selName

= self.listbox.GetStringSelection()

    if numbers.has_key(selName):

self.number = numbers[selName]

self.button.SetLabel(“Call “+ selName + " (”+self.number+”)")

def MakeCall(self, event):

    m.callNumber(self.number)

    self.frame.Show(False)

class Fader(wx.Frame):

def __init__(self):

    print "Running

fader…"

    popWidth

= 250

    popHeight

= 100

    displayRect

= wx.GetClientDisplayRect()

    posWidth

= (displayRect.width - 10 ) - popWidth

    posHeight

= (displayRect.height -10) - popHeight

    self.frame

= wx.Frame.init(self, None, title=‘Test’, pos=(posWidth, posHeight),
size=(popWidth, popHeight), style=wx.STAY_ON_TOP | wx.FRAME_NO_TASKBAR)

    self.amount

= 10

    self.delta

= 10

    self.panel

= wx.Panel(self, wx.ID_ANY, pos=(0, 0), size=(popWidth, 20))

    self.panel.SetBackgroundColour(wx.Colour(220,

220, 220))

    self.CloseButton

= wx.BitmapButton(self.panel, wx.ID_ANY, wx.Bitmap(“close.bmp”),
pos=(228, 0), size=(20, 20), style=0)

    self.Bind(wx.EVT_BUTTON,

self.OnClose, self.CloseButton)

    self.panel2

= wx.Panel(self, wx.ID_ANY, pos=(0, 20), size=(popWidth, 80))

    self.panel2.SetBackgroundColour(wx.Colour(238,

238, 238))

    self.calltype

= wx.StaticText(self.panel, wx.ID_ANY, “Internal Call”, pos=(88,2))

    self.calltype.SetFont(wx.Font(8,

wx.SWISS, wx.NORMAL, wx.BOLD))

    self.calltype.SetBackgroundColour(wx.Colour(220,

220, 220))

    self.calltype.SetForegroundColour(wx.Colour(100,

100, 100))

    self.callImage

= wx.StaticBitmap(self.panel2, wx.ID_ANY, wx.Bitmap(“call.bmp”),
size=(22, 22), pos=(2,5))

    self.callNumber

= wx.StaticText(self.panel2, wx.ID_ANY, “01234 567890”, pos=(28,5))

    self.callNumber.SetFont(wx.Font(14,

wx.SWISS, wx.NORMAL, wx.BOLD))

    self.callImage

= wx.StaticBitmap(self.panel2, wx.ID_ANY, wx.Bitmap(“person.bmp”),
size=(20, 31), pos=(4,37))

    self.caller

= wx.StaticText(self.panel2, wx.ID_ANY, “Username”, pos=(28,37))

    self.caller.SetFont(wx.Font(10,

wx.SWISS, wx.NORMAL, wx.BOLD))

    self.caller.SetBackgroundColour(wx.Colour(238,

238, 238))

    self.company

= wx.StaticText(self.panel2, wx.ID_ANY, “Company Name”, pos=(29,54))

    self.company.SetFont(wx.Font(8,

wx.SWISS, wx.NORMAL, wx.BOLD))

    self.company.SetBackgroundColour(wx.Colour(238,

238, 238))

    self.SetTransparent(self.amount)



    # -------

Fader Timer -------- ##

    self.timer

= wx.Timer(self, wx.ID_ANY)

    self.timer.Start(30)

    self.Bind(wx.EVT_TIMER,

self.AlphaCycleUp)

    # ----------------------------
···

def AlphaCycleUp(self,

evt):

    self.amount

+= self.delta

    if self.amount

= 230:

self.timer.Stop()

self.delta = -5

------- Fader Timer --------

self.timer2 = wx.Timer(self, wx.ID_ANY)

self.timer2.Start(5000)

self.Bind(wx.EVT_TIMER, self.AlphaWait)

----------------------------

self.amount = 230

    self.SetTransparent(self.amount)



def AlphaWait(self, evt):

    self.timer2.Stop()

    self.timer3

= wx.Timer(self, wx.ID_ANY)

    self.timer3.Start(60)

    self.Bind(wx.EVT_TIMER,

self.AlphaCycleDown)

def AlphaCycleDown(self,

evt):

    self.amount

+= self.delta

    if self.amount

<= 0:

self.timer3.Stop()

self.amount = 0

    self.SetTransparent(self.amount)

def OnClose(self, evt):

    self.timer.Stop()

    try:

self.timer2.Stop()

    except:

print “Can’t stop timer2”

    try:

self.timer3.Stop()

    except:

print “Can’t stop timer3”

    self.Destroy()

#---------------- run the program -----------------------

def go():

app = wx.App(False)

MyApp = MailFrame(None,

-1, ’ ')

MyApp.Center(wx.BOTH)


MyApp.Show(False)  

app.MainLoop()

#m.tapiManager.shutdown()

if name == ‘main’:

go()

#-ENDOFCODE

Thanks !

Christopher McEwan

Crummock (Scotland) Limited is a limited company registered in Scotland.

Registered company no.: 130376

Registered office: Crummock, Butlerfield Ind. Est., Bonnyrigg, Midlothian, EH19 3JQ

Crummock may monitor email traffic data and also the content of email for the purposes of security.

Scanned by MIMEDefang

Chris McEwan wrote:

Hi WxPython Experts !

I have written a caller application, it sits in the sys tray
and waits for the tapi client to say there is an incoming call.

When a call comes in
1) the remote_call function (in the TwistedClient Class)
takes the details
2) it passes this on to the onIncomingCall function (in the
MailFrame class)
3) this function is supopsed to run the Fader() class which
in turn pops up the fading window !

I tested it by setting one of the menu items to trigger the
fade window, which worked just fine. But when I call my phone
it gives me the error "timercan only be started from the main thread"

I suspect this is because the TAPI client is running on its
own thread but i've no idea how to fix it ? is there any way
around this problem. My wxpython knowledge is not great !
Here is the relevant code from the app (sorry its not the
best of code as I was trying to get it working before tidying
it up !)

There is a simple rule - calls to the wx system can only be made from the
same thread that wx is running in. Therefore I am pretty sure that your
suspicion is correct.

There is an equally simple solution - use wx.CallAfter(). This tells wx to
call the specified function after all pending events have been completed.

In your case, instead of
    self.FadeTest('abc')
try
    wx.CallAfter(self.FadeTest,'abc')

HTH

Frank Millman

`>There is a simple rule - calls to the wx system
can only be made from the

same thread that wx is running in. Therefore I am pretty sure that
your

suspicion is correct.

There is an equally simple solution - use wx.CallAfter(). This tells
wx to

call the specified function after all pending events have been completed.

In your case, instead of

self.FadeTest(‘abc’)

try

wx.CallAfter(self.FadeTest,‘abc’)

HTH

Frank Millman`

Frank,

You are a GENIUS !

Seriously, you have no idea how long
I have been trying to fix it !

Thank you !

Crummock (Scotland) Limited is a limited company registered in Scotland.

Registered company no.: 130376

Registered office: Crummock, Butlerfield Ind. Est., Bonnyrigg, Midlothian, EH19 3JQ

Crummock may monitor email traffic data and also the content of email for the purposes of security.

Scanned by MIMEDefang