【问题标题】:in Jython, is there a Pythonesque way of submitting a method to the EDT?在 Jython 中,是否有一种 Pythonesque 方式向 EDT 提交方法?
【发布时间】:2012-06-19 18:06:25
【问题描述】:

在 Python 中,人们习惯于这样做

def runTaskInNonEDT():
  pass
tRunTask = threading.Thread( target = runTaskInNonEDT )
tRunTask.start()

在 Jython 中,我发现如果我想向 EDT 提交方法,我必须去

def makeRunnableClass():
  class RunnableClass( Runnable ):
    def run( rSelf ):
      pass
  return RunnableClass
SwingUtilities.invokeAndWait( makeRunnableClass()() )

显然,您随后会遇到有关传递参数等的所有伴随问题。 我只是想知道是否有一种更快捷、更 Pythonesque 的方式来向 EDT 提交方法?

@lvc

谢谢……是的,事实上我明白了……实际上是成语

def makeSthgClass():
  class SthgClass():
    pass
  return SthgClass

是我习惯使用的一种,只是为了停止使用一次性子类实例的类名来混淆命名空间。

其实我有办法减轻任务

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()() )

这样你就可以走了

def doSthg():
  pass
self.runToMessageTree( doSthg )

...但它没有任何令人满意的 Pythonic。

稍后:

  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 是一个非常简单、经典的python习语:

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

所以你提交一个方法,然后是可选的 args ...然后这个方法最终在 EDT 中运行,使用有问题的 args。不必创建 Runnables...

当然,另一种可能性是从 SwingWorker 子类化...然后你就不会有这种稍微麻烦的“双队列”安排(即 WorkerThread 队列和 EDT 自己的队列,它传递给 process())。 ..但是你必须在doInBackground中有一个相当不雅的循环(使用sleep())......

会对人们的观点感兴趣

【问题讨论】:

  • 当你编辑你的问题时,即使你在里面说@person,它实际上并没有通知那个人——通常的做法是更新它,然后在他们的答案上发表评论(或@人在他们评论过的地方发表评论)说你已经更新了。
  • ...话虽如此,我还是做了同样的事情。请查看我的更新答案。

标签: python multithreading jython event-dispatch-thread


【解决方案1】:

要意识到的主要事情是SwingUtilities.invokeAndWait 需要一个单一方法接口的实例,因为 Java 没有一流的函数。如果不使用SwingUtilities 以外的其他东西来实现这个功能,那么这一点是不可避免的。

如果您决定使用该特定 API,您仍然可以避免使用包装函数。做吧:

class RunnableClass(Runnable):
    def run(self):
       pass

SwingUtilities.invokeAndWait(RunnableClass())

使用包装函数的唯一原因是能够使用传入函数来调用run 使用闭包;您仍然可以通过将函数传递到 RunnableClass.__init__ 并存储它来做到这一点:

class RunnableClass(Runnable):
    def __init__(self, func):
       self._func = func

    def run(self):
       self._func()

注意func 不应该self 作为第一个参数——因为它是实例而不是类的属性,所以它不会被视为方法。

根据您的编辑 - 将 func 传递给 RunnableClass.__init__ 的目的是它不再需要是一次性子类 - 对于每个功能,您不需要 Runnable 的一个子类'重新运行,只有RunnableClass 的一个实例。 类本身 是从 Python 习语到 Java 的通用适配器,因此您不需要围绕它的函数来完成相同的工作。

这意味着您的runToMessageTree 可能如下所示:

def runToMessageTree(self, method, *args, **kwargs):
    if SwingUtilities.isEventDispatchThread():
       method(*args, **kwargs)
    else:
       SwingUtilities.invokeAndWait(RunnableClass(method, *args, **kwargs))

【讨论】:

  • 感谢您。是的,这是一种巧妙的技术,而且非常符合 Python 风格。我想到的一件非常轻微的事情是它仍然涉及创建和提交 Runnable。我想到的另一种选择是拥有一个(python)线程子类以及一个封装的(python)队列和一个(Java)SwingWorker ... 工作得很好,我不知道性能提升(如果有的话)。已经把上面的代码,欢迎任何cmets
猜你喜欢
  • 1970-01-01
  • 2011-04-23
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 2019-02-28
  • 1970-01-01
  • 2015-05-22
  • 2012-07-08
相关资源
最近更新 更多