如何在PyQt5上使用Vars,就像在tkinter上使用IntVar()或StringVar()一样?

2022-02-23 00:00:00 python python-3.x pyqt pyqt5

问题描述

创建变量

region = {'top': tk.IntVar(), 'left': tk.IntVar(), 'right': tk.IntVar(), 'bottom': tk.IntVar()}

创建条目并连接到无变量

tk.Entry(master, textvariable=region['top']).place(x=60, y=100)
tk.Entry(master, textvariable=region['left']).place(x=60, y=120)
tk.Entry(master, textvariable=region['right']).place(x=60, y=140)
tk.Entry(master, textvariable=region['bottom']).place(x=60, y=160)

我在某些函数上使用变量

def get_region():
    np.array(ImageGrab.grab(bbox=(region['top'].get(), region['left'].get(), region['right'].get(), region['bottom'].get())))

并使用变量在应用程序上保存和打开配置文件

def open_file(self):
    with open('{}'.format(filedialog.askopenfilename(initialdir="/", title="Select file",
                                                     filetypes=(("json files", "*.json"),
                                                                ("all files", "*.*")))), 'r') as config:
        data = json.load(config)

        region['top'].set(data['region']['top'])
        region['left'].set(data['region']['left'])
        region['right'].set(data['region']['right'])
        region['bottom'].set(data['region']['bottom'])

def save_as(self):
    json.decoder = {
        'region': {
            'top': region['top'].get(),
            'left': region['left'].get(),
            'right': region['right'].get(),
            'bottom': region['bottom'].get(),
            },
        }

    with open('{}'.format(filedialog.asksaveasfilename(initialdir="/", title="Save file",
                                                       filetypes=(("json files", "*.json"),
                                                                  ("all files", "*.*")))), 'w',
              encoding='utf-8') as config:
        json.dump(json.decoder, config, ensure_ascii=False, indent=2)
        config.close()

我在PyQt5中使用什么而不是IntVar()来构建这样的结构?


解决方案

Qt中没有相似的元素(有两个不同的库,方法不同),一种可能的解决方案是创建一个映射qproperties的对象:

from dataclasses import dataclass
import json
import sys

from PyQt5 import QtCore, QtGui, QtWidgets
import sip


@dataclass
class Property:
    qobject: QtCore.QObject = None
    qproperty: str = ""

    @property
    def value(self):
        if self.qobject and not sip.isdeleted(self.qobject):
            return self.qobject.property(self.qproperty)

    @value.setter
    def value(self, value):
        if self.qobject and not sip.isdeleted(self.qobject):
            self.qobject.setProperty(self.qproperty, value)


class Widget(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        top_spinbox = QtWidgets.QSpinBox(maximum=2147483647)
        left_spinbox = QtWidgets.QSpinBox(maximum=2147483647)
        right_spinbox = QtWidgets.QSpinBox(maximum=2147483647)
        bottom_spinbox = QtWidgets.QSpinBox(maximum=2147483647)

        button = QtWidgets.QPushButton(self.tr("grab"), clicked=self.grab_screen)

        file_menu = self.menuBar().addMenu(self.tr("&File"))

        open_action = file_menu.addAction(self.tr("Open File..."))
        open_action.triggered.connect(self.load_config)

        save_action = file_menu.addAction(self.tr("Save"))
        save_action.triggered.connect(self.save_config)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        flay = QtWidgets.QFormLayout(central_widget)
        flay.addRow(button)
        flay.addRow("Top:", top_spinbox)
        flay.addRow("Left:", left_spinbox)
        flay.addRow("Right:", right_spinbox)
        flay.addRow("Bottom:", bottom_spinbox)

        self.resize(640, 480)

        self.region = {
            "top": Property(qobject=top_spinbox, qproperty="value"),
            "left": Property(qobject=left_spinbox, qproperty="value"),
            "right": Property(qobject=right_spinbox, qproperty="value"),
            "bottom": Property(qobject=bottom_spinbox, qproperty="value"),
        }

    @QtCore.pyqtSlot()
    def grab_screen(self):
        top = self.region["top"].value
        left = self.region["left"].value
        right = self.region["right"].value
        bottom = self.region["bottom"].value

        print(top, left, right, bottom)

    @QtCore.pyqtSlot()
    def load_config(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(
            self,
            self.tr("Select file"),
            "/",
            self.tr("JSON files (*.json);;All files (*.*)"),
        )
        if filename:
            with open(filename, "r") as f:
                data = json.load(f)
                if "region" in data:
                    self.region["top"].value = data["region"].get("top", 0)
                    self.region["left"].value = data["region"].get("left", 0)
                    self.region["right"].value = data["region"].get("right", 0)
                    self.region["bottom"].value = data["region"].get("bottom", 0)

    @QtCore.pyqtSlot()
    def save_config(self):
        filename, _ = QtWidgets.QFileDialog.getSaveFileName(
            self,
            self.tr("Save file"),
            "/",
            self.tr("JSON files (*.json);;All files (*.*)"),
        )
        if filename:
            with open(filename, "w", encoding="utf-8") as f:
                data = {
                    "region": {
                        "top": self.region["top"].value,
                        "left": self.region["left"].value,
                        "right": self.region["right"].value,
                        "bottom": self.region["bottom"].value,
                    },
                }

                json.dump(data, f, ensure_ascii=False, indent=2)


def main():
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

QLineEdit示例:

from dataclasses import dataclass
import sys

from PyQt5 import QtCore, QtGui, QtWidgets
import sip


@dataclass
class Property:
    qobject: QtCore.QObject = None
    qproperty: str = ""

    @property
    def value(self):
        if self.qobject and not sip.isdeleted(self.qobject):
            return self.qobject.property(self.qproperty)

    @value.setter
    def value(self, value):
        if self.qobject and not sip.isdeleted(self.qobject):
            self.qobject.setProperty(self.qproperty, value)


def main():
    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QLineEdit()

    prop = Property(w, "text")

    # variable for test
    counter = 0

    def on_timeout():
        nonlocal counter
        print("text:", prop.value)
        counter += 1
        prop.value = counter

    timer = QtCore.QTimer(interval=1000, timeout=on_timeout)
    timer.start()

    w.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

相关文章