我遇到了理论上的问题。我正在使用pyqt5,但这可能是非常笼统且与框架无关的问题。
我有一个QMainwindow
坐在旁边,等待用户做事情。用户可以使用QDockwidgets
和关联的快捷方式(每个单独的对话框可检查QMenu
,选择显示/隐藏对话框(QAction
的子类) )。
我一直在努力有效地显示/隐藏对话。目前,我只是在启动时就将它们全部启动,将那些我不想在一开始出现的东西隐藏起来。这使触发对话变得容易,因为我只能根据对话的当前可见性dialogue.show()
/ dialogue.hide()
。
但是我不能相信这是最佳实践,而且非常有效。
我已经尝试过(我目前在此计算机上未设置pyqt环境,所以我不得不精简我的实际代码,而无法测试它是否可以运行):
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class InfoPanel(QDockWidget):
def __init__(self, title='Tool Box'):
QDockWidget.__init__(self, title)
self.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable)
self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
frame = QFrame()
layout = QGridLayout()
self.canvas = QGraphicsView()
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
layout.addWidget(self.canvas)
frame.setLayout(layout)
self.setWidget(frame)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.showpanelAct = QAction("&Show Panel", self, enabled=True,checkable=True, shortcut="F10")
self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
self.viewMenu = QMenu("&View", self)
self.viewMenu.addAction(self.showpanelAct)
self.setDockOptions(QMainWindow.AnimatedDocks)
def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
dialogueExists = False
if dialogueExists:
print('destroy')
self.infoPanel.destroy()
else:
print('create')
self.infoPanel = InfoPanel() #init
self.infoPanel.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
这是第一次工作,但是在那之后,它似乎仅仅触发了对话的销毁(令人惊讶的是,对话并没有使它继续进行的任何事情崩溃)。
为什么会这样?有没有显示隐藏对话的标准方法?
答案 0 :(得分:0)
我使用了公开的MCVE OP,并尝试使其在Windows 10的cygwin64中运行。
起初,我不得不应用一些小的修复程序。 (OP表示他在发布时无法对其进行测试。)
首先,为了方便起见,我在第一行插入了“小屋”:
bash
第二,#!/usr/bin/python3
没有出现。因此,我在
self.viewMenu
将 self.viewMenu = QMenu("&View", self)
self.viewMenu.addAction(self.showpanelAct)
添加到主菜单栏:
viewMenu
已将其修复。
第三,当我点击菜单项时得到:
self.menuBar().addMenu(self.viewMenu)
我必须承认我的Python知识非常有限。 (我是为同事用C ++编写Python绑定的人。所以,我的同事是真正的专家。最多,当我测试新实现的绑定是否符合预期时,我会在Python中扮演一些角色。)我修改了
Traceback (most recent call last):
File "./testQDockPanelShowHide.py", line 27, in <lambda>
self.showpanelAct.triggered.connect(lambda: self.showPanel(0))
File "./testQDockPanelShowHide.py", line 45, in showPanel
self.infoPanel = InfoPanel() #init
File "./testQDockPanelShowHide.py", line 17, in __init__
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
NameError: name 'QtGui' is not defined
Aborted (core dumped)
收件人:
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
解决了此问题。
此后,我得到了OP描述的行为,并仔细查看了我(和OP)怀疑该错误的位置:
self.canvas.setBackgroundBrush(QBrush(QColor(40, 40, 40)))
我坚信 def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
dialogueExists = False
if dialogueExists:
print('destroy')
self.infoPanel.destroy()
else:
print('create')
self.infoPanel = InfoPanel() #init
self.infoPanel.show()
并没有OP认为的那样。
它将尝试访问直到第一次调用此方法之前不存在的try: self.infoPanel
。 (请注意,成员变量 self.infoPanel
不存在。)因此,执行了self.infoPanel
分支并设置了except:
几行导致dialogueExists = False
。现在,成员变量self.infoPanel = InfoPanel() #init
存在,并且self.infoPanel
将永远不会再次失败,直到销毁该try: self.infoPanel
。
出于好奇,我看了QWidget.destroy()
(以确保不要说错什么):
QWidget.destroy(自身,bool destroyWindow = True,bool destroySubWindows = True)
释放窗口系统资源。如果destroyWindow为true,则销毁窗口小部件窗口。
destroy()对所有子窗口小部件进行递归调用,将destroySubWindows传递给destroyWindow参数。要更好地控制子小部件的销毁,请先选择性地销毁子小部件。
通常从QWidget析构函数调用此函数。
它绝对不会破坏成员变量MainWindow
。
了解了这一点之后,修复很容易而且很明显:
self.infoPanel
顺便说一句。我用 def showPanel(self,i:int = 0): # this is not so smart - should construct and deconstuct to save memory!?
if i == 0: #infopanel
try: self.infoPanel
#except NameError: #does not catch the error
except:
print('create')
self.infoPanel = InfoPanel() #init
if self.infoPanel.isVisible():
self.infoPanel.hide()
else:
self.infoPanel.show()
取代了destroy()
,这使得hide()
的重新创建变得过时了。
我通过多次切换菜单项进行了测试-现在它可以按预期运行(至少看起来像这样)。
最终完整的示例:
InfoPanel()
答案 1 :(得分:0)
在对解决方案进行编码后,我的问题得到了明显解决。回到我的原始代码(未产生按需创建/销毁对话框self.infoPanel
的预期输出)
dialogueExists = True
try: self.infoPanel
#except NameError: #does not catch the error
except:
dialogueExists = False
if dialogueExists:
print('destroy')
self.infoPanel.destroy()
else:
print('create')
self.infoPanel = InfoPanel() #init
self.infoPanel.show()
我的主要问题是我混淆了两件事。当我调用self.infoPanel
时,Qt破坏了对象self.infoPanel.destroy()
中包含的小部件。但这并不意味着对象self.infoPanel不存在(这正是我使用try: ...
的目的,以查看对象是否存在)。创建和销毁按需对话的简单明了方式显然涉及从环境中删除对象(del self.infoPanel
)。
工作代码为:
dialogueExists = True
try:
self.infoPanel.destroy() #not sure this is needed, but I guess it doesn't hurt
del self.infoPanel #this is the deletion of the actual object
except:
dialogueExists = False
if not dialogueExists :
self.infoPanel = InfoPanel()
欢呼,非常感谢您在决定显示/隐藏对话还是创建/销毁对话方面的有用建议!