我正在编写一个使用QtGUI的应用程序,现在正在尝试对其进行多线程处理(第一次学习)。一个较长的任务最终将包含在工作线程中,但输出日志需要在执行过程中写入。GUI类具有将这些日志输出到纯文本小部件的方法。到目前为止,一切都在GUI类中运行
我目前有以下代码(包括为简洁起见的重要部分):
class Worker(QRunnable):
@pyqtSlot()
def run(self):
Ui.logentry(self, "Test")
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi(resourcepath('./pycdra.ui'), self)
self.outputlog = self.findChild(QtWidgets.QPlainTextEdit, 'outputlog')
self.button = self.findChild(QtWidgets.QPushButton, 'button')
self.button.clicked.connect(self.Button)
self.threadpool = QThreadPool()
self.logentry("Available threads: %d" % self.threadpool.maxThreadCount())
def Button(self):
worker = Worker()
self.threadpool.start(worker)
def logentry(self, returntext):
self.outputlog.appendPlainText(self.timestamp() + " " + str(returntext))
self.outputlog.repaint()
def timestamp(self):
import datetime
ts = datetime.datetime.now(tz=None).strftime("%Y-%m-%d %H:%M:%S"))
return ts
def applic():
app = QApplication(sys.argv)
window = Ui()
window.show()
sys.exit(app.exec_())
applic()
当我试着运行这个程序时,GUI可以完美地加载,但是在按下button
时,Worker
的Ui.logentry
部分返回错误:AttributeError: 'Worker' object has no attribute 'outputlog'
我试图使logentry()
和timestamp()
成为全局的,这样它们就可以被这两个类访问,但最接近Ui.self.outputlog.appendPlainText(self.timestamp() + " " + str(returntext))
行的是'Ui' object has no attribute 'self'
我错过了什么?我让它们全球化是正确的,还是有其他方法可以做到这一点
您的代码存在各种问题。我将试着按照重要性的顺序来处理它们
logentry
是Ui
的一个实例方法,这意味着它需要一个Ui
实例才能运行;您试图实现的目标永远不会起作用,因为self
引用了Worker
的一个实例,它显然没有任何outputlog
属性(这是logentry
所期望的)。记住:self
参数不只是为了好玩,它总是指调用函数的对象(实例,例如实例方法)self.logentry
表示“使用self
作为第一个参数调用函数logentry
。因为在您的例子中,self
引用了Ui
的一个实例,这解释了您的主要问题,因为该实例没有该属性None
正是它的名字所说的:nothing。timestamp
函数将抛出另一个AttributeError,因为在None
中没有strftime
属性。这是datetime
对象的一部分,因此您必须获得一个now
对象,然后对其调用strftime
函数。在任何情况下,tz
参数都需要一个参数tzinfo
子类pyqtSlot
仅适用于从QObject(如QWidget)继承的Qt类。QRunnable不是这些类中的一个。您可以在每个类的文档标题中检查整个继承树:如果有“继承:“字段”,直到“没有”。如果您在某个点获得了QObjt继承,则可以使用该对象的槽和信号,否则不必考虑。QWIDGET也继承了QObjt; >所有EEM> QT窗口小部件继承自QWIDGET;Qt对象继承QQObjt,但不即使忽略以上所有内容,从外部线程(包括QRunnable对象)访问UI元素总是被禁止的,虽然理论上是可以的(但不可靠)获取它们的属性,尝试设置它们很可能会导致崩溃;为了更改UI元素中的某些内容,必须使用signals命令。请注意,“访问”还包括创建,并且总是导致崩溃
调用} 总是首选的,而且无论如何都必须从主UI线程调用它
repaint
是解决上述问题的常见(也是错误的)尝试;这是不必要的,因为正确地设置小部件属性(如使用appendPlainText()
)已经会导致单独的计划重新绘制;很少有情况下repaint
实际上是必要的,经验法则是,如果你调用它,你可能不知道你在做什么或者为什么要这么做。在任何情况下,调用^{很少需要在函数中使用导入,因为导入语句应该始终在脚本的最开始处;虽然有些情况下可以(或应该)在以后或在特定函数中进行导入,但在可能经常调用的函数中进行导入会使在那里使用它们完全没有意义是标准库的一部分,因此按需导入它几乎不会影响性能(特别是考虑到它的“性能权重”与类似Qt的大型库相比)
当从访问这些小部件了和
.ui
文件(或pyuic生成的文件)加载ui时,PyQt alread创建所有小部件作为实例属性,因此不需要findChild
。不幸的是,有很多教程建议使用这种方法,它们完全是错误的。您已经可以作为^{self.button
之后的uic.loadUi
函数名(如变量和属性)应始终以小写字母开头,因为只有类和常量应以大写字母开头(参见官方Style Guide for Python Code)。此外,对象名应始终解释这些对象的功能(参见“self documenting代码“):执行“操作”的函数应该有一个动词;如果它被命名为“按钮”,它不会告诉我它将进行一些处理,这不是一件很好的事情
“main”函数(如
applic
)通常在公共if __name__ == '__main__':
块中有意义,它确保在文件被导入而不是直接运行时不会调用该函数(请参见this answer和相关的question)由于QRunnable不是从QObject继承的,我们可以创建一个QObject子类作为信号“代理”,并使其成为QRunnable实例的成员。然后每次创建新的Worker对象时,我们都必须连接到该信号
以下是基于以上几点的代码修订版
我建议您仔细研究提供的代码及其与您的代码的差异,然后对上面给出的链接和以下主题进行仔细、耐心的研究:
self
的含义None
)相关问题 更多 >
编程相关推荐