<p>为了将长时间运行的代码移动到线程中,您需要首先确定长时间运行的代码的哪些部分与GUI交互,哪些部分不与GUI交互。这样做的关键原因是禁止从辅助线程与GUI交互,这将导致错误。你知道吗</p>
<p>它看起来像<code>self.population.next_gen()</code>是代码的长时间运行位,并且不与GUI交互(尽管没有提供这样做的功能,所以我不能确定),而<code>self.add_generation_data(...)</code>更新GUI应该相当快。你知道吗</p>
<p>因此,这使得分离变得相当简单,我将在下面展示。你知道吗</p>
<p>现在,关于线程。Python通过<code>threading</code>模块提供线程(如其他答案所示),但是如果希望线程与GUI有任何关系,则不建议将这些线程用于PyQt应用程序(请参见<a href="https://stackoverflow.com/q/1595649/1994235">here</a>)。PyQt还通过<code>QThread</code>对象提供线程,该对象集成了对发送和接收Qt信号(线程安全)的支持。简而言之,<code>QThread</code>有一个单独的事件循环,并处理异步接收到的信号到主线程,从而将事件循环留在主线程中处理GUI事件(如按钮单击)。你知道吗</p>
<p>通常创建一个从<code>QObject</code>继承的新类,实例化它并将其移动到<code>QThread</code>。对象中由信号发射触发的插槽(也称为方法),然后在线程中运行。你知道吗</p>
<p>所以你会想这样做</p>
<pre><code>class MyWorker(QObject):
done = pyqtSignal(object) # you may need to update "object" to the type returned by Population.next_gen()
def __init__(self, settings):
# create the population object with whatever settings you need
# Note that this method runs in the parent thread as you have
# yet to move the object to a new thread. It shouldn't cause any
# problems, but may depend on what the Population class is/does.
# TODO: I've removed the reference to an image here...
#it may or may not be thread safe. I can't tell from your code.
self.population = Population(..., settings)
@pyqtSlot()
def next_gen(self):
new_gen = self.population.next_gen()
self.done.emit(new_gen)
class Window(....):
make_next_generation = pyqtSignal()
....
def run(self):
self.btn_restart.setText('End')
self.btn_pauser.setText('Pause Execution')
self.first_run = False
self.running = True
settings = dict(ind_per_gen=self.sb_ipg.value(), shapes_per_im=self.sb_spi.value(),
complexity=self.sb_complexity.value(), mut_rate=self.sb_mutation.value(),
cross_chance=self.sb_cross.value(), seed=self.sb_seed.value())
self.setupThread(settings)
def setupThread(self, settings):
self.thread = QThread()
self.worker = MyWorker(settings)
self.worker.moveToThread(self.thread)
# connect a signal in the main thread, to a slot in the worker.
# whenever you emit the signal, a new generation will be generated
# in the worker thread
self.make_next_generation.connect(self.worker.next_gen)
# connect the signal from the worker, to a slot in the main thread.
# This allows you to update the GUI when a generation has been made
self.worker.done.connect(self.process_generation)
# Start thread
self.thread.start()
# emit the signal to start the process!
self.make_next_generation.emit()
def process_generation(new_gen):
# run the GUI side of the code
# ignore the new generation if the "end" button was clicked
if not self.first_run:
self.add_generation_data(new_gen)
if self.running:
# make another generation in the thread!
self.make_next_generation.emit()
def pause_button(self):
if self.first_run:
self.run()
elif self.running:
self.running = False
self.btn_pauser.setText('Resume Execution')
# pause stuff goes here
else:
self.running = True
self.btn_pauser.setText('Pause Execution')
# make another generation in the thread!
self.make_next_generation.emit()
</code></pre>
<p>注意事项:</p>
<ul>
<li>我的答案中没有包括你所有的代码。酌情合并。你知道吗</li>
<li>我不确定是什么自我im是。它被传递给<code>Population</code>,因此在代码中可能存在一些我看不到的线程不安全行为。我把它交给你修理</li>
<li>我熟悉PyQt4,而不是PyQt5,所以有可能我做的一些事情不太对劲。您应该可以很容易地从任何引发的错误消息中找出要更改的内容。你知道吗</li>
<li>每次从头开始重新创建线程和工作线程都有点麻烦。您可能需要考虑将<code>Population</code>的实例化移到worker中的一个方法(一个不是<code>__init__</code>的方法)中,并在每次您希望从头开始时调用它(就像我们触发新一代一样)。这将允许您将几乎所有的<code>setupThread</code>移动到<code>Window.__init__</code>方法,然后当单击开始按钮时,您只需发出一个信号来重新创建<code>Population</code>,然后是一个信号来生成第一代。你知道吗</li>
</ul>