我一直在为我正在研究的一个遗传算法制作GUI,我犯了一个错误,因为我不知道(现在仍然不知道)如何去做,所以才这么晚才离开线程。因此,基本上,当单击start按钮时,函数run启动整个无限循环过程,这实际上发生在generation\u循环中。每一代循环都会检查它是否仍在运行。其思想是,如果单击了停止或暂停按钮,它将停止循环(使用停止按钮,所有数据将被清除,而使用暂停按钮,它将保留,取消暂停按钮只是将运行设置为True并调用生成\u循环)
因此,我需要找到一种方法,使我的GUI在第1代循环运行时具有响应性。这是我的代码,我尽量减少它,但我不确定什么是线程的重要信息:
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_())
编辑:我也刚刚发现,我甚至不能改变图形视图从生成循环,但它的工作和变化,如果我限制循环
您可以在这里使用线程事件。你知道吗
一旦你检测到按钮点击
现在,在您的代码中嵌入此代码段并保留此线程的引用。 比如说
现在,一旦你得到一个停止按钮,点击你调用的事件
为了将长时间运行的代码移动到线程中,您需要首先确定长时间运行的代码的哪些部分与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
。对象中由信号发射触发的插槽(也称为方法),然后在线程中运行。你知道吗所以你会想这样做
注意事项:
Population
,因此在代码中可能存在一些我看不到的线程不安全行为。我把它交给你修理Population
的实例化移到worker中的一个方法(一个不是__init__
的方法)中,并在每次您希望从头开始时调用它(就像我们触发新一代一样)。这将允许您将几乎所有的setupThread
移动到Window.__init__
方法,然后当单击开始按钮时,您只需发出一个信号来重新创建Population
,然后是一个信号来生成第一代。你知道吗相关问题 更多 >
编程相关推荐