繁体   English   中英

PyQt5:一个回调起作用,另一个不起作用-为什么?

[英]PyQt5: one callback works, the other doesn't - why?

我的研究包括:

PyQt引用可调用问题?

Python PyQt回调永远不会运行-如何调试?

将额外的参数传递给PyQt插槽

我正在构建一个linux'launcher'程序,当前有两个回调。 一个简单地启动单击的应用程序,另一个创建新的启动器。 第一个很好用-第二个非常棘手。 我已经做了很多工作来解决这个问题。

  1. 在脚本上运行PyCharm调试并观察自身值等以了解更多信息
  2. 将NewLauncher函数移到InitUI方法中。
  3. 无休止地更改“自我”,“ centralWidget”和其他对象引用。
  4. 使用functools部分。

我收到的错误是“ AttributeError:'QWidget'对象没有属性'newLauncher'”

这是代码:(很抱歉,我很抱歉-最近建议我不要编辑得太多)。

import sys, os
import subprocess
from functools import partial

from PyQt5.QtWidgets import QFileDialog, QToolButton, QHBoxLayout, QGridLayout, QSizePolicy, QSpacerItem, QWidget, QPushButton, QFormLayout, QLineEdit, QAction, QApplication, QDesktopWidget, QMainWindow, QTabWidget, QVBoxLayout

from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize

from ruamel.yaml import YAML


yaml = YAML()
file_object = open("/home/tsc/PycharmProjects/launcher/Matrix.yaml", "r")
code = file_object.read()
matrix = yaml.load(code)
file_object.close()


class App(QMainWindow):
    def __init__(self):
        super(App, self).__init__()

        self.initUI()

    def launch(self, filepath):
        subprocess.run(filepath)


    def newLauncher(self):
        num_butts = len(matrix)
        btn_str = 'btn' + str(num_butts)

        file_object = open("/home/tsc/PycharmProjects/launcher/Matrix.yaml", "a")
        btn_str = 'btn' + str(num_butts + 1)
        file_object.write("\n" + btn_str + ":\n")

        self.setStyleSheet('padding: 3px; background: white');
        fname, _ = QFileDialog.getOpenFileName(self, "select an executable or document to launch:", "",
                                               "all files (*.*)")

        path = fname
        fname = os.path.basename(fname)

        file_object.write("  " + "name: " + str(fname) + "\n" + "  " + "path: " + str(path) + "\n")

        self.setStyleSheet('padding: 3px; background: white');
        icon, _ = QFileDialog.getOpenFileName(self, "select an image file for the icon:", "",
                                              "all files (*.*)")

        file_object.write("  " + "icon: " + str(icon) + "\n")
        file_object.close()


    def initUI(self):
        super(App, self).__init__()

        centralWidget = QWidget()
        tabWidget = QTabWidget()

        lay = QVBoxLayout(centralWidget)

        for i in range(3):
            page = QWidget()
            pagelay = QGridLayout(page)
            bmatrix = {}

            for btn in matrix:
                name = matrix[btn]['name']
                filepath = matrix[btn]['path']
                icon = matrix[btn]['icon']
                bmatrix[btn] = QToolButton(page)
                bmatrix[btn].setIcon(QIcon(icon))
                bmatrix[btn].setIconSize(QSize(64, 64))
                bmatrix[btn].resize(100, 100)
                bmatrix[btn].clicked.connect(lambda checked, arg=filepath: self.launch(arg))

                pagelay.addWidget(bmatrix[btn])

            tabWidget.addTab(page, 'tab{}'.format(i))

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        mainMenu.addMenu(fileMenu)
        newAction = QAction('&New', centralWidget)

        #1 newAction.triggered.connect(lambda checked, arg=matrix: centralWidget.newLauncher(arg)) - shows window.
        #2 newAction.triggered.connect(partial(self.NewLauncher, self)) - shows nothing, App has no NewLauncher

        fileMenu.addAction(newAction)
        editMenu = mainMenu.addMenu('Edit')

        lay.addWidget(mainMenu)
        lay.addWidget(tabWidget)

        centralWidget.setGeometry(100, 100, 1080, 630)
        centralWidget.setWindowTitle('LaunchMaster')
        qtRectangle = centralWidget.frameGeometry()
        centerPoint = QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        centralWidget.move(qtRectangle.topLeft())

        centralWidget.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

这是yaml配置文件。 如果要测试它,则需要自定义路径等。 该界面有一个menuBar和一个tabWidget,其中包含的页面本身包含启动器按钮。

Matrix.yaml:下划线替换空格(缩进为2个字符)。 我尚不确定这种标记语法,为麻烦而感到抱歉。

btn1:  
  name: firefox  
  path: firefox-esr  
  icon: /home/tsc/PycharmProjects/launcher/icons/firefox.jpeg  

btn2:  
  name: thunderbird  
  path: /home/tsc/thunderbird/thunderbird  
  icon: /home/tsc/PycharmProjects/launcher/icons/thunderbird.jpeg  

