In Python one is used to doing
def runTaskInNonEDT(): pass tRunTask = threading.Thread( target = runTaskInNonEDT ) tRunTask.start()
In Jython, I find that if I want to submit a method to the EDT I have to go
def makeRunnableClass(): class RunnableClass( Runnable ): def run( rSelf ): pass return RunnableClass SwingUtilities.invokeAndWait( makeRunnableClass()() )
obviously you then have all the attendant questions about passing parameters, etc. I was just wondering if there might be a snappier, more Pythonesque way of submitting a method to the EDT?
thanks... yes in fact I get that... in fact the idiom
def makeSthgClass(): class SthgClass(): pass return SthgClass
is one I use habitually just to stop cluttering up the namespace with one-shot subclassed instances' classnames.
I have in fact got sthg to lighten the task
def runToMessageTree( self, method, *args, **kvargs ): if SwingUtilities.isEventDispatchThread(): method( *args, **kvargs ) else: def makeRunnableClass(): class RunnableClass( Runnable ): def run( self ): method( *args, **kvargs ) return RunnableClass SwingUtilities.invokeAndWait( makeRunnableClass()() )
so you can go
def doSthg(): pass self.runToMessageTree( doSthg )
... but there's nothing satisfyingly Pythonic about it.
class EDTWorkerThread( WorkerThread ): def __init__( ewt_self, name ): super( EDTWorkerThread, ewt_self ).__init__( name ) class EDTWorker( SwingWorker ): def doInBackground(self ): check_event_thread( False ) while True: method_call_elements = ewt_self.input_queue.get() if method_call_elements is None: # "poison pill" break self.super__publish( [ method_call_elements ]) ewt_self.input_queue.task_done() return def process( self, chunks ): check_event_thread( True ) for chunk in chunks: assert type( chunk ) is list assert chunk # i.e. must have at least one element! # check that first item is callable assert hasattr( chunk[ 0 ], "__call__" ) method_call_elements = chunk method_args = method_call_elements[ 1 : ] method_call_elements[ 0 ]( *method_args ) ewt_self.input_queue.task_done() ewt_self.swing_worker = EDTWorker() def run( self ): self.swing_worker.execute()
ẀorkerThread is a very simple, classic python idiom:
class WorkerThread( threading.Thread ): def __init__( self, *args, **kvargs ): threading.Thread.__init__( self, *args, **kvargs ) self.input_queue = Queue() def send( self, item ): assert type( item ) is list assert item # i.e. must have at least one element! # check that first item is callable assert hasattr( item[ 0 ], "__call__" ) self.input_queue.put( item ) def close( self ): self.input_queue.put( None ) self.input_queue.join() def run( self ): while True: method_call_elements = self.input_queue.get() if method_call_elements is None: # "poison pill" break method_args = method_call_elements[ 1 : ] method_call_elements[ 0 ]( *method_args ) self.input_queue.task_done() self.input_queue.task_done() return
so you submit a method followed by optional args ... and this method then ends up being run in the EDT, using the args in question. No Runnables have to be created...
Of course the other possibility is to subclass from SwingWorker... then you wouldn't have this slightly troubling "double-queue" arrangement (i.e. WorkerThread Queue, and the EDT's own queue, which delivers to process())... but then you have to have a rather inelegant loop (using sleep()) in doInBackground...
Would be interested in people's viewsAnswer1:
The major thing to realise is that
SwingUtilities.invokeAndWait expects an instance of a single-method interface because Java doesn't have first-class functions. That bit isn't avoidable without using something other than
SwingUtilities, with a more Pythonic interface, for this functionality.
If your heart is set on using that particular API, you can still avoid having the wrapper function. Just do:
class RunnableClass(Runnable): def run(self): pass SwingUtilities.invokeAndWait(RunnableClass())
The only reason for using the wrapper function is to be able to use pass a function in to invoke in
run using closures; you can still do this by passing the function into
RunnableClass.__init__ and storing it:
class RunnableClass(Runnable): def __init__(self, func): self._func = func def run(self): self._func()
func <em>shouldn't</em> take
self as a first parameter - since its an attribute on the instance rather than on the class, it doesn't get treated as a method.
Per your edit - the point of passing
RunnableClass.__init__ is that it doesn't need to be a one-shot subclass anymore - you don't need one subclass of
Runnable for every func you're going to run, just one instance of
RunnableClass. The <em>class itself</em> is a generic adapter from the Python idiom to the Java one, so you don't need a function around it to do the same job.
This means your
runToMessageTree can look like this:
def runToMessageTree(self, method, *args, **kwargs): if SwingUtilities.isEventDispatchThread(): method(*args, **kwargs) else: SwingUtilities.invokeAndWait(RunnableClass(method, *args, **kwargs))