gpt4 book ai didi

python - 我怎么能在开发这么晚的时候线程化这段代码呢?

转载 作者:太空宇宙 更新时间:2023-11-04 00:41:41 26 4
gpt4 key购买 nike

我一直在为我正在研究的遗传算法制作 GUI,我犯了这么晚才离开线程的错误,这仅仅是因为我不知道(现在仍然不知道)该怎么做。因此,基本上当单击开始按钮时,函数“运行”将启动整个无限循环过程,这实际上发生在 generation_loop 中。每一代循环都会检查它是否应该仍在运行。这个想法是,如果单击了停止或暂停按钮,它将停止循环(使用停止按钮,所有数据都被清除,暂停按钮仍然存在,取消暂停按钮只是将运行设置为 True 并调用 generation_loop)

所以我需要找到一种方法,让我的 GUI 在 generation_loop 运行时响应。这是我的代码,我试图将它最小化,但我不确定什么是线程的重要信息:

class Window(main_window, QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
main_window.__init__(self)
self.setupUi(self)

self.scene = QGraphicsScene()
self.im_view.setScene(self.scene)
self.setWindowTitle('Fantasy Generator')
self.running = False
self.first_run = True
self.im = Image.new('RGBA', (400, 400), (0, 0, 0, 255))
self.saved_gens = deque([('A', self.im, self.im, self.im)])
self.set_save_amount(self.sb_saveamt.value())
self.population = []

self.btn_exit.clicked.connect(self.close)
self.actionQuit.triggered.connect(self.close)
self.btn_pauser.clicked.connect(self.pause_button)
self.sb_saveamt.valueChanged[int].connect(self.set_save_amount)
self.btn_restart.clicked.connect(self.start_button)
self.btn_loadimage.clicked.connect(self.get_image)
self.actionLoad_Image.triggered.connect(self.get_image)
self.gen_sldr.valueChanged[int].connect(self.display_gen)
self.cb_display.currentIndexChanged.connect(self.change_quality)

self.has_image = True
self.display_gen(0)

def get_image(self):
pass
# To save you time I removed the code here. It just sets self.im using a file dialog basically

def set_save_amount(self, amt):
if amt == -1:
self.saved_gens = deque(self.saved_gens)
else:
self.saved_gens = deque(self.saved_gens, amt + 1)

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')
self.generation_loop()
# resume from pause stuff goes here

def start_button(self):
if self.first_run:
self.run()
else:
self.end()

# The run function should start the actual process
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.population = Population(self.im, **settings)
self.generation_loop()

# This is the loop I want to be able to exit out of using buttons
def generation_loop(self):
while self.running:
if self.first_run:
break
self.add_generation_data(self.population.next_gen())

def end(self):
self.btn_restart.setText('Start')
self.btn_pauser.setText('Start Execution')
self.first_run = True
self.running = False

self.saved_gens = deque([('A', self.im, self.im, self.im)])
self.set_save_amount()
self.display_gen(0)

def add_generation_data(self, data):
self.saved_gens.append(data)
self.gen_sldr.setMaximum(len(self.saved_gens) - 1)
self.gen_sldr.setValue(len(self.saved_gens) - 1)
self.display_gen(data[0] + 1)

def change_quality(self):
self.display_gen(self.gen_sldr.value())

def resizeEvent(self, e):
if self.has_image:
self.im_view.fitInView(QRectF(0, 0, self.width, self.height), Qt.KeepAspectRatio)
self.scene.update()

def display_image(self, image):
self.scene.clear()
if image.mode != 'RGBA':
image = image.convert('RGBA')
self.width, self.height = image.size
qim = ImageQt.ImageQt(image)
pixmap = QPixmap.fromImage(qim)
self.scene.addPixmap(pixmap)
self.im_view.fitInView(QRectF(0, 0, self.width, self.height), Qt.KeepAspectRatio)
self.scene.update()

def display_gen(self, index):
self.lcd_cur_gen.display(self.saved_gens[index][0])

if self.cb_display.currentIndex() == 0:
self.display_image(self.saved_gens[index][1])
elif self.cb_display.currentIndex() == 1:
self.display_image(self.saved_gens[index][2])
else:
self.display_image(self.saved_gens[index][3])



if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())

编辑:我还刚刚发现我什至无法从 generation_loop 中更改图形 View ,但如果我限制循环它会起作用并发生变化

最佳答案

为了将长时间运行的代码移至线程,您需要首先确定长时间运行的代码的哪些部分与 GUI 交互,哪些部分不与 GUI 交互。这样做的关键原因是禁止从辅助线程与 GUI 交互,这将导致段错误。

它看起来像 self.population.next_gen() 是代码的长时间运行位并且不与 GUI 交互(尽管没有提供它的作用所以我不能确定)同时 self.add_generation_data(...) 更新 GUI,这应该相当快。

因此,这使得分离变得相当简单,我将在下面展示。

现在,关于线程。 Python 通过 threading 模块提供线程(如其他答案所示),但是如果您希望线程与 GUI 有任何关系,则不建议将这些用于 PyQt 应用程序(参见 here ). PyQt 还通过 QThread 对象提供线程,它集成了对发送和接收 Qt 信号(线程安全)的支持。简而言之,QThread 有一个单独的事件循环,并处理异步接收到主线程的信号,从而将事件循环留在主线程中以处理 GUI 事件(如按钮点击)。

通常,您创建一个继承自 QObject 的新类,将其实例化并将其移至 QThread。对象中的插槽(又名方法)由信号发射触发,然后在线程中运行。

所以你会想做这样的事情

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

注意事项:

  • 我没有在我的回答中包含您的所有代码。适当合并。
  • 我不确定 self.im 是什么。它已传递给 Population,因此您的代码中可能存在一些我看不到的线程不安全行为。我留给你解决了
  • 我熟悉 PyQt4,而不是 PyQt5,因此我所做的某些事情可能不太正确。您应该很容易从出现的任何错误消息中找出要更改的内容。
  • 每次从头开始重新创建线程和工作线程有点麻烦。您可能需要考虑将 Population 的实例化移动到 worker 中的一个方法(不是 __init__ 的方法,并在每次您想从头开始时调用它(在与我们触发新一代的方式相同)。这将允许您将几乎所有的 setupThread 移动到 Window.__init__ 方法,然后在单击开始按钮时,你只需发出一个信号来重新创建 Population,然后再发出一个信号来创建第一代。

关于python - 我怎么能在开发这么晚的时候线程化这段代码呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41689416/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com