用于将字符串或其他非列表/树类型绑定到qt小部件的简单模型(mvvm样式)

时间:2014-08-05 07:08:14

标签: qt mvvm garbage-collection pyqt signals-slots

我上次使用桌面gui编程的主要经验是以mvvm方式使用c#/ wpf。我添加了一个可观察的集合,你不必在主线程上进行更改(它是通过在适当的时候使用锁定并在gui线程上运行来调用普通可观察集合的实例来实现的)。您可以在viewmodel中使用仅影响viewmodel的方法,而不是使用gui事件处理程序来浏览文件树。我非常喜欢这样一个事实:你可以大部分包含自己的gui代码,并且视图模型代码基本上是直接的,避免处理视图,除非通过更新状态和发送更改事件。

我正在使用pyqt 4.6构建一个小的自包含(如果可能的话没有依赖)应用程序(所以我不认为qml是可用的,同时qtwebkit似乎是在一个不同的包中,不是&#39 ; t安装在这些盒子上)。我有兴趣尝试以某种mvvm方式将数据与ui分开。

我可以将qt模型/视图(setModel)用于列表和树模型,但我不确定如何处理字符串/整数/其他值。该应用程序可能是相当静态的(中间没有消失的gui元素),但如果它变得更复杂(比如弹出窗口),我希望它不会因内存泄漏而崩溃或内存使用爆炸。我对python gc如何与信号/插槽以及qt的父/子内存清理进行交互有点不确定。

我写的应用程序很小,所以一个较小的自包含解决方案(可能不是通用/功能)或者我可以从中提取/复制代码的小型bsd / mit库将是首选。

到目前为止,这是我所拥有的,尽管如上所述,我有点不确定它的无错误。

#!/usr/bin/python
#
from __future__ import print_function
from PyQt4 import QtCore
from PyQt4 import QtGui
import sys
from time import sleep

def identity_function(x):
    return x


def data_bind(model_get_prop, model_set_prop,
              get_widget_prop, set_widget_prop,
              model_prop_changed, window_prop_changed,
              transform_func=identity_function,
              transform_back_func=identity_function):
    set_widget_prop(transform_func(model_get_prop()))

    def set_m():
        if model_get_prop() != get_widget_prop():
            model_set_prop(transform_back_func(get_widget_prop()))
    window_prop_changed.connect(set_m)

    def set_w_prop():
        if get_widget_prop() != model_get_prop():
            set_widget_prop(transform_func(model_get_prop()))
    model_prop_changed.connect(set_w_prop)

class ValueModel(QtCore.QObject):
    model_changed = QtCore.pyqtSignal()

    def __init__(self, value):
        super(ValueModel, self).__init__()
        self.__value = value
    @property
    def value(self):
        return self.__value
    @value.setter
    def value(self, value):
        if self.__value != value:
            self.__value = value
            self.model_changed.emit()


def gui_main():
    app = QtGui.QApplication([])
    s_model = ValueModel("yo")

    w = QtGui.QWidget()
    vbox = QtGui.QVBoxLayout()

    t = QtGui.QTextEdit()
    vbox.addWidget(t)
    t2 = QtGui.QTextEdit()
    vbox.addWidget(t2)
    w.setLayout(vbox)
    data_bind(lambda: s_model.value, lambda v: setattr(s_model, 'value', v), t.toPlainText, t.setPlainText, s_model.model_changed, t.textChanged)
    data_bind(lambda: s_model.value, lambda v: setattr(s_model, 'value', v), t2.toPlainText, t2.setPlainText, s_model.model_changed, t2.textChanged)

    l1 = QtGui.QLineEdit()
    l2 = QtGui.QLineEdit()

    lm1 = ValueModel(2)
    lm2 = ValueModel(1)

    vbox.addWidget(l1)
    vbox.addWidget(l2)

    lm1.model_changed.connect(lambda: print("l1 + l2 is " + str(lm1.value+lm2.value)))
    lm2.model_changed.connect(lambda: print("l1 + l2 is " + str(lm1.value+lm2.value)))
    data_bind(lambda: lm1.value, lambda v: setattr(lm1, 'value', v), l1.text, l1.setText, lm1.model_changed, l1.textChanged, str, int)
    data_bind(lambda: lm2.value, lambda v: setattr(lm2, 'value', v), l2.text, l2.setText, lm2.model_changed, l2.textChanged, str, int)

    w.show()
    t.setText("a")
    print(s_model.value)
    sys.exit(app.exec_())

gui_main()

1 个答案:

答案 0 :(得分:3)

我认为你想要的是QDataWidgetMapper

简而言之,它允许您将模型列的值映射到窗口小部件的显示属性(如标签或lineedit)。如果更新窗口小部件或模型中的值,则另一个将更新。

您还可以逐步浏览模型的行,这些行将所有映射的小部件更新为模型中下一行的内容(这可能超出您的需要,因此您只需要有一行模特)。

有关详细信息,请参阅Qt文档:http://qt-project.org/doc/qt-4.8/qdatawidgetmapper.html