【问题标题】:PySide: How to overload classes with static functionsPySide:如何使用静态函数重载类
【发布时间】:2014-09-15 19:04:54
【问题描述】:

我想重载QInputDialog,因为我想用我自己的QCombobox 衍生物替换它的QCombobox。我检查了QInputDialog 的源代码并试图重载它的ensureCombobox()。但是,当我尝试类似:

class AutoCompleteInputDialog(QtGui.QInputDialog):

    def __init__(self, *args, **kwargs):
        self.ensureComboBox()
        super(AutoCompleteInputDialog, self).__init__(*args, **kwargs)


    def ensureComboBox(self):
        print "ensureComboBox"
        self.comboBox = AutoCompleteComboBox(self)
        self.comboBox.hide()

        self.comboBox.editTextChanged.connect(self.textChanged)
        self.comboBox.currentIndexChanged.connect(self.textChanged)

AutoCompleteInputDialog.getItems(None, "test title", "test label", ["albatross 12", "tiger 12", "item 2", "item 3"])

ensureCombobox 永远不会被调用。

我还尝试定义一个创建QInputDialog 并设置其组合框的静态方法。但它也不起作用。

    @staticmethod
    def getItem(*args, **kwargs):
        dialog = QtGui.QInputDialog()
        dialog.comboBox = AutoCompleteComboBox(dialog)
        return dialog.getItem(*args, **kwargs)

为了完整起见,AutoCompleteCombobox的代码

class AutoCompleteComboBox(QtGui.QComboBox):
    def __init__(self, *args, **kwargs):
        super(AutoCompleteComboBox, self).__init__(*args, **kwargs)

        self.setEditable(True)
        self.setInsertPolicy(self.NoInsert)

        # self.comp = QtGui.QCompleter([""], self)
        self.comp = CustomQCompleter([""], self)
        self.comp.setCompletionMode(QtGui.QCompleter.PopupCompletion)
        self.comp.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.setCompleter(self.comp)#
        self.setModel(["hallo babe", "world", "we", "are babe"])

    def setModel(self, strList):
        # self.comp.model().setStringList(strList)
        self.clear()
        self.insertItems(0, strList)
        self.comp.setModel(self.model())

    def focusInEvent(self, event):
        self.clearEditText()
        super(AutoCompleteComboBox, self).focusInEvent(event)

class CustomQCompleter(QtGui.QCompleter):
    """
    copied from: http://stackoverflow.com/a/7767999/2156909
    """
    def __init__(self, *args):#parent=None):
        super(CustomQCompleter, self).__init__(*args)
        self.local_completion_prefix = ""
        self.source_model = None

    def setModel(self, model):
        self.source_model = model
        super(CustomQCompleter, self).setModel(self.source_model)

    def updateModel(self):
        local_completion_prefix = self.local_completion_prefix
        class InnerProxyModel(QtGui.QSortFilterProxyModel):
            def filterAcceptsRow(self, sourceRow, sourceParent):
                index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
                return local_completion_prefix.lower() in self.sourceModel().data(index0).lower()
        proxy_model = InnerProxyModel()
        proxy_model.setSourceModel(self.source_model)
        super(CustomQCompleter, self).setModel(proxy_model)

    def splitPath(self, path):
        self.local_completion_prefix = path
        self.updateModel()
        return ""

【问题讨论】:

  • 你在super().__init__()之前调用self.ensureComboBox(),这意味着你的self.comboBox可能会被覆盖。
  • 对不起。我忘了提,我试过在super() 之前和之后打电话给self.ensureCombobox

标签: python oop pyside


【解决方案1】:

为了在 PySide 或 PyQt 中重新实现 C++ 方法,至少必须满足三个条件:

  1. 该方法必须是 Qt 的公共 API 的一部分
  2. 方法必须定义为virtual
  3. PySide/PyQt 必须为方法提供 python 包装器

请注意,如果第二个条件不成立,则在 python 中重新实现该方法将永远不会被 Qt 内部调用,因此几乎没有用处。 Qt 文档应始终清楚地指出方法是否为虚拟方法,例如:

    virtual QSize sizeHint() const

因此,由于ensureCombobox 不满足上述条件,因此很明显重新实现它不会有效(正如您已经发现的那样)。

但无论如何,对于像输入对话框这样简单的东西,真的值得费心去创建一个子类吗?一个对话框中只有四个小部件...

【讨论】:

  • 感谢您的解释。我问这个“简单”案例的问题是因为我想知道专家们一般会如何进行,因为我预计会有一个优雅的替代方案来替代这种“重新实现”。
  • @P.R.有许多 Qt 类提供 API 用于用您自己的实现替换某些元素(QComboBox 就是其中之一)。但也有像 QInputDialog 这样的简单便利类,它们的 API 通常不太灵活。这些类的优雅替代方案是简单地编写您想要的版本。这通常比尝试解决内置类中的限制更快、更容易。
【解决方案2】:

好的,所以问题是当您调用 QInputDialog 时,它会在 C++ 领域设置子小部件(或者在我看来是这样),因此您不能直接替换为您喜欢的风格。因此,您需要走更典型的路线,如下所示:

class AutoCompleteInputDialog(QtGui.QDialog):

    def __init__(self, *args, **kwargs):
        super(AutoCompleteInputDialog, self).__init__(*args, **kwargs)
        self.comboBox = AutoCompleteComboBox(self)
        self.va = QtGui.QVBoxLayout(self)
        self.va.addWidget(self.comboBox)
        self.box = QtGui.QWidget(self)
        self.ha = QtGui.QHBoxLayout(self)
        self.va.addWidget(self.box)
        self.box.setLayout(self.ha)
        self.OK = QtGui.QPushButton("OK",self)
        self.OK.setDefault(True)
        self.cancel = QtGui.QPushButton("Cancel",self)
        self.ha.addWidget(self.OK)
        self.ha.addWidget(self.cancel)
        self.OK.clicked.connect(self.accept)
        self.cancel.clicked.connect(self.reject)

结果值存储在acid.comboBox.currentText()中。

【讨论】:

  • 好的,谢谢,我明天试试。但这意味着在那个级别上,我必须重新实现完整的 QInputDialog 才能更改该单个元素。在这种情况下,这并不麻烦,但是在标准类更复杂的情况下,使用 OOP 的方法是不可能的?!这是 PySide 的问题,还是我在 C++ 中也会遇到同样的问题?
【解决方案3】:

AutoCompleteInputDialog.getItems 是一个类方法,在这种情况下实际上与 QtGui.QInputDialog.getItems 相同,通过继承。重载仅适用于实例方法,通过传递隐式 self。所以要让这个工作,你要么需要重新实现getItems,要么直接使用类而不使用便利函数:

acid = AutoCompleteInputDialog(parent=None)
#set up options, buttons
answer = acid.exec_()

【讨论】:

  • 我喜欢acid ;) 一般来说,你是对的,这样我的ensureComboBox 被调用了。不幸的是,当对话框出现时,我仍然可以看到标准的QComboBox..
  • 一个问题可能是这些事情(如QComboBox 成员以及ensureComboBox 函数)由InputDialogPrivate 处理,它与Qt 代码中的qpointer code.woboq.org/kde/qt4/src/gui/dialogs/… 一起使用
猜你喜欢
  • 2011-07-18
  • 2012-06-28
  • 1970-01-01
  • 2022-01-22
  • 2013-08-01
  • 2022-07-01
  • 1970-01-01
相关资源
最近更新 更多