本文介绍了PyQt5:对象没有属性“连接"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在关注有关在PyQt中进行线程化的教程(来自此处).正如它是用PyQt4(和Python2)编写的那样,我修改了代码以使其与PyQt5和Python3一起使用.

I am currently following this tutorial on threading in PyQt (code from here). As it was written in PyQt4 (and Python2), I adapted the code to work with PyQt5 and Python3.

这是gui文件(newdesign.py):

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'threading_design.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(526, 373)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.subreddits_input_layout = QtWidgets.QHBoxLayout()
        self.subreddits_input_layout.setObjectName("subreddits_input_layout")
        self.label_subreddits = QtWidgets.QLabel(self.centralwidget)
        self.label_subreddits.setObjectName("label_subreddits")
        self.subreddits_input_layout.addWidget(self.label_subreddits)
        self.edit_subreddits = QtWidgets.QLineEdit(self.centralwidget)
        self.edit_subreddits.setObjectName("edit_subreddits")
        self.subreddits_input_layout.addWidget(self.edit_subreddits)
        self.verticalLayout.addLayout(self.subreddits_input_layout)
        self.label_submissions_list = QtWidgets.QLabel(self.centralwidget)
        self.label_submissions_list.setObjectName("label_submissions_list")
        self.verticalLayout.addWidget(self.label_submissions_list)
        self.list_submissions = QtWidgets.QListWidget(self.centralwidget)
        self.list_submissions.setBatchSize(1)
        self.list_submissions.setObjectName("list_submissions")
        self.verticalLayout.addWidget(self.list_submissions)
        self.progress_bar = QtWidgets.QProgressBar(self.centralwidget)
        self.progress_bar.setProperty("value", 0)
        self.progress_bar.setObjectName("progress_bar")
        self.verticalLayout.addWidget(self.progress_bar)
        self.buttons_layout = QtWidgets.QHBoxLayout()
        self.buttons_layout.setObjectName("buttons_layout")
        self.btn_stop = QtWidgets.QPushButton(self.centralwidget)
        self.btn_stop.setEnabled(False)
        self.btn_stop.setObjectName("btn_stop")
        self.buttons_layout.addWidget(self.btn_stop)
        self.btn_start = QtWidgets.QPushButton(self.centralwidget)
        self.btn_start.setObjectName("btn_start")
        self.buttons_layout.addWidget(self.btn_start)
        self.verticalLayout.addLayout(self.buttons_layout)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Threading Tutorial - nikolak.com "))
        self.label_subreddits.setText(_translate("MainWindow", "Subreddits:"))
        self.edit_subreddits.setPlaceholderText(_translate("MainWindow", "python,programming,linux,etc (comma separated)"))
        self.label_submissions_list.setText(_translate("MainWindow", "Submissions:"))
        self.btn_stop.setText(_translate("MainWindow", "Stop"))
        self.btn_start.setText(_translate("MainWindow", "Start"))

和主脚本(main.py):

from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal, QObject
import sys
import newdesign
import urllib.request
import json
import time


class getPostsThread(QThread):
    def __init__(self, subreddits):
        """
        Make a new thread instance with the specified
        subreddits as the first argument. The subreddits argument
        will be stored in an instance variable called subreddits
        which then can be accessed by all other class instance functions

        :param subreddits: A list of subreddit names
        :type subreddits: list
        """
        QThread.__init__(self)
        self.subreddits = subreddits

    def __del__(self):
        self.wait()

    def _get_top_post(self, subreddit):
        """
        Return a pre-formatted string with top post title, author,
        and subreddit name from the subreddit passed as the only required
        argument.

        :param subreddit: A valid subreddit name
        :type subreddit: str
        :return: A string with top post title, author,
                    and subreddit name from that subreddit.
        :rtype: str
        """
        url = "https://www.reddit.com/r/{}.json?limit=1".format(subreddit)
        headers = {'User-Agent': 'nikolak@outlook.com tutorial code'}
        request = urllib.request.Request(url, header=headers)
        response = urllib.request.urlopen(request)
        data = json.load(response)
        top_post = data['data']['children'][0]['data']
        return "'{title}' by {author} in {subreddit}".format(**top_post)

    def run(self):
        """
        Go over every item in the self.subreddits list
        (which was supplied during __init__)
        and for every item assume it's a string with valid subreddit
        name and fetch the top post using the _get_top_post method
        from reddit. Store the result in a local variable named
        top_post and then emit a pyqtSignal add_post(QString) where
        QString is equal to the top_post variable that was set by the
        _get_top_post function.

        """
        for subreddit in self.subreddits:
            top_post = self._get_top_post(subreddit)
            self.emit(pyqtSignal('add_post(QString)'), top_post)
            self.sleep(2)


