设置 QDialog 框并保留对 QDialog 中元素的访问的最佳方法?

Posted

技术标签:

【中文标题】设置 QDialog 框并保留对 QDialog 中元素的访问的最佳方法?【英文标题】:Best approach to setting up QDialog boxes and retain access to elements in the QDialog? 【发布时间】:2017-12-11 13:41:56 【问题描述】:

我目前正在运行 QDialog 的这个实现,它可以满足我的需要,但似乎不是最佳实践方法,并且存在我似乎无法解决的缺陷。我知道 QDialog 可以实现为类或方法,但我需要能够访问 QDialog 的元素,例如 QLineEditQComboBox,除了我在下面列出的代码之外,我不确定如何执行此操作. QDialog 中与 QComboBox 关联的所有值都会立即应用,因此在 QDialog 关闭后无需保留其副本。

Test.py

from PyQt5 import uic
from PyQt5.Qt import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

Ui_MainWindow = uic.loadUiType('TestMainWindow.ui')[0]
Ui_ExampleDialog = uic.loadUiType('TestExampleDialog.ui')[0]


class ExampleDialog(QDialog, Ui_ExampleDialog):

    def __init__(self, parent=None, flags=Qt.Dialog):
        QDialog.__init__(self, parent, flags)
        self.setupUi(self)


class MainWindow(QMainWindow, Ui_MainWindow):

    def __init__(self, parent=None, flags=Qt.Window):
        QMainWindow.__init__(self, parent, flags)
        self.setupUi(self)
        self.setWindowTitle('MCVE')

        # THIS DOES NOT SEEM APPROPRIATE, BUT I NEED TO ACCESS
        # ELEMENTS OF THE UI FILE OUTSIDE OF 'showExampleDialog'
        self.exampleDialog = ExampleDialog()

        self.itemInfoBtn.clicked.connect(self.showExampleDialog)
        self.setupItemInfo()

    def showExampleDialog(self):
        # ACCORDING TO PYCHARM 'self' IS 'QMainWindow',
        # DO I ASSUME THAT 'parent = QMainWindow'?
        self.exampleDialog.setWindowFlags(Qt.WindowCloseButtonHint)
        self.exampleDialog.closeButton.clicked.connect(self.exampleDialog.accept)
        self.exampleDialog.exec_()  # CREATES A TASKBAR ENTRY ... BUT WHY?

    def setupItemInfo(self):
        # THIS IS AN EXAMPLE OF HOW I'M CURRENTLY ACCESSING
        # ELEMENTS OF THE `showExampleDialog' DIALOG
        val = 'Entry #3', 'Entry #2', 'Entry #1'
        self.exampleDialog.setWindowTitle('Example Dialog')
        self.exampleDialog.exampleCombo.insertItems(0, val)
        self.exampleDialog.exampleCombo.setCurrentIndex(1)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

TestMainWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>282</width>
    <height>173</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
     <widget class="QPushButton" name="itemInfoBtn">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
      <property name="text">
       <string>ItemInfoBtn</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

TestExampleDialog.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="windowModality">
   <enum>Qt::ApplicationModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>174</width>
    <height>98</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <property name="modal">
   <bool>true</bool>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QLabel" name="Message">
     <property name="text">
      <string>I'm a Dialog ...</string>
     </property>
     <property name="alignment">
  <set>Qt::AlignCenter</set>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QComboBox" name="exampleCombo"/>
   </item>
   <item>
    <widget class="QPushButton" name="closeButton">
     <property name="text">
      <string>Close</string>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

我遇到的一个问题是QDialogQMainWindow 旁边创建了第二个任务栏条目。这对我来说没有意义。据我了解,QDialog 可以是模态的或无模态的,我相信我当前的实现具有模态集。考虑到这一点,PyCharm 说 self 是 QMainWindow 所以我假设它继承了应该防止第二个任务栏条目填充的父级。也许我错了。

我遇到的另一个问题是访问QDialog 的元素和当它作为一个类实现时的变量,我不完全确定如何做到这一点,而且我的研究并没有真正解释清楚。理解方式。

解决此问题的最佳方法是什么?如何防止 QDialog 创建第二个任务栏条目?

【问题讨论】:

