[英]PyQt5: one callback works, the other doesn't - why?
我的研究包括:
我正在构建一个linux'launcher'程序,当前有两个回调。 一个简单地启动单击的应用程序,另一个创建新的启动器。 第一个很好用-第二个非常棘手。 我已经做了很多工作来解决这个问题。
我收到的错误是“ 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.