python - 在选项卡之间传递变化的数据

标签 python qt pyqt pyside pyqtgraph

我正在尝试构建一个带有两个选项卡的程序。在 Tab1 中,我从图像中选择点坐标 (x,y) 并将其转换为值 self.a。除了图像之外,我在 Tab1 中还有一些其他 UI(即表格)。现在,我想将值 self.a 传递给 Tab2 (不继承所有其他内容)。请记住,当单击新点时,self.a 可以不断更新。

from PySide import QtGui, QtCore
import pandas as pd
import pyqtgraph as pg
import numpy as np
QVariant = lambda value=None: value


class Widget(QtGui.QWidget):

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

        v_global_layout = QtGui.QVBoxLayout()
        v_global_layout.addWidget(TabDialog())
        v_global_layout.setAlignment(QtCore.Qt.AlignTop)

        self.setLayout(v_global_layout)


class TabDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        tab_widget = QtGui.QTabWidget()

        tab_widget.addTab(Tab1(), "1")
        tab_widget.addTab(Tab2(), "2")

        main_layout = QtGui.QVBoxLayout()
        main_layout.addWidget(tab_widget)
        self.setLayout(main_layout)


class Tab1(QtGui.QTabWidget):
    def __init__(self):
        super().__init__()
        layout = QtGui.QHBoxLayout()
        self.fig = pg.PlotWidget(name='Example: Selecting scatter points')

        self.plot_area = self.fig.plotItem
        self.a = pg.ScatterPlotItem(pxMode=False)
        spots = []
        for i in range(10):
            for j in range(10):
                spots.append({'pos': (1*i, 1*j), 'size': 1, 'pen': {'color': 'w', 'width': 2},
                              'brush': pg.intColor(i*10+j, 100)})
        self.a.addPoints(spots)

        self.plot_area.addItem(self.a)

        self.a.dataModel = DataFrameModel()
        self.a.dataTable = QtGui.QTableView()
        self.a.dataTable.setModel(self.a.dataModel)

        layout.addWidget(self.a.dataTable)
        layout.addWidget(self.fig)
        self.setLayout(layout)

        self.a.array = np.zeros((0, 2))

        def clicked(self, points):
            for p in points:
                p.setPen('b', width=2)
                position = p.viewPos()
                self.array = np.append(self.array, np.array([[position.x(), position.y()]]), axis=0)
            c = range(len(self.array))
            c = list(map(str, c))
            self.dataModel.signalUpdate(self.array, columns=c)
            self.dataModel.printValues() # also: print(self.array)
        self.a.sigClicked.connect(clicked)


class Tab2(QtGui.QTabWidget):
    def __init__(self):
        super().__init__()
        layout = QtGui.QHBoxLayout()

        ##### Here I want to use Tab1.a and not inherit all the other stuff(layout) #####
        #print("values = ", Tab1.a.array) # a should change when a new point is selected in Tab1
        #####################################
        self.setLayout(layout)


class DataFrameModel(QtCore.QAbstractTableModel):
    """ data model for a DataFrame class """
    def __init__(self):
        super(DataFrameModel, self).__init__()
        self.df = pd.DataFrame()

    def signalUpdate(self, dataIn, columns):
        self.df = pd.DataFrame(dataIn, columns)
        self.layoutChanged.emit()

    def printValues(self):
        print("DataFrame values:\n", self.df.values)

    def values(self):
        return self.df.values

    #------------- table display functions -----------------
    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QVariant()

        if orientation == QtCore.Qt.Horizontal:
            try:
                return self.df.columns.tolist()[section]
            except (IndexError, ):
                return QVariant()
        elif orientation == QtCore.Qt.Vertical:
            try:
                # return self.df.index.tolist()
                return self.df.index.tolist()[section]
            except (IndexError, ):
                return QVariant()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QVariant()

        if not index.isValid():
            return QVariant()
        return QVariant(str(self.df.ix[index.row(), index.column()]))

    def rowCount(self, index=QtCore.QModelIndex()):
        return self.df.shape[0]

    def columnCount(self, index=QtCore.QModelIndex()):
        return self.df.shape[1]


if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)

    main_window = Widget()
    main_window.setGeometry(100, 100, 640, 480)
    main_window.show()

    sys.exit(app.exec_())

最佳答案

在这种情况下,您只需使用信号即可完成工作。

在这里,您尝试像访问静态属性一样访问 Tab1.a,但它不是静态属性。理想情况下,我们应该尝试解耦​​不同的小部件。我们应该尽量将他们之间的依赖性降到最低,并将他们视为无知和不了解对方。 TabDialog 可以了解这些小部件中的每一个以及它们之间的连接(在本例中为 Tab1Tab2)。因此,TabDialog 可以负责这些小部件之间的通信。

为此,我们将两个选项卡作为 TabDialog 类的属性,如下所示:

# Have the tabs as this dialog's class properties
self.tab1 = Tab1(image)
self.tab2 = Tab2()

tab_widget.addTab(self.tab1, "1")
tab_widget.addTab(self.tab2, "2")

在 Tab2 类中,我们假设要与 Tab1.a 映射的值是 points_from_tab1_a:

class Tab2(QtGui.QTabWidget):
    def __init__(self):
        super().__init__()
        layout = QtGui.QHBoxLayout()

        self.points_from_tab1_a = []
        self.setLayout(layout)

