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

时间:2018-11-12 11:11:26

标签: qt user-interface pyqt5

我遇到了理论上的问题。我正在使用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_()

这是第一次工作,但是在那之后,它似乎仅仅触发了对话的销毁(令人惊讶的是,对话并没有使它继续进行的任何事情崩溃)。
为什么会这样?有没有显示隐藏对话的标准方法?

2 个答案:

答案 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()

Snapshot of MCVE

答案 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()

欢呼,非常感谢您在决定显示/隐藏对话还是创建/销毁对话方面的有用建议!