class ThreadingTutorial(QtWidgets.QMainWindow, newdesign.Ui_MainWindow):
    """
    How the basic structure of PyQt GUI code looks and behaves like is
    explained in this tutorial
    http://nikolak.com/pyqt-qt-designer-getting-started/
    """

    def __init__(self):
        super(self.__class__, self).__init__()
        self.setupUi(self)
        self.btn_start.clicked.connect(self.start_getting_top_posts)

    def start_getting_top_posts(self):
        # Get the subreddits user entered into an QLineEdit field
        # this will be equal to '' if there is no text entered
        subreddit_list = str(self.edit_subreddits.text()).split(',')
        if subreddit_list == ['']:  # since ''.split(',') == [''] we use that to check
                                    # whether there is anything there to fetch from
                                    # and if not show a message and abort
            QtWidgets.QMessageBox.critical(self, "No subreddits",
                                       "You didn't enter any subreddits.",
                                       QtWidgets.QMessageBox.Ok)
            return
        # Set the maximum value of progress bar, can be any int and it will
        # be automatically converted to x/100% values
        # e.g. max_value = 3, current_value = 1, the progress bar will show 33%
        self.progress_bar.setMaximum(len(subreddit_list))
        # Setting the value on every run to 0
        self.progress_bar.setValue(0)

        # We have a list of subreddits which we use to create a new getPostsThread
        # instance and we pass that list to the thread
        self.get_thread = getPostsThread(subreddit_list)

        # Next we need to connect the events from that thread to functions we want
        # to be run when those pyqtSignals get fired

        # Adding post will be handeled in the add_post method and the pyqtSignal that
        # the thread will emit is  pyqtSignal("add_post(QString)")
        # the rest is same as we can use to connect any pyqtSignal
        self.connect(self.get_thread, pyqtSignal("add_post(QString)"), self.add_post)

        # This is pretty self explanatory
        # regardless of whether the thread finishes or the user terminates it
        # we want to show the notification to the user that adding is done
        # and regardless of whether it was terminated or finished by itself
        # the finished pyqtSignal will go off. So we don't need to catch the
        # terminated one specifically, but we could if we wanted.
        self.connect(self.get_thread, pyqtSignal("finished()"), self.done)

        # We have all the events we need connected we can start the thread
        self.get_thread.start()
        # At this point we want to allow user to stop/terminate the thread
        # so we enable that button
        self.btn_stop.setEnabled(True)
        # And we connect the click of that button to the built in
        # terminate method that all QThread instances have
        self.btn_stop.clicked.connect(self.get_thread.terminate)
        # We don't want to enable user to start another thread while this one is
        # running so we disable the start button.
        self.btn_start.setEnabled(False)

    def add_post(self, post_text):
        """
        Add the text that's given to this function to the
        list_submissions QListWidget we have in our GUI and
        increase the current value of progress bar by 1

        :param post_text: text of the item to add to the list
        :type post_text: str
        """
        self.list_submissions.addItem(post_text)
        self.progress_bar.setValue(self.progress_bar.value()+1)

    def done(self):
        """
        Show the message that fetching posts is done.
        Disable Stop button, enable the Start one and reset progress bar to 0
        """
        self.btn_stop.setEnabled(False)
        self.btn_start.setEnabled(True)
        self.progress_bar.setValue(0)
        QtWidgets.QMessageBox.information(self, "Done!", "Done fetching posts!")


def main():
    app = QtWidgets.QApplication(sys.argv)
    form = ThreadingTutorial()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

现在我遇到以下错误:

AttributeError: 'ThreadingTutorial' object has no attribute 'connect'

有人可以告诉我如何解决此问题吗?一如既往,我们将不胜感激.

Can anyone please tell me how to fix this? Any help would be, as always, very much appreciated.

推荐答案

在PyQt4中使用QObject.connect()和类似名称被称为旧样式信号",而不再在PyQt5 中得到支持,它仅支持"新型信号",建议将其连接在PyQt4中.

Using QObject.connect() and similar in PyQt4 is known as "Old style signals", and is not supported in PyQt5 anymore, it supports only "New style signals", which already in PyQt4 was the recommended way to connect signals.

在PyQt5中,您需要直接使用绑定信号的connect()emit()方法,例如代替:

In PyQt5 you need to use the connect() and emit() methods of the bound signal directly, e.g. instead of:

self.emit(pyqtSignal('add_post(QString)'), top_post)
...
self.connect(self.get_thread, pyqtSignal("add_post(QString)"), self.add_post)
self.connect(self.get_thread, pyqtSignal("finished()"), self.done)

使用:

self.add_post.emit(top_post)
...
self.get_thread.add_post.connect(self.add_post)
self.get_thread.finished.connect(self.done)

但是,要使此方法起作用,您首先需要在getPostsThread上显式定义add_post信号,否则会出现属性错误.

However for this to work you need to explicitly define the add_post signal on your getPostsThread first, otherwise you'll get an attribute error.

class getPostsThread(QThread):
    add_post = pyqtSignal(str)
    ...

在带有旧式信号的PyQt4中,使用信号时自动定义,现在需要显式完成.

In PyQt4 with old style signals when a signal was used it was automatically defined, this now needs to be done explicitly.

这篇关于PyQt5:对象没有属性“连接"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-05 07:34