【问题标题】:What is the best way to share data between widgets with PyQt?使用 PyQt 在小部件之间共享数据的最佳方式是什么?
【发布时间】:2017-05-29 07:48:15
【问题描述】:

目标

我正在使用 Python 和 PyQt、Numpy 和 Matplotlib 做一个数据处理和可视化程序。数据从 csv 或二进制文件(使用 np.genfromtxt 和 np.fromfile 函数)作为 Numpy 数组中主窗口的成员变量导入。我希望能够从在 Matplotlib 画布中绘制曲线的父(或子父)小部件访问这些数据。

我的想法

  • 我考虑过使用 QApplication.topLevelWidgets(),但它返回一个包含 4 个小部件的列表:主窗口和 3 个意外的 QMenu 小部件。
  • 我知道我可以在 Matplotlib 画布中多次使用 getParent(),但我正在寻找更强大的解决方案:我不希望它依赖于小部件层次结构。
  • 我想知道全局变量是否是一个明智的解决方案?我从未在 Python 中使用过它们。

提前感谢您的任何建议。

【问题讨论】:

  • 你可以使用模型-视图-控制器模式。
  • 我从来没有这样做过,我能存储 Numpy 数组吗?我看到的关于使用 SQL 数据库的示例。
  • 全局变量通常是个坏主意。如果您需要在不同对象之间共享相同的数据,您可以考虑使用Singleton
  • 模型-视图-控制器模式在视图-控制器模式中经常被简化,因为模型通常引用冻结的数据。如果你想处理动态数据(如在运行时生成的),你只需要你的控制器知道这些数据。通常的结构如下:您在 PyQt 中有一个运行图形方面的模块,并且只有图形方法(如“显示内容”);处理模块,其方法旨在处理数据(“计算内容”、“从文件中获取内容”);和一个控制模块(可能是main.py),只是将它们放在一起。
  • 感谢您的解释。你能指出我可以用来实现这一目标的类吗?我不明白控制器具体是什么。

标签: python qt numpy pyqt


【解决方案1】:

您似乎需要一个集中的地方来存储您的数据,这些数据可以被任意数量的不同对象(Qt 对象、numpy 对象,没有区别)访问。我的建议是Singleton 模式。我将使用 Alex Martelli Borg 实现进行一个小实验,但请查看 this resource (Python 3 Patterns, Recipes and Idioms) 以查看一些不同的示例。

我认为解释 Singleton 最简单的方法如下:当您多次实例化同一个对象(例如 QWidget)时,每个实例将引用不同的对象。因此,如果您在一个实例中进行更改,它不会影响其他实例(一般而言)。 Singleton 的工作方式不同。您创建的每个实例都指向同一个对象。检查以下示例:

import numpy as np


class Singleton:
    """Alex Martelli implementation of Singleton (Borg)
    http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html"""
    _shared_state = {}

    def __init__(self):
        self.__dict__ = self._shared_state


class Database(Singleton):
    def __init__(self):
        Singleton.__init__(self)

    def get(self):
        return self._data

    def change(self):
        # Just arbitrary experiment of changing the data
        self.load()

    def load(self):
        # For example from csv or binary files using
        # np.genfromtxt or np.fromfile
        self._data = np.random.randint(0, 10, (3, 3))

使用上面的代码,我刚刚创建了我的“数据库”对象。每次我调用这个数据库并询问它的数据时,它都会返回相同的数据。请注意我如何将随机 numpy 数组作为数据。假设我创建了两个新对象:

class SomeObject:
    def __init__(self):
        self._something = None

    def doSomething(self):
        db = Database()
        print(db.get())


class OtherObject:
    def __init__(self):
        self._othersomething = None

    def doOtherSomething(self):
        db = Database()
        print(db.get())

两者都调用数据库的一个实例。但他们会引用相同的数据吗?让我们检查一下:

# Loading data into Singleton
db = Database()
db.load()

# Creating a couple of different objects
so = SomeObject()
oo = OtherObject()

# Making sure each object is calling the database
print("Original data in database:")
so.doSomething()
oo.doOtherSomething()

# Changing data in database
db.change()

# Checking the changes in both objects.
print("Changed data in database:")
so.doSomething()
oo.doOtherSomething()

结果是这样的:

Original data in database:
[[7 4 7]
 [3 4 1]
 [9 5 6]]
[[7 4 7]
 [3 4 1]
 [9 5 6]]
Changed data in database:
[[3 8 8]
 [7 2 8]
 [1 5 1]]
[[3 8 8]
 [7 2 8]
 [1 5 1]]

因此,创建它们自己的数据库实例的两个对象都获得完全相同的数据(在调用 load 或 change 方法时是随机的)。这可能是在不同对象之间共享数据的最平易近人(和可维护)的方式。

编辑: 使数据库能够了解数据是否已加载。让我们通过添加“hasData”方法对我们的 Database 类进行一些小修改:

class Database(Singleton):
    def __init__(self):
        Singleton.__init__(self)

    def get(self):
        return self._data

    def change(self):
        # Just arbitrary experiment of changing the data
        self.load()

    def hasData(self):
        return hasattr(self, "_data")

    def load(self):
        # For example from csv or binary files using
        # np.genfromtxt or np.fromfile
        self._data = np.random.randint(0, 10, (3, 3))

这个方法是询问数据库是否已经有属性_data。让我们做一个实验:

db = Database()
print(db.hasData())
db.load()
print(db.hasData())

结果:

False
True

其他变体可以使用第一个实例化来加载数据。这在很大程度上取决于您的软件的需求。

【讨论】:

  • 好的,我明白了,非常感谢您提供这个详细的示例。为了确保我理解正确,如果我在任何小部件中创建数据库对象,它总是会引用与我创建的第一个数据库对象相同的数据(即使它在另一个小部件中)?
  • 是的。请注意首次将数据加载到 Database 对象的位置(这样您就不会尝试使用尚未加载的数据)。例如,您可以在创建 MainWindow(或 Widget)对象之前执行此操作。
  • 事实上,我必须在创建主窗口后将数据加载到其中,因为导入是由用户使用 QFileDialog 完成的。我在 MDI 子窗口中使用数据。有没有一种简单的方法来存储文件已经加载的事实(例如,在数据库中使用布尔成员变量)?
  • 是的。我使用的单例示例并没有真正准备好在 init 中创建变量(如果需要,请查看资源链接以了解实现单例的其他方法),但标志系统可以工作。此外,如果您仍想使用这种 Singleton 实现,还有一些方法。检查我的编辑。
  • 谢谢,这正是我需要的。关于单例的最后一件事,如果我理解正确的话,它的定义意味着我们创建的所有属性(使用 self.[...] = [...])都是自动静态的?
猜你喜欢
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 2011-05-02
  • 1970-01-01
  • 2010-11-29
  • 2010-09-20
  • 2012-02-05
  • 1970-01-01
相关资源
最近更新 更多