在运行时替换 QWidget 对象 [英] Replace QWidget objects at runtime

查看:88
本文介绍了在运行时替换 QWidget 对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我必须用自定义的 QLineEdit 替换所有 QLineEdit 元素.为此,有不同的解决方案:

In my application I have to replace all QLineEdit elements with customized QLineEdit. To do that there are different solutions:

  1. 修改从 pyuic4 生成的 py 文件,并将所有 QLineEdit 对象替换为我的一个 LineEdit.这个解决方案并不是最好的,因为每次运行 pyuic4 时,我都会丢失对生成的输出文件所做的修改.
  2. 编写一个新类,在我的窗口或对话框中递归搜索 QLineEdit 小部件类型,并将它们替换为我的一个 LineEdit.这个解决方案要好得多.它允许我对其他对象也做同样的事情或扩展它,而无需关心对话框或窗口.

到目前为止,我可以编写一个模块 (WidgetReplacer),它递归地搜索 QGridLayout 项目并搜索是否有 QLineEdit 子项.如果是,那就用我的替换它.

So far, I could write a module (WidgetReplacer) which search recursively for QGridLayout items and search if there are QLineEdit children. If yes, then replace it with my one.

问题是,当我尝试访问 LineEdit 对象之一时,出现以下错误:

The problem is that when I try to access one of my LineEdit objects I get the following error:

RuntimeError: wrapped C/C++ object of type QLineEdit has been deleted

如果我查看我的输出,我注意到修改后的 QLineEdit 对象具有旧 ID:

And if I look at my output I notice that the modified QLineEdit object has sitll the old id:

old qt_obj <PyQt4.QtGui.QLineEdit object at 0x0543C930>
Replaced txt_line_1 with LineEdit
new qt_obj <LineEdit.LineEdit object at 0x0545B4B0>
----------------------------------
...
----------------------------------
<PyQt4.QtGui.QLineEdit object at 0x0543C930>

问题

为什么会这样?如何在运行时替换 QWidgets?

Question

Why happen this? How can I replace QWidgets at runtime?

这里有一些关于对象包装的额外细节:如果我转储 QEditLine 和 LineEdit 对象,则在模块 WidgetReplace 中:

Here some additional details regarding the ojects wrapping: Inside the module WidgetReplace if I dump the QEditLine and LineEdit objects I get:

<PyQt4.QtGui.QLineEdit object at 0x05378618>
    Reference count: 7
    Address of wrapped object: 051FAC40
    Created by: Python
    To be destroyed by: C/C++
    Parent wrapper: <PyQt4.QtGui.QGridLayout object at 0x05378588>
    Next sibling wrapper: NULL
    Previous sibling wrapper: <PyQt4.QtGui.QLabel object at 0x05378930>
    First child wrapper: NULL

<LineEdit.LineEdit object at 0x05378978>
    Reference count: 3
    Address of wrapped object: 051FAC40
    Created by: Python
    To be destroyed by: C/C++
    Parent wrapper: <__main__.Dialog object at 0x0535FBB8>
    Next sibling wrapper: <PyQt4.QtGui.QGridLayout object at 0x05378780>
    Previous sibling wrapper: NULL
    First child wrapper: NULL

相反,如果我从主要转储我的行编辑,我会得到关注,它代表已删除的 QLineEdit 对象:

Instead if I dump my line edit from the main, I get follow, which represent the deleted QLineEdit object:

<PyQt4.QtGui.QLineEdit object at 0x05378618>
    Reference count: 2
    Address of wrapped object: 00000000
    Created by: Python
    To be destroyed by: C/C++
    Parent wrapper: NULL
    Next sibling wrapper: NULL
    Previous sibling wrapper: NULL
    First child wrapper: NULL

<小时>

这是我的代码

对话框ui 和生成文件可以从 DropBox dialog.ui 和 dialog.py 下载


Here my code

Dialog The ui and generate file can be dowloaded from DropBox dialog.ui and dialog.py

主要

import sys
from PyQt4.QtGui import QDialog, QApplication
from dialog import Ui_Form
from WidgetReplacer import WidgetReplacer

class Dialog(QDialog, Ui_Form):

    def __init__(self, parent = None):
        super(Dialog, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)
        WidgetReplacer().replace_all_qlineedit(self)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Dialog()

    window.show()

    print window.txt_line_1
    window.txt_line_1.setText("Hello Text 1")

    sys.exit(app.exec_())

行编辑

from PyQt4.QtGui import QLineEdit, QValidator, QPalette
from PyQt4 import QtCore

class LineEdit(QLineEdit):

    def __init__(self, parent=None):
        super(LineEdit, self).__init__(parent)

        self.color_red = QPalette()
        self.color_red.setColor(QPalette.Text, QtCore.Qt.red)

        self.color_black = QPalette()
        self.color_black.setColor(QPalette.Text, QtCore.Qt.red)

        # Make connections
        self.textChanged.connect(self.verify_text)

    def verify_text(self, text):
        validator = self.validator()
        is_valid = QValidator.Acceptable

        if validator is not None:
            is_valid = validator.validate(text, 0)

        if is_valid == QValidator.Acceptable:
            self.setPalette(self.color_black)
        elif is_valid in [QValidator.Invalid, QValidator.Intermediate]:
            self.setPalette(self.color_red)

WidgetReplacer

import sip
from LineEdit import LineEdit
from PyQt4.QtCore import QRegExp
from PyQt4 import QtGui

class WidgetReplacer():

    def __init__(self):
        pass

    def replace_all_qlineedit(self, qt_dlg):
        children = self._get_all_gridlayout(qt_dlg)

        items = []
        for child in children:
            new_items = self._find_all_obj_in_layout(child, QtGui.QLineEdit)
            items.extend(new_items)

        for item in items:
            layout = item[0]
            row = item[1]
            column = item[2]
            qt_obj = item[3]
            self._replace_qlineedit_from_gridlayout(qt_dlg, qt_obj,
                                                    layout, row, column)

    def _get_all_gridlayout(self, qt_dlg):
        return qt_dlg.findChildren(QtGui.QGridLayout, QRegExp("gridLayout_[0-9]"))

    def _find_all_obj_in_layout(self, layout, qt_type):
        # Output list format:
        # layout, row, col, qt_obj,
        objects = []
        if type(layout) == QtGui.QGridLayout:
            layout_cols = layout.columnCount()
            layout_rows = layout.rowCount()
            for i in range(layout_rows):
                for j in range(layout_cols):
                    item = layout.itemAtPosition(i, j)
#                    print "item(",i, j, "):", item

                    if type(item) == QtGui.QWidgetItem:
                        if type(item.widget()) == qt_type:
                            new_obj = [layout, i, j, item.widget()]
                            objects.append(new_obj)

        elif type(layout) in [QtGui.QHBoxLayout, QtGui.QVBoxLayout]:
            raise SyntaxError("ERROR: Find of Qt objects in QHBoxLayout or QVBoxLayout still not supported")
#            for i in range(layout.count()):
#                item = layout.itemAt(i)

        return objects

    def _replace_qlineedit_from_gridlayout(self, parent, qt_obj, layout, row, column):
        print "old qt_obj", qt_obj
        obj_name = qt_obj.objectName()
        layout.removeWidget(qt_obj)
        sip.delete(qt_obj)
        qt_obj = LineEdit(parent)
        qt_obj.setObjectName(obj_name)

        layout.addWidget(qt_obj, row, column)
        print "Replaced", obj_name, "with LineEdit"

        print "new qt_obj", qt_obj
        print "----------------------------------"

推荐答案

不要在运行时替换小部件:在 Qt 设计器中提升小部件,以便在 GUI 模块处于生成.

Don't replace the widgets at runtime: promote the widgets in Qt Designer so that the line-edits are automatically replaced by your custom class when the GUI module is generated.

以下是提升小部件以使用自定义类的方法:

Here's how to promote a widget to use a custom class:

在 Qt Designer 中,选择所有要替换的行编辑,然后右键单击它们并选择Promote to...".在对话框中,将Promoted class name"设置为LineEdit",并将Header file"设置为包含此类的模块的python导入路径(例如myapp.LineEdit).然后单击添加"和提升",您将在对象检查器窗格中看到类从QLineEdit"更改为LineEdit".

In Qt Designer, select all the line-edits you want to replace, then right-click them and select "Promote to...". In the dialog, set "Promoted class name" to "LineEdit", and set "Header file" to the python import path for the module that contains this class (e.g. myapp.LineEdit). Then click "Add", and "Promote", and you will see the class change from "QLineEdit" to "LineEdit" in the Object Inspector pane.

现在,当您使用 pyuic 重新生成您的 ui 模块时,您应该会看到它使用您的自定义 LineEdit 类,并且文件底部将多出一行,如下所示:

Now when you re-generate your ui module with pyuic, you should see that it uses your custom LineEdit class and there will be an extra line at the bottom of the file like this:

    from myapp.LineEdit import LineEdit

这篇关于在运行时替换 QWidget 对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