Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Qt 显示/隐藏对话框的最佳方法_Qt_User Interface_Pyqt5 - Fatal编程技术网

Qt 显示/隐藏对话框的最佳方法

Qt 显示/隐藏对话框的最佳方法,qt,user-interface,pyqt5,Qt,User Interface,Pyqt5,我遇到了一个理论问题。我使用的是pyqt5,但这可能是一个非常笼统且与框架无关的问题。 我有一个QMainwindow坐在那里等待用户做一些事情。用户可以选择使用QMenu和相关快捷方式(每个单独的对话都是可检查的QAction)显示/隐藏对话(QDockwidgets的子类)。 我一直在努力有效地显示/隐藏对话。目前,我只是在启动时启动它们,隐藏那些我不想在开始时出现的。这使得触发对话变得很容易,因为我可以根据对话的当前可见性,直接执行dialogue.show()/dialogue.hide

我遇到了一个理论问题。我使用的是pyqt5,但这可能是一个非常笼统且与框架无关的问题。
我有一个
QMainwindow
坐在那里等待用户做一些事情。用户可以选择使用
QMenu
和相关快捷方式(每个单独的对话都是可检查的
QAction
)显示/隐藏对话(QDockwidgets的子类)。
我一直在努力有效地显示/隐藏对话。目前,我只是在启动时启动它们,隐藏那些我不想在开始时出现的。这使得触发对话变得很容易,因为我可以根据对话的当前可见性,直接执行
dialogue.show()
/
dialogue.hide()
。 但我不能相信这是最佳实践,而且非常有效

我已经尝试过(我目前没有在这台计算机上设置pyqt环境,因此我不得不删除实际代码,而无法测试它是否运行):

它第一次起作用,但在那之后,它似乎只会触发对话的破坏(令人惊讶的是,这不会破坏它一直在进行的任何事情)。
这是为什么?有没有一种标准的方法来显示隐藏的对话?

我使用了OP的曝光功能,并试图在我的Windows 10上运行它

起初,我不得不做一些小修改。(OP表示,他在出版时无法对其进行测试。)

首先,为了便于在
bash
中启动,我在第一行插入了一个“hut”:

#!/usr/bin/python3
其次,
self.viewMenu
没有出现。因此,我在后面插入了一行

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.showpanelAct)
要将查看菜单添加到主菜单栏,请执行以下操作:

        self.menuBar().addMenu(self.viewMenu)
这把它修好了

第三,单击我得到的菜单项时:

回溯(最近一次呼叫最后一次):
文件“/testQDockPanelShowHide.py”,第27行,在
self.showpanelAct.triggered.connect(lambda:self.showPanel(0))
showPanel中第45行的文件“/testQDockPanelShowHide.py”
self.infoPanel=infoPanel()#init
文件“/testQDockPanelShowHide.py”,第17行,在__
self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40,40,40)))
NameError:未定义名称“QtGui”
中止(堆芯转储)
我必须承认,我的Python知识非常有限。(我是在C++中为同事编写Python绑定的家伙。所以,我的同事是真正的专家。我在Python中玩一点点,当我测试新的实现绑定是否做了预期的事情。)但是,我修改了< /P>
    self.canvas.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(40, 40, 40)))
致:

解决了这个问题

在此之后,我得到了OP描述的行为,并仔细查看了我(和OP)怀疑错误的地方:

    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()
我坚信,
try:self.infoPanel
不会像OP认为的那样做

它尝试访问
self.infoPanel
,该方法在首次调用此方法之前不存在。(请注意,成员变量
self.infoPanel
不存在。)因此,执行
分支,设置
dialogueExists=False
,几行之后会导致
self.infoPanel=infoPanel()#init
。现在,成员变量
self.infoPanel
已存在,
try:self.infoPanel
在销毁此
主窗口之前不会再次失败

出于好奇,我看了一眼(以确保不说错话):

QWidget.destroy(self,bool destroy窗口=True,bool destroy子窗口=True) 释放窗口系统资源。如果destroyWindow为true,则销毁小部件窗口

destroy()为所有子窗口小部件递归调用自身,为destroy窗口参数传递destroystubwindows。要更好地控制子部件的销毁,请先有选择地销毁子部件

此函数通常从QWidget析构函数调用

它肯定不会破坏成员变量
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()
过时了

我通过多次切换菜单项来测试这一点——它现在可以像预期的那样工作(至少看起来是这样)

最后,完整样本:

#!/usr/bin/python3

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)))
        self.canvas.setBackgroundBrush(QBrush(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.menuBar().addMenu(self.viewMenu)
        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
            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()

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

在暂停编写代码后,我的问题的解决方案非常明显。回到我的原始代码(它没有产生创建/销毁对话的预期输出
self.infoPanel
on demand):

我的主要问题是我混淆了两件不同的事情。当我调用
self.infoPanel.destroy()
时,Qt销毁了对象
self.infoPanel
中包含的小部件。但这并不意味着对象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()

干杯,非常感谢关于决定是否显示/隐藏对话或创建/销毁对话的有用建议

依我看,通常在程序开始时设置任何GUI内容(如对话框),根据用户交互的需要显示/隐藏,并在程序结束时销毁。我无法想象GUI对象会消耗那么多内存,以至于按需创建/销毁它们是值得的。(视图数据是例外,例如大表的模型数据。当GUI的各个部分由于关闭对话框而隐藏时,我通常会丢弃这些数据。)这就是我在Qt中的操作方式。我在gtkmm和OSF/Motif中都做过这件事。(我不记得我是如何开始使用Win3.1的GDI的,可能是错的。);-)对于标准方式,您可以咨询
#!/usr/bin/python3

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)))
        self.canvas.setBackgroundBrush(QBrush(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.menuBar().addMenu(self.viewMenu)
        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
            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()

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()
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()
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()