您要询问的行(我认为)是此注释掉的代码:

newAction.triggered.connect(partial(self.NewLauncher, self))

该评论说“什么也没显示,App没有NewLauncher”。

如果是这样,这里有两个问题。 第一个是简单的错字-您写的是NewLauncher而不是newLauncher我假设您在实际测试时已经解决了。 第二个更深一些,您可能会遇到问题。

self.newLauncher是一个绑定方法。 就是说,它知道它是针对哪个self的,当您调用它时,该self将作为第一个参数传递。 如果然后编写partial(self.newLauncher, self) ,则在调用该partial(self.newLauncher, self)时,它会与self.newLauncher(self)做同样的事情—也就是说,它将传入两个 self副本作为单独的参数。

拼写错误将非常明显地失败,在connect调用时会出现AttributeError 但是额外的self只会在按钮单击信号内出现TypeError失败。 我相信,这意味着PyQt会向stderr写一些警告(您可能没有看过-尤其是如果您使用的是Windows,甚至没有安装命令行窗口),并且单击时不执行任何操作。

您可能只想这样做:

newAction.triggered.connect(self.newLauncher)

偶尔,你想通过从类对象( 未结合的方法App.newLauncher ),以及partial是一个实例:

newAction.triggered.connect(partial(App.newLauncher, self))

…但是在大多数情况下,包括这一点,这只是做与传递绑定方法相同的事情的一种较不易读(且较慢)的方式。

  • 如果不需要传递某些参数,则不必使用lambda函数,因此可以进行常规连接。

  • 另一方面,您不应该调用centralWidget.show(),而是要显示,还必须使用setCentralWidget设置centralWidget。

  • 还有一点是,您必须验证用户是否选择了路径。

  • 代码的另一个改进是使用QProcess.startDetached()而不是subprocess.run()因为它被阻塞了。


import sys
import os

from PyQt5.QtWidgets import QFileDialog, QToolButton, QHBoxLayout, QGridLayout, QSizePolicy, QSpacerItem, QWidget, QPushButton, QFormLayout, QLineEdit, QAction, QApplication, QDesktopWidget, QMainWindow, QTabWidget, QVBoxLayout

from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize, QProcess

from ruamel.yaml import YAML

yaml_filename = "/home/tsc/PycharmProjects/launcher/Matrix.yaml" 


yaml = YAML()
file_object = open(yaml_filename, "r")
code = file_object.read()
matrix = yaml.load(code)
file_object.close()


class App(QMainWindow):
    def __init__(self):
        super(App, self).__init__()

        self.initUI()

    def launch(self, filepath):
        QProcess.startDetached(filepath)

    def newLauncher(self):
        fname, _ = QFileDialog.getOpenFileName(self, "select an executable or document to launch:", "",
                                               "all files (*.*)")
        if fname == "":
            return

        icon, _ = QFileDialog.getOpenFileName(self, "select an image file for the icon:", "",
                                              "all files (*.*)")
        if icon == "":
            return

        num_butts = len(matrix)
        btn_str = 'btn' + str(num_butts)
        file_object = open(yaml_filename, "a")
        btn_str = 'btn' + str(num_butts + 1)
        file_object.write("\n" + btn_str + ":\n")

        path = fname
        fname = os.path.basename(fname)
        file_object.write("  " + "name: " + str(fname) + "\n" + "  " + "path: " + str(path) + "\n")
        file_object.write("  " + "icon: " + str(icon) + "\n")
        file_object.close()


    def initUI(self):
        super(App, self).__init__()

        centralWidget = QWidget()
        tabWidget = QTabWidget()

        lay = QVBoxLayout(centralWidget)

        for i in range(3):
            page = QWidget()
            pagelay = QGridLayout(page)
            bmatrix = {}

            for btn in matrix:
                name = matrix[btn]['name']
                filepath = matrix[btn]['path']
                icon = matrix[btn]['icon']
                bmatrix[btn] = QToolButton(page)
                bmatrix[btn].setIcon(QIcon(icon))
                bmatrix[btn].setIconSize(QSize(64, 64))
                bmatrix[btn].resize(100, 100)
                bmatrix[btn].clicked.connect(lambda checked, arg=filepath: self.launch(arg))

                pagelay.addWidget(bmatrix[btn])

            tabWidget.addTab(page, 'tab{}'.format(i))

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        mainMenu.addMenu(fileMenu)
        newAction = QAction('&New', centralWidget)
        newAction.triggered.connect(self.newLauncher)

        fileMenu.addAction(newAction)
        editMenu = mainMenu.addMenu('Edit')

        lay.addWidget(mainMenu)
        lay.addWidget(tabWidget)

        self.setGeometry(100, 100, 1080, 630)
        self.setWindowTitle('LaunchMaster')
        qtRectangle = self.frameGeometry()
        centerPoint = QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        self.move(qtRectangle.topLeft())
        self.setCentralWidget(centralWidget)

        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM