发射信号会导致核心转储
Posted
技术标签:
【中文标题】发射信号会导致核心转储【英文标题】:Emitting signals causes core dumps 【发布时间】:2016-07-13 12:29:44 【问题描述】:作为 Python 和 Qt 的新手(相当)我正在摆弄这段代码。 这可能有点乱,但我已经尝试尽可能地减少代码,并且仍然得到核心转储。
基本上有一个按钮来启动“某事” - 现在它只是一个 for 循环 - 一个进度条和一个标签。
点击界面中的按钮输出 1 到控制台,然后核心转储。在进度条或标签中都看不到任何数据。
我正在使用 Python 2.7.11、Qt-4.8.7 和 PySide 1.2.2。
线程代码来自这个 youtube 视频: https://www.youtube.com/watch?v=ivcxZSHL7jM
我尝试将发射线放在循环之外,甚至放在 MainDialog 类中,似乎只要发射信号来自 MainDialog 类之外,它就会崩溃。它只能在 MainDialog 内部工作(使用静态整数进行测试,在进度条重置后进行测试)。
showGui.py - 这里没有错 - (使用设计师制作并使用 pyside 转换):
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'show.ui'
#
# Created: Wed Jul 13 09:10:12 2016
# by: pyside-uic 0.2.15 running on PySide 1.2.2
#
# WARNING! All changes made in this file will be lost!
from PySide import QtCore, QtGui
class Ui_mainDialog(object):
def setupUi(self, mainDialog):
mainDialog.setObjectName("mainDialog")
mainDialog.resize(369, 171)
self.pushButton = QtGui.QPushButton(mainDialog)
self.pushButton.setGeometry(QtCore.QRect(50, 40, 84, 33))
self.pushButton.setObjectName("pushButton")
self.progressBar = QtGui.QProgressBar(mainDialog)
self.progressBar.setGeometry(QtCore.QRect(50, 110, 231, 23))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.label = QtGui.QLabel(mainDialog)
self.label.setGeometry(QtCore.QRect(170, 40, 81, 31))
self.label.setObjectName("label")
self.retranslateUi(mainDialog)
QtCore.QMetaObject.connectSlotsByName(mainDialog)
def retranslateUi(self, mainDialog):
mainDialog.setWindowTitle(QtGui.QApplication.translate("mainDialog", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("mainDialog", "Button", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("mainDialog", "TextLabel", None, QtGui.QApplication.UnicodeUTF8))
test.py - 发出信号时失败:
from __future__ import print_function
import sys
import time
from PySide import QtCore, QtGui
import showGui
from PySide.QtCore import *
from PySide.QtGui import *
class MainDialog(QDialog, showGui.Ui_mainDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.connect(self.threadclass, QtCore.SIGNAL('GFX_PROGRESS'), self.setProgress)
self.connect(self.threadclass, QtCore.SIGNAL('TEXT_PROGRESS'), self.setTextLabel)
self.connect(self.pushButton, SIGNAL("clicked()"), self.threadclass.doSomething)
self.progressBar.reset()
def setTextLabel(self, val):
self.label.setText(val)
def setProgress(self, val):
self.progressBar.setValue(val)
class ThreadClass(QtCore.QThread):
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
def doSomething(self):
self.runcmd()
# some more code here
def runcmd(self):
for i in range(1, 100):
print("Status at : %s " % i)
# this one crashes
self.emit(QtCore.SIGNAL('TEXT_PROGRESS'), i)
# this one crashes too
self.emit(QtCore.SIGNAL('GFX_PROGRESS'), i)
time.sleep(1)
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
【问题讨论】:
您应该尝试使用new style signals。它们不太容易出错,如果你做错了什么,它们通常会触发异常而不是使程序崩溃。所以你应该在你的类中定义一个QtCore.Signal
,然后使用self.the_signal.emit()
并使用self.threadclass.the_signal.connect(self.the_slot)
连接它。
【参考方案1】:
不要使用old-style signal and slot syntax。它很容易出错,如果你弄错了也不会引发异常。除此之外,看起来 PySide 中的实现有些破损。我将您的代码示例转换为 PyQt4,它不会转储内核。
要让您的示例在 PySide 中运行,您首先需要切换到新样式的信号和槽语法。此外,您当前的线程实现是错误的。它实际上并没有启动工作线程,所以所有代码都会在主线程中运行,它会阻塞gui。
以下修复应该使示例按预期工作:
class MainDialog(QDialog, Ui_mainDialog):
def __init__(self, parent=None):
..
# use new-style connections
self.threadclass.gfxProgress.connect(self.setProgress)
self.threadclass.textProgress.connect(self.setTextLabel)
self.pushButton.clicked.connect(self.threadclass.doSomething)
class ThreadClass(QtCore.QThread):
# define new-style signals
gfxProgress = QtCore.Signal(int)
textProgress = QtCore.Signal(str)
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
def doSomething(self):
# start the thread
# by default, this will automatically call run()
self.start()
def run(self):
for i in range(1, 100):
print("Status at : %s " % i)
# emit the new-style signals
self.gfxProgress.emit(i)
self.textProgress.emit(str(i))
time.sleep(1)
【讨论】:
非常感谢(我以为我确实使用了pyside的新信号)。我在您的示例中遇到的一个小问题是未定义 Signal。它是 pyqtSignal [此处] (pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html) 引用的,您可以编辑您的示例以反映这一点吗? @CarstenJensen。您的问题都与 PySide 有关,所以我认为现在更改它是不对的。无论如何,这只是语法上的细微差别 - 代码在其他方面完全相同。 我拿了你的例子,用我的实现了它,将我的转换为 PyQT,这就是我发现 Signal 找不到问题的地方,但 pyqtSignal 适用于 PyQT。而且由于您的示例是 PyQT,供将来参考,像我这样的新手在使用您的示例时不会遇到问题...我希望您知道我在哪里:-) @CarstenJensen。你误会了。我的答案中的代码是针对 PySide,而不是 PyQt。如果您将这些更改添加到原始 PySide 示例中,它将起作用。无需切换到 PyQt。在我回答的第一部分中,我表明使用旧式信号语法似乎是问题的原因。我通过将您的原始示例转换为 PyQt4 来测试这一点(但只进行了最小的更改)。 PyQt4 版本没有转储内核,这表明 PySide 中可能存在错误。但无论如何,您可以通过使用新型信号轻松避免该错误(如我在示例代码中所示)。 啊,我明白了。 “我将您的代码示例转换为 PyQt4,它不会转储内核。”我认为这句话适用于您提供的代码示例。很抱歉造成混乱。再次感谢您的帮助:-)以上是关于发射信号会导致核心转储的主要内容,如果未能解决你的问题,请参考以下文章
如何获取已向已退出转储核心的另一个进程发送 SIGABRT 信号的进程的 pid