JTrout opened this issue on Nov 02, 2011 · 9 posts
JTrout posted Wed, 02 November 2011 at 11:33 AM
Hi
I can't implement multithreading code in PoserPython.
For example, Poser freezes when it runs the following simple script.
#----------------
import time
import threading
class MyThread(threading.Thread):
def init(self):
threading.Thread.init(self)
self.setDaemon(True)
def run(self):
print "Start1n"
for i in range(10):
print 'Thread1: %in' % i
time.sleep(2)
print "Completed1n"
def main():
thread = MyThread()
thread.start()
print "Start0n"
for i in range(10):
print 'Thread0: %in' % i
time.sleep(2)
print "Completed0n"
main()
#----------------
Of course, that works properly outside Poser as expected.
I tried with PoserPro2010.
Please tell me, if there is a solution.
Thank you
bagginsbill posted Wed, 02 November 2011 at 11:53 AM
Only one thread is allowed to use the UI, or in fact use the Poser API at all. Your print statement is a crash-maker, since it doesn't go to the OS, but rather to the wx API.
Also, you should know something about multi-threading in Python (in general, not specific to Poser). It's not multi-core. Two threads do not run literally at the same time. Only one thread runs within a process, because there is a mutex needed to gain access to the interpreter. Look up Global Interpreter Lock.
So - while you do create actual multiple threads, and these can interrupt each other and cause crashes due to non-atomic API calls, you get zero performance benefit. It can be helpful for keeping track of multiple complex activities with waiting being handled automatically, but that's all it's good for.
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
JTrout posted Wed, 02 November 2011 at 1:06 PM
Thank you very much for the clear answer.
I understood it.
I must think about another method if those are the specifications of Poser.
Because a so-called parallel computation is not my purpose, I do not care even about imitative thread of Python.
However, I grope for other method, at any rate.
As for me, I wanted to implement the following user interface.
Actually, my Python program which has such a user interface is working suitably outside Poser.
Of course a thread-safe mechanism is made consciously.
At any rate, I must reconsider design fundamentally to work it into Poser.
Thank you again.
bagginsbill posted Wed, 02 November 2011 at 5:34 PM
You want to use a thread safe queue where background threads can post strings for display, but not actually display them.
Then, set up a timer from the UI thread that pulls messages from the queue and shows them in a wx dialog window. This way you can write code to do long background work and not worry about output from there.
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
JTrout posted Thu, 03 November 2011 at 7:01 AM
I noticed that now by your suggestion though I was a little mistaken.
I was mistaken when multithreading of Python in Poser was unacceptable.
A print statement for debugging had been included in the code which worried me.
I should have noticed it when a print statement was used for a last example.
(I may be very tired now by the long programming work.)
The following code actually works in Poser.
#--------------------
import time
import threading
import Queue
class MyThread(threading.Thread):
def init(self, id, aFiFo):
threading.Thread.init(self)
self.id = id
self.queue = aFiFo
self.setDaemon(True)
def run(self):
for i in range(5):
self.queue.put("[%i] %i" % (self.id, i), True)
time.sleep(1)
def main():
queue = Queue.Queue()
thread1 = MyThread(1, queue)
thread2 = MyThread(2, queue)
thread1.start()
thread2.start()
print "Startn"
while True: # the break condition is irresponsible
try:
print queue.get(True, 1)
except Queue.Empty:
break
time.sleep(1)
print "Completed"
main()
#--------------------
I don't have that vigor / reserve power now though I may have to write it a little more in detail.
At any rate, I could probably get a solution.
Thank you.
bagginsbill posted Thu, 03 November 2011 at 7:22 AM
There you go - that is very close.
If, in the future, you would like to send things other than strings to the queue, then just do so. In the consumer thread, you can examine the objects sent to see if they are strings or something more complicated. In this way, the worker threads can deliver more than just messages. They can deliver completed work to the main thread.
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
JTrout posted Thu, 03 November 2011 at 9:01 AM
A so-called queuing model itself is easy to understand.
Therefore, I think that "the program that only main thread calls API of wx" is the most reliable.
However, won't the event hand ring mechanism of wx have a more convenient structure?
I am expecting that it is combination with the event hand ring model of wx that I seem probably to have difficulty.
Or, it may be actually easy.
Though the superficial use of wx is easy, I may not have understood the event hand ring mechanism of this API yet. An AUI manager, too.
I try after I rest a little.
Thank you.
By the way, Isn't the stub package of Poser released by anyone?
Though I like Eclipse and PyDev, I don't know how to import a poser package outside of Poser.
If that is possible (probably impossible), it is the most desirable.
Only so long as it is necessary, I am making the stub package of Poser unwillingly gradually.
I should use that stub if perfect stub has been already released by someone.
bagginsbill posted Thu, 03 November 2011 at 9:23 AM
You are correct - you could use wx event handling. The reason I tend not to use such things is they have a lot of bookkeeping. You have to:
Get an unique event ID
Create a class to represent the event
Bind a listener callback for the event.
...
Remember to later unbind a listener callback for the event.
Furthermore, the whole thing goes through one queue, so if you have several independent activities, they all funnel through the main event handler and you have to sort out who is doing what to whom. I prefer a dedicated queue for each group of master/slave components. But this is a simple matter either way.
I am not aware of a stub poser module.
Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)
JTrout posted Sun, 06 November 2011 at 8:47 AM
Hi, again
I can't get the expected behavior of callback in Poser.
It seems the event can't get the chance when it is handled.
The following sample code doesn't do an expected behavior with poser.
The program which has the same event structure do an expected behavior without poser.
What do I mistake?
#-------------------------------------------------------------
import poser
import wx, wx.aui, wx.lib.newevent
import threading, time
(UpdateButtonEvent, EVT_UPDATE_BUTTON) = wx.lib.newevent.NewEvent()
(UpdateCountEvent, EVT_UPDATE_COUNT) = wx.lib.newevent.NewEvent()
class UserEvaluationView(wx.Panel):
def init(self, model, *args, *kwds):
super(UserEvaluationView, self).init(args, kwds)
self.countLabel = wx.StaticText(id=wx.ID_ANY,parent=self, label='', name='count', size = (100,30), style=0)
self.startButton = wx.Button(self, wx.ID_ANY,"Start", size=(45,20))
self.startButton.Bind(wx.EVT_BUTTON, self.OnStart)
self.abortButton = wx.Button(self, wx.ID_ANY,"Abort", size=(45,20))
self.abortButton.Bind(wx.EVT_BUTTON, self.OnAbort)
self.abortButton.Disable()
vLayout1 = wx.BoxSizer(wx.VERTICAL)
vLayout1.Add(self.countLabel, flag=wx.ALIGN_LEFT|wx.ALL, border=5)
vLayout1.Add(self.startButton, flag=wx.ALIGN_LEFT|wx.ALL, border=5)
vLayout1.Add(self.abortButton, flag=wx.ALIGN_LEFT|wx.ALL, border=5)
self.SetSizerAndFit(vLayout1)
** self.Bind(EVT_UPDATE_BUTTON, self.OnUpdateButton)
** self.Bind(EVT_UPDATE_COUNT, self.OnUpdateCount)
self.thread = None
def OnStart(self, event):
if self.thread == None:
self.thread = TestThread(self)
self.thread.start()
def OnAbort(self, event):
if self.thread != None:
self.thread.abort()
def setForRunning(self):
self.startButton.Disable()
self.abortButton.Enable()
def setForStop(self):
self.startButton.Enable()
self.abortButton.Disable()
def OnUpdateButton(self, event):
if event.name == "start":
self.setForRunning()
elif event.name== "stop":
self.setForStop()
self.Refresh()
def OnUpdateCount(self, event):
self.countLabel.SetLabel('%3i' % event.count)
class TestThread(threading.Thread):
def init(self, aView):
threading.Thread.init(self)
self.view = aView
self.setDaemon(True)
self.abortInvoked = False
def run(self):
** event = UpdateButtonEvent(name="start")**
** wx.PostEvent(self.view, event) **
for i in range(20): # for safety
if self.abortInvoked:
break
** event = UpdateCountEvent(count=i)**
** wx.PostEvent(self.view, event)**
# Actually, various processing is put here.
time.sleep(1) # TBD
** event = UpdateButtonEvent(name="stop")**
** wx.PostEvent(self.view, event)**
self.view.thread = None
def abort(self):
self.abortInvoked=True
def main():
model = None # dummy
man = poser.WxAuiManager()
root = man.GetManagedWindow()
panel = UserEvaluationView(model, parent=root)
pinfo = wx.aui.AuiPaneInfo()pinfo.CloseButton(True).DestroyOnClose(True).Resizable().Float().FloatingSize(wx.Size(200, 200)).BestSize(wx.Size(200, 200))
man.AddPane(panel, pinfo)
pinfo.Show()
man.Update()
main()
#-------------------------------------------------------------