现在,在 TabDialog 中,我们将 tab1.asigClicked 信号连接到更新 tab2.points_from_tab1_a< 的方法:

self.tab1.a.sigClicked.connect(self.pointChanged)

def pointChanged(self, points):
    tab2.points_from_tab1_a = tab1.a

这应该可以解决问题。因此,经过这些更改后,您的完整代码片段将如下所示:

from PySide import QtGui, QtCore
import pandas as pd
import pyqtgraph as pg
import numpy as np
QVariant = lambda value=None: value


class Widget(QtGui.QWidget):

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

        v_global_layout = QtGui.QVBoxLayout()
        v_global_layout.addWidget(TabDialog())
        v_global_layout.setAlignment(QtCore.Qt.AlignTop)

        self.setLayout(v_global_layout)


class TabDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        tab_widget = QtGui.QTabWidget()

        # Have the tabs as this dialog's class properties
        self.tab1 = Tab1(image)
        self.tab2 = Tab2()

        tab_widget.addTab(self.tab1, "1")
        tab_widget.addTab(self.tab2, "2")

        self.tab1.a.sigClicked.connect(self.pointChanged)

        main_layout = QtGui.QVBoxLayout()
        main_layout.addWidget(tab_widget)
        self.setLayout(main_layout)

    def pointChanged(self, points):
        tab2.points_from_tab1_a = tab1.a


class Tab1(QtGui.QTabWidget):
    def __init__(self):
        super().__init__()
        layout = QtGui.QHBoxLayout()
        self.fig = pg.PlotWidget(name='Example: Selecting scatter points')

        self.plot_area = self.fig.plotItem
        self.a = pg.ScatterPlotItem(pxMode=False)
        spots = []
        for i in range(10):
            for j in range(10):
                spots.append({'pos': (1*i, 1*j), 'size': 1, 'pen': {'color': 'w', 'width': 2},
                              'brush': pg.intColor(i*10+j, 100)})
        self.a.addPoints(spots)

        self.plot_area.addItem(self.a)

        self.a.dataModel = DataFrameModel()
        self.a.dataTable = QtGui.QTableView()
        self.a.dataTable.setModel(self.a.dataModel)

        layout.addWidget(self.a.dataTable)
        layout.addWidget(self.fig)
        self.setLayout(layout)

        self.a.array = np.zeros((0, 2))

        def clicked(self, points):
            for p in points:
                p.setPen('b', width=2)
                position = p.viewPos()
                self.array = np.append(self.array, np.array([[position.x(), position.y()]]), axis=0)
            c = range(len(self.array))
            c = list(map(str, c))
            self.dataModel.signalUpdate(self.array, columns=c)
            self.dataModel.printValues() # also: print(self.array)
        self.a.sigClicked.connect(clicked)


class Tab2(QtGui.QTabWidget):
    def __init__(self):
        super().__init__()
        layout = QtGui.QHBoxLayout()

        self.points_from_tab1_a = []
        ##### Here I want to use Tab1.a and not inherit all the other stuff(layout) #####
        #print("values = ", Tab1.a.array) # a should change when a new point is selected in Tab1
        #####################################
        self.setLayout(layout)


class DataFrameModel(QtCore.QAbstractTableModel):
    """ data model for a DataFrame class """
    def __init__(self):
        super(DataFrameModel, self).__init__()
        self.df = pd.DataFrame()

    def signalUpdate(self, dataIn, columns):
        self.df = pd.DataFrame(dataIn, columns)
        self.layoutChanged.emit()

    def printValues(self):
        print("DataFrame values:\n", self.df.values)

    def values(self):
        return self.df.values

    #------------- table display functions -----------------
    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QVariant()

        if orientation == QtCore.Qt.Horizontal:
            try:
                return self.df.columns.tolist()[section]
            except (IndexError, ):
                return QVariant()
        elif orientation == QtCore.Qt.Vertical:
            try:
                # return self.df.index.tolist()
                return self.df.index.tolist()[section]
            except (IndexError, ):
                return QVariant()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QVariant()

        if not index.isValid():
            return QVariant()
        return QVariant(str(self.df.ix[index.row(), index.column()]))

    def rowCount(self, index=QtCore.QModelIndex()):
        return self.df.shape[0]

    def columnCount(self, index=QtCore.QModelIndex()):
        return self.df.shape[1]


if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)

    main_window = Widget()
    main_window.setGeometry(100, 100, 640, 480)
    main_window.show()

    sys.exit(app.exec_())

您可以使用信号和槽的概念随意更改它以满足您的需求。希望这有用。

关于python - 在选项卡之间传递变化的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27469654/

相关文章:

python - 图像预处理和数据增强应该如何用于语义分割?

c++ - 带有自定义小部件的 QItemDelegate

python - 如何在 PyQt5 中用 QDrag 拖动时显示 QPushButton?

android - QtCreator : how to see program output when deployed on Android

c++ - Qt程序部署

python - PyQt- 哪一列获得了右键单击?

qt - 解决与 PyQt 新式信号槽的冲突

python - 类继承: where to put a method?

python - 将nan值转换为零

python - Python 中最长的递增子序列(For vs While 循环)