看起来好像您错误地使用了返回的小部件。您应该在给定的小部件上运行“setupUI”,在您的情况下是 QDialog 的实例。见pyqt.sourceforge.net/Docs/PyQt5/designer.html#the-uic-module @deets 在阅读完文档后,这是否意味着我需要使用 pyuic5 编译 UI 文件,将它们导入为 from ui_file import ui_class?这种方法似乎不太经济,因为我的应用程序仍在开发中,并且 UI 文件的更改正在迅速发生,因此直接加载 UI 文件会删除很多步骤。此外,我将QDialog 的 UI 文件分配给了self.exampleDialog,因此我可以访问 UI 文件中的对象,尽管这对我来说似乎并不正确,而且我不知道如何解决这个问题。 不,不是这个意思。文档有点不清楚,但是如果您实例化自己,或者使用 loadUIC 来获取实例,这应该没关系。但这两种情况下都需要 setupUi。此外,即使您必须使用 pyuic,这也只是 ui 修改后的构建步骤。关于你的第二个问题:为什么感觉不对?如果你想访问东西,你必须让它们可以访问。我看不出有什么办法。 请不要getter和setter! Python 不是 Java!您对相关小部件的访问是基于它们的名称,这正是这样做的正确方法,因为它将周围代码的交互点与小部件的内部结构分离。 Python 允许比其他语言更强大的属性概念在这里工作,因此您可以获得“dialog.widgetName”-模式。顺便说一句,我会遵循小部件名称的 python 命名约定,因为现在它们看起来像类名。当然还有更多语义命名 - ComboBox 是一种类型,而不是具体的列表,例如国家。 @AaronTomason。 self.exampleDialog.setWindowFlags(self.exampleDialog.windowFlags() | Qt.WindowCloseButtonHint)。 (而且你需要导入QDialog)。 【参考方案1】:

这完全解决了这个问题。将 self 分配给 self.exampleDialog = ExampleDialog(self) 并更新 self.exampleDialog.setWindowFlags 以包含 self.exampleDialog.windowFlags() 消除了第二个任务栏条目。

可以使用self.exampleDialog.&lt;element&gt; 访问对话框的元素,例如self.exampleDialog.setWindowTitle('Example Dialog')

from PyQt5 import uic
from PyQt5.Qt import Qt
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow
import sys

Ui_MainWindow = uic.loadUiType('TestMainWindow.ui')[0]
Ui_ExampleDialog = uic.loadUiType('TestExampleDialog.ui')[0]


class ExampleDialog(QDialog, Ui_ExampleDialog):
    def __init__(self, parent = None, flags = Qt.Dialog):
        QDialog.__init__(self, parent, flags)
        self.setupUi(self)


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent = None, flags = Qt.Window):
        QMainWindow.__init__(self, parent, flags)
        self.setupUi(self)
        self.setWindowTitle('MCVE')

        # Assign 'self' to 'ExampleDialog' so we can
        # access elements of the dialog.
        self.exampleDialog = ExampleDialog(self)

        self.itemInfoBtn.clicked.connect(self.showExampleDialog)
        self.setupItemInfo()

    def showExampleDialog(self):
        self.exampleDialog.setWindowFlags(self.exampleDialog.windowFlags() | Qt.WindowCloseButtonHint)
        self.exampleDialog.closeButton.clicked.connect(self.exampleDialog.accept)
        self.exampleDialog.exec_()  # CREATES A TASKBAR ENTRY ... BUT WHY?

    def setupItemInfo(self):
        val = 'Entry #3', 'Entry #2', 'Entry #1'
        self.exampleDialog.setWindowTitle('Example Dialog')
        self.exampleDialog.exampleCombo.insertItems(0, val)
        self.exampleDialog.exampleCombo.setCurrentIndex(1)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

【讨论】:

以上是关于设置 QDialog 框并保留对 QDialog 中元素的访问的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何控制QDialog的位置?

PyQt5 组件之QDialog

如何更改 QDialog 中的默认按钮行为

如何从QDialog刷新/更新/重绘QMainWindow?

Qt - 对主要的 QDialog 选择感到困惑

调整 QDialog 的大小作为 MainWindow 调整大小