跟进this Question以及tcaswell提供的解决方案我尝试采用imshow()
的代码来生成一个非冻结窗口,其中包含用于图像处理的滑块,例如高斯模糊过滤器。 (我在彼此的顶部绘制了两个图像,因为我想在稍后阶段显示部分透明的蒙版。)
我希望你们中的一些人可能觉得这很有用,虽然我仍然可以使用一些帮助。
编辑:您可以在下面的第三节中找到当前状态。我正在为希望深入了解详细信息的其他用户保留旧版本。
我派生了两个不同的工作代码,每个代码都有一些(次要)问题,我真的很感激一些建议。
第一个代码:
QSlider
线程正在运行。但是,您不能简单地单击滑块。有什么建议吗?imshow()
更快。我怎样才能加快速度呢?为了不进入QThread: Destroyed while thread is still running
我在closeEvent()
中放了一个time.sleep(1)。我知道这真的很糟糕,但如果没有新的旗帜我怎么能避免呢?
import time, sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from scipy import misc
from scipy import ndimage
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class ApplicationWindow(QtGui.QMainWindow):
get_data = QtCore.pyqtSignal()
close_request = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.thread = QtCore.QThread(parent=self)
self.worker = Worker(parent=None)
self.worker.moveToThread(self.thread)
self.create_main_frame()
self.close_request.connect(self.thread.quit)
self.startButton.clicked.connect(self.start_calculation)
self.stopButton.clicked.connect(self.stop_calculation)
self.worker.started.connect(self.thread.start)
self.worker.new_pixel_array.connect(self.update_figure)
self.slider.sliderPressed.connect(self.start_calculation)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.sliderReleased.connect(self.stop_calculation)
self.get_data.connect(self.worker.get_data)
self.thread.start()
def create_main_frame(self):
self.main_frame = QtGui.QWidget()
self.dpi = 100
self.width = 5
self.height = 5
self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.axis((0,512,0,512))
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.updateGeometry()
self.canvas.draw()
self.background = None
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
self.im1 = self.axes.imshow(misc.ascent(), cmap='bone', interpolation='lanczos', extent=[0,512,0,512], aspect=(1), animated=True)
self.im2 = self.axes.imshow(misc.lena(), cmap='afmhot', interpolation='lanczos', extent=[0,265,0,256], aspect=(1), animated=True)
self.startButton = QtGui.QPushButton(self.tr("Keep Calculating"))
self.stopButton = QtGui.QPushButton(self.tr("Stop Calculation"))
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setTracking(True)
self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
layout = QtGui.QGridLayout()
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.slider, 1, 0)
layout.addWidget(self.startButton, 2, 0)
layout.addWidget(self.stopButton, 3, 0)
self.main_frame.setLayout(layout)
self.setCentralWidget(self.main_frame)
self.setWindowTitle(self.tr("Gaussian Filter - Slider not clickable"))
def slider_value_changed(self):
#self.worker.blockSignals(False)
self.worker.slider = self.slider.value()
def start_calculation(self):
self.worker.exiting = False
self.worker.slider = self.slider.value()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
self.get_data.emit()
def stop_calculation(self):
self.worker.exiting = True
self.startButton.setEnabled(True)
self.stopButton.setEnabled(False)
self.cleanup_UI()
def update_figure(self, im1_data,im2_data):
self.canvas.restore_region(self.background)
self.im1.set_array(im1_data)
self.im2.set_array(im2_data)
self.axes.draw_artist(self.im1)
self.axes.draw_artist(self.im2)
self.canvas.blit(self.axes.bbox)
def cleanup_UI(self):
self.background = None
self.canvas.draw()
def closeEvent(self, event):
self.stop_calculation()
self.close_request.emit()
time.sleep(1)
## ugly workaround to prevent window from closing before thread is closed. (calculation takes time) How can this be avoided without additional flag?
event.accept()
class Worker(QtCore.QObject):
new_pixel_array = QtCore.pyqtSignal(np.ndarray,np.ndarray)
started = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtCore.QObject.__init__(self, parent)
self.exiting = True
self.slider = 0
@QtCore.pyqtSlot()
def get_data(self):
while self.exiting == False:
self.started.emit()
im1_data = self.gauss(misc.ascent(),self.slider)
im2_data = self.gauss(misc.lena(),self.slider)
self.new_pixel_array.emit(im1_data, im2_data)
print 'Slider Value: ', self.slider
def gauss(self,im,radius):
gaussed = ndimage.gaussian_filter(im, radius)
return gaussed
def main():
app = QtGui.QApplication(sys.argv)
form = ApplicationWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
第二个代码:
self.canvas.draw()
中调用cleanup_UI()
会以某种方式修复此问题。单击滑块时,计算执行一次,但如果拖动并释放滑块,则计算将以相同的值执行两次。为什么?我试图用blockSignals
捕捉到这个但有时(当滑块被快速拖动并释放时)图中的第二个图像没有正确更新。你通过两种不同的模糊来识别它。
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from scipy import misc
from scipy import ndimage
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class ApplicationWindow(QtGui.QMainWindow):
get_data = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.thread = QtCore.QThread(parent=self)
self.worker = Worker(parent=None)
self.worker.moveToThread(self.thread)
self.create_main_frame()
self.startButton.clicked.connect(self.start_calculation)
self.worker.new_pixel_array.connect(self.update_figure)
self.worker.done.connect(self.stop_calculation)
self.slider.sliderPressed.connect(self.start_calculation)
self.slider.valueChanged.connect(self.slider_value_changed)
self.slider.actionTriggered.connect(self.start_calculation)
self.get_data.connect(self.worker.get_data)
self.thread.start()
def create_main_frame(self):
self.main_frame = QtGui.QWidget()
self.dpi = 100
self.width = 5
self.height = 5
self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.axis((0,512,0,512))
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.updateGeometry()
self.canvas.draw()
self.background = None
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
self.im1 = self.axes.imshow(misc.ascent(), cmap='bone', interpolation='lanczos', extent=[0,512,0,512], aspect=(1), animated=True)
self.im2 = self.axes.imshow(misc.lena(), cmap='afmhot', interpolation='lanczos', extent=[0,265,0,256], aspect=(1), animated=True)
self.startButton = QtGui.QPushButton(self.tr("Do a Calculation"))
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setTracking(True)
self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
layout = QtGui.QGridLayout()
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.slider, 1, 0)
layout.addWidget(self.startButton, 2, 0)
self.main_frame.setLayout(layout)
self.setCentralWidget(self.main_frame)
self.setWindowTitle(self.tr("Gaussian Filter"))
def slider_value_changed(self):
#self.worker.blockSignals(False)
self.worker.slider = self.slider.value()
def start_calculation(self):
self.slider_value_changed()
self.worker.exiting = False
self.startButton.setEnabled(False)
self.get_data.emit()
def stop_calculation(self):
self.worker.exiting = True
self.startButton.setEnabled(True)
self.cleanup_UI()
def update_figure(self, im1_data,im2_data):
self.im1.set_array(im1_data)
self.im2.set_array(im2_data)
self.axes.draw_artist(self.im1)
self.axes.draw_artist(self.im2)
self.canvas.blit(self.axes.bbox)
def cleanup_UI(self):
self.canvas.restore_region(self.background)
#self.canvas.draw()
#self.worker.blockSignals(True)
class Worker(QtCore.QObject):
new_pixel_array = QtCore.pyqtSignal(np.ndarray,np.ndarray)
done = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtCore.QObject.__init__(self, parent)
self.exiting = True
self.slider = 0
@QtCore.pyqtSlot()
def get_data(self):
if self.exiting == False:
im1_data = self.gauss(misc.ascent(),self.slider)
im2_data = self.gauss(misc.lena(),self.slider)
self.new_pixel_array.emit(im1_data,im2_data)
print 'Calculation performed, Slider Value: ', self.slider
self.done.emit()
else: None
def gauss(self,im,radius):
gaussed = ndimage.gaussian_filter(im, radius)
return gaussed
def main():
app = QtGui.QApplication(sys.argv)
form = ApplicationWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
编辑:第三段代码(已解决的主要问题和更新率有限)
disconnect
实现的。restore_region
似乎仍然没有效果。 Queue()
返回结果。如果你看到一些改进的可能性,请告诉我。 我曾尝试切换到multiprocessing
模块并将计算放在Pool()
内,但它会引发Can't pickle...
错误。由于我对多处理完全陌生,我非常希望了解更多信息。
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from scipy import misc
from scipy import ndimage
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from threading import Thread
from Queue import Queue
class ApplicationWindow(QtGui.QMainWindow):
get_data = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.thread = QtCore.QThread(parent=self)
self.worker = Worker(parent=None)
self.worker.moveToThread(self.thread)
self.create_main_frame()
self.startButton.clicked.connect(self.start_calculation)
self.stopButton.clicked.connect(self.stop_calculation)
self.worker.started.connect(self.thread.start)
self.worker.new_pixel_array.connect(self.update_figure)
self.slider.actionTriggered.connect(self.start_calculation)
self.slider.valueChanged.connect(self.slider_value_changed)
self.worker.done.connect(self.stop_calculation)
self.get_data.connect(self.worker.get_data)
self.thread.start()
def create_main_frame(self):
self.main_frame = QtGui.QWidget()
self.dpi = 100
self.width = 5
self.height = 5
self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.axis((0,512,0,512))
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.updateGeometry()
self.background = None
self.canvas.draw()
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
self.im1 = self.axes.imshow(misc.ascent(), cmap='bone', interpolation='lanczos', extent=[0,512,0,512], aspect=(1), animated=True)
self.im2 = self.axes.imshow(misc.lena(), cmap='afmhot', interpolation='lanczos', extent=[0,265,0,256], aspect=(1), animated=True)
self.startButton = QtGui.QPushButton(self.tr("Start Calculation"))
self.stopButton = QtGui.QPushButton(self.tr("Stop Calculation"))
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setTracking(True)
self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
layout = QtGui.QGridLayout()
layout.addWidget(self.canvas, 0, 0)
layout.addWidget(self.slider, 1, 0)
layout.addWidget(self.startButton, 2, 0)
layout.addWidget(self.stopButton, 3, 0)
self.main_frame.setLayout(layout)
self.setCentralWidget(self.main_frame)
self.setWindowTitle(self.tr("Gaussian Filter"))
def slider_value_changed(self):
self.worker.slider = self.slider.value()
def start_calculation(self):
if self.worker.exiting:
self.slider.actionTriggered.disconnect(self.start_calculation)
self.worker.slider = self.slider.value()
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
self.get_data.emit()
self.worker.exiting = False
def stop_calculation(self):
if not self.worker.exiting:
self.slider.actionTriggered.connect(self.start_calculation)
self.worker.exiting = True
self.startButton.setEnabled(True)
self.stopButton.setEnabled(False)
self.cleanup_UI()
def update_figure(self, im1_data,im2_data):
#self.canvas.restore_region(self.background)
self.im1.set_array(im1_data)
self.im2.set_array(im2_data)
self.axes.draw_artist(self.im1)
self.axes.draw_artist(self.im2)
self.canvas.blit(self.axes.bbox)
def cleanup_UI(self):
self.background = None
self.canvas.draw()
class Worker(QtCore.QObject):
new_pixel_array = QtCore.pyqtSignal(np.ndarray,np.ndarray)
started = QtCore.pyqtSignal()
done = QtCore.pyqtSignal()
def __init__(self, parent = None):
QtCore.QObject.__init__(self, parent)
self.exiting = True
self.slider = 0
@QtCore.pyqtSlot()
def get_data(self):
while self.exiting == False:
self.started.emit()
queue1 = Queue()
queue2 = Queue()
im1T = Thread(target=self.gauss, args=(misc.ascent(),queue1))
im2T = Thread(target=self.gauss, args=(misc.lena(),queue2))
slider_val = self.slider
im1T.start()
im2T.start()
im1T.join()
im2T.join()
im1_data = queue1.get()
im2_data = queue2.get()
self.new_pixel_array.emit(im1_data, im2_data)
if slider_val == self.slider:
self.done.emit()
print 'Slider Value: ', self.slider
break
def gauss(self,im,output_queue):
gaussed = ndimage.gaussian_filter(im,self.slider)
output_queue.put(gaussed)
def main():
app = QtGui.QApplication(sys.argv)
form = ApplicationWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()