【问题标题】:Plots created within a thread do not appear在线程中创建的图不出现
【发布时间】:2014-11-25 11:51:49
【问题描述】:

我是 Python 新手,想请你帮忙解决我的代码中的问题。我使用 QtDesigner 创建 UI,然后使用 pyuic4 转换为 test.py 文件。我还有 mplwidget.py 和 modules.py 文件,如下所示:

test.py

from PyQt4 import QtCore, QtGui
from numpy import *
import threading, thread
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(617, 588)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.tabWidget = QtGui.QTabWidget(self.centralwidget)
        self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
        self.tab = QtGui.QWidget()
        self.tab.setObjectName(_fromUtf8("tab"))
        self.verticalLayout_2 = QtGui.QVBoxLayout(self.tab)
        self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
        self.pushButton = QtGui.QPushButton(self.tab)
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.verticalLayout_2.addWidget(self.pushButton)
        self.pushButton_2 = QtGui.QPushButton(self.tab)
        self.pushButton_2.setObjectName(_fromUtf8("pushButton_2"))
        self.verticalLayout_2.addWidget(self.pushButton_2)
        self.tabWidget.addTab(self.tab, _fromUtf8(""))
        self.tab_2 = QtGui.QWidget()
        self.tab_2.setObjectName(_fromUtf8("tab_2"))
        self.verticalLayout_3 = QtGui.QVBoxLayout(self.tab_2)
        self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
        self.widget = MplWidget(self.tab_2)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
        self.widget.setSizePolicy(sizePolicy)
        self.widget.setObjectName(_fromUtf8("widget"))
        self.verticalLayout_3.addWidget(self.widget)
        self.tabWidget.addTab(self.tab_2, _fromUtf8(""))
        self.verticalLayout.addWidget(self.tabWidget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.pushButton.setText(_translate("MainWindow", "PushButton I", None))
        self.pushButton_2.setText(_translate("MainWindow", "PushButton II", None))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1", None))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Tab 2", None))


        QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("pressed()")), self.ploting1)
        QtCore.QObject.connect(self.pushButton_2, QtCore.SIGNAL(_fromUtf8("pressed()")), self.calling)


    def calling(self):
        from modules import MyForm
        arg="yes",
        tmain=threading.Thread(target=MyForm().ploting2,args=(arg))
        tmain.start()


    def ploting1(self,plot="yes"):
        if plot != "no":
            t = logspace(1E-6,1,132,base=1E-6)
            y1 = exp(-t/1E-3)
            self.widget.canvas.fig.clear()
            self.widget.canvas.ax = self.widget.canvas.fig.add_axes([0.15, 0.15, 0.7, 0.7])
            self.widget.canvas.ax.set_xlabel('t')
            self.widget.canvas.ax.set_ylabel('corr')
            label1='y1'
            self.widget.canvas.ax.semilogx(t,y1,'ro-',label=label1)
            self.widget.canvas.ax.legend(loc='lower left')
            self.widget.canvas.ax.tick_params(axis='y', colors='red')
            self.widget.canvas.draw()
            self.tabWidget.setCurrentIndex(1)

from mplwidget import MplWidget

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

modules.py

from numpy import *
from test import Ui_MainWindow
from mplwidget import MplWidget

class MyForm(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyForm, self).__init__(parent)
        self.setupUi(self)

    def ploting2(self,plot="yes"):
        if plot != "no":
            t = logspace(1E-6,1,132,base=1E-6)
            y1 = exp(-t/1E-3)
            self.widget.canvas.fig.clear()
            self.widget.canvas.ax = self.widget.canvas.fig.add_axes([0.15, 0.15, 0.7, 0.7])
            self.widget.canvas.ax.set_xlabel('t')
            self.widget.canvas.ax.set_ylabel('corr')
            label1='y1'
            self.widget.canvas.ax.semilogx(t,y1,'ro-',label=label1)
            self.widget.canvas.ax.legend(loc='lower left')
            self.widget.canvas.ax.tick_params(axis='y', colors='red')
            self.widget.canvas.draw()
            self.tabWidget.setCurrentIndex(1)

mplwidget.py

from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

class MplCanvas(FigureCanvas):
    """Class to represent the FigureCanvas widget"""

    def __init__(self, parent=None, name=None, width=5, height=4, dpi=100, bgcolor=None):
    self.parent = parent
    if self.parent:
        bgc = parent.backgroundBrush().color()
        bgcolor = float(bgc.red())/255.0, float(bgc.green())/255.0, float(bgc.blue())/255.0

        self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor=bgcolor, edgecolor=bgcolor)
        self.ax = self.fig.add_subplot(111)
        self.ax.hold(False)

        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self,
                                   QtGui.QSizePolicy.Expanding,
                                   QtGui.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)


class MplWidget(QtGui.QWidget):
    """Widget defined in Qt Designer"""
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)
        self.canvas = MplCanvas()
        self.vbl = QtGui.QVBoxLayout()
        self.vbl.addWidget(self.canvas)
        self.setLayout(self.vbl)

嗯,正如您在主窗口中看到的那样,我有两个按钮。第一个连接到 test.py 中定义的函数 (ploting1),并在 tab2 中绘制图形。问题出现在按钮 2 中,我使用线程调用 modules.py 中定义的函数 (ploting2)。我正在使用 Thread 因为我需要在绘制曲线的同时执行一些计算。这两个函数是相同的,但是从 modules.py 调用的函数没有显示绘图。关于我做错了什么的任何提示?

提前感谢您的帮助。

PS:我没有收到任何错误消息,一切正常,除了按钮 2 没有显示情节。

【问题讨论】:

    标签: python matplotlib pyqt


    【解决方案1】:

    这里有几个问题。前两个围绕这条线:

    tmain=threading.Thread(target=MyForm().ploting2,args=(arg))
    
    1. tmain 是一个局部变量。当calling 方法结束时, 线程将被垃圾收集。所以它不会运行
    2. 您在theading.Thread 调用的参数中实例化MyForm(),但不保存引用。到创建的对象。因此,您的对象中包含的 GUI 将被垃圾回收,并且将不复存在。

    因此,您可以通过将变量存储在父对象(例如self.tmain = ... 等)中来解决这些问题,但这毫无意义,正如您将在下一个问题中看到的那样

    1. 您不应该从线程访问 Qt 小部件。这样做是expressly forbidden 并且会在你的程序中随机导致可怕的可怕崩溃。

    我强烈建议您重新考虑使用线程。如果必须使用线程,请不要从线程调用任何 Qt 方法(这包括 matplotlib 绘图调用)。

    根据评论更新

    由于我正在执行一个很长的计算,我认为最好的方法 正在使用线程。但我也想绘制参数 计算过程中实时获取。你有什么想法吗 最好的方法?

    您仍然可以在线程中进行长时间运行的计算,以便您的 GUI 的其余部分保持响应。然后,您需要将计算结果传达给主线程,您将在主线程中调用实际的绘图函数。您可以通过多种方式将数据传递给主线程。通过发出 Qt 信号(需要使用 QThreads),显式地将 Qt 事件发布到主线程(使用 QCoreApplication.postEvent() 或将数据放入 Python 队列。

    一些相关链接(不一定给出绘图示例,但应该给出总体思路):

    【讨论】:

    • 谢谢三菠萝。由于我正在执行一个很长的计算,我认为最好的方法是使用线程。但我也想实时绘制计算过程中获得的参数。你知道最好的方法吗?
    猜你喜欢
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多