如何使用PyQt

时间:2018-07-25 19:51:51

标签: python matplotlib pyqt widget pyqt5

我是PyQt的新手,我发现很难在窗口中订购窗口小部件。

我有一个实时更新的图,一个用户每次单击该图时都会更新的表,以及两个按钮(开始/停止)。

当前布局的行为如下:

enter image description here

我希望它看起来像这样:

enter image description here

“其他小部件”对应于以后将在工作时添加的小部件(例如滑块或下拉列表)。表格也应该尽可能薄,以最大程度地增加图的大小,并且表格不要占用不必要的空间。我当前的代码是以下代码(它是独立的):

import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure

def onclick(event):
    global clicks
    clicks.append(event.xdata)
    return

class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(ApplicationWindow, self).__init__()
        self._title = 'Prueba real-time'
        self.setWindowTitle(self._title)
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        layout = QtWidgets.QHBoxLayout(self._main)

        dynamic_canvas = FigureCanvas(Figure(figsize=(10, 10)))
        layout.addWidget(dynamic_canvas)

        self._dynamic_ax = dynamic_canvas.figure.subplots()
        dynamic_canvas.figure.canvas.mpl_connect('button_press_event', onclick)
        self._dynamic_ax.grid()
        self._timer = dynamic_canvas.new_timer(
            100, [(self._update_window, (), {})])
        self._timer.start()

        button_stop = QtWidgets.QPushButton('Stop', self)
        layout.addWidget(button_stop)
        button_stop.clicked.connect(self.button_pressed)

        button_start = QtWidgets.QPushButton('Start', self)
        layout.addWidget(button_start)
        button_start.clicked.connect(self.button_pressed)

        self.table_clicks = QtWidgets.QTableWidget()
        self.table_clicks.setRowCount(0)
        self.table_clicks.setColumnCount(2)
        layout.addWidget(self.table_clicks)

    def button_pressed(self):
        if self.sender().text() == 'Stop':
            self._timer.stop()
        if self.sender().text() == 'Start':
            self._timer.start()

    def _update_window(self):
        self._dynamic_ax.clear()
        global x, y1, y2, y3, N, count_iter, last_number_clicks
        x.append(x[count_iter] + 0.01)
        y1.append(np.random.random())
        idx_inf = max([count_iter-N, 0])
        if last_number_clicks < len(clicks):
            for new_click in clicks[last_number_clicks:(len(clicks))]:
                rowPosition = self.table_clicks.rowCount()
                self.table_clicks.insertRow(rowPosition)
                self.table_clicks.setItem(rowPosition,0, QtWidgets.QTableWidgetItem(str(new_click)))
                self.table_clicks.setItem(rowPosition,1, QtWidgets.QTableWidgetItem("Descripcion"))
            last_number_clicks = len(clicks)
        self._dynamic_ax.plot(x[idx_inf:count_iter], y1[idx_inf:count_iter],'-o', color='b')
        count_iter += 1
        self._dynamic_ax.figure.canvas.draw()
#%%
if __name__ == "__main__":
    pressed_key = {}
    clicks = []
    last_number_clicks = len(clicks)
    N = 25
    y1 = [np.random.random()]
    x = [0]
    count_iter = 0
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()

2 个答案:

答案 0 :(得分:2)

所需的结构是网格,因此应使用QGridLayout

QGridLayout具有addWidget()方法,您可以在其中指示将放置小部件的行和列,与addLayout()相同

  

void QGridLayout :: addWidget(QWidget * widget,int行,int列,Qt :: Alignment对齐= ...)

     

void QGridLayout :: addLayout(QLayout * layout,int行,int列,Qt :: Alignment对齐= ...)

在您的情况下,结构应为以下形式:

QGridLayout
├── Plot (0, 0)
├── Table (0, 1)
├── Other Widgets (1, 0)
└── QVBoxLayout (1, 1)
    ├── button_start
    └── button_stop

然后您可以使用setColumnStretch()来指示每列的权重,即列宽相对于第2列具有成比例的宽度:

layout.setColumnStretch(0, 2)
layout.setColumnStretch(1, 1)

考虑到上述情况,我们有以下内容:

import sys
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure

def onclick(event):
    global clicks
    clicks.append(event.xdata)

class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(ApplicationWindow, self).__init__()
        self._title = 'Prueba real-time'
        self.setWindowTitle(self._title)

        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)

        dynamic_canvas = FigureCanvas(Figure(figsize=(10, 10)))
        self._dynamic_ax = dynamic_canvas.figure.subplots()
        dynamic_canvas.figure.canvas.mpl_connect('button_press_event', onclick)
        self._dynamic_ax.grid()
        self._timer = dynamic_canvas.new_timer(
            100, [(self._update_window, (), {})])
        self._timer.start()

        button_stop = QtWidgets.QPushButton('Stop', self)

        button_stop.clicked.connect(self._timer.stop)

        button_start = QtWidgets.QPushButton('Start', self)

        button_start.clicked.connect(self._timer.start)

        self.table_clicks = QtWidgets.QTableWidget(0, 2)
        self.table_clicks.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)

        other_widget = QtWidgets.QLabel("Other widgets", 
            font=QtGui.QFont("Times", 60, QtGui.QFont.Bold), 
            alignment=QtCore.Qt.AlignCenter)

        # layouts

        layout = QtWidgets.QGridLayout(self._main)

        layout.addWidget(dynamic_canvas, 0, 0)
        layout.addWidget(self.table_clicks, 0, 1)
        layout.addWidget(other_widget, 1, 0)

        button_layout = QtWidgets.QVBoxLayout()
        button_layout.addWidget(button_stop)
        button_layout.addWidget(button_start)        

        layout.addLayout(button_layout, 1, 1)

        layout.setColumnStretch(0, 2)
        layout.setColumnStretch(1, 1)

    def _update_window(self):
        self._dynamic_ax.clear()
        global x, y1, y2, y3, N, count_iter, last_number_clicks
        x.append(x[count_iter] + 0.01)
        y1.append(np.random.random())
        idx_inf = max([count_iter-N, 0])
        if last_number_clicks < len(clicks):
            for new_click in clicks[last_number_clicks:(len(clicks))]:
                rowPosition = self.table_clicks.rowCount()
                self.table_clicks.insertRow(rowPosition)
                self.table_clicks.setItem(rowPosition,0, QtWidgets.QTableWidgetItem(str(new_click)))
                self.table_clicks.setItem(rowPosition,1, QtWidgets.QTableWidgetItem("Descripcion"))
            last_number_clicks = len(clicks)
        self._dynamic_ax.plot(x[idx_inf:count_iter], y1[idx_inf:count_iter],'-o', color='b')
        count_iter += 1
        self._dynamic_ax.figure.canvas.draw()
#%%
if __name__ == "__main__":
    pressed_key = {}
    clicks = []
    last_number_clicks = len(clicks)
    N = 25
    y1 = [np.random.random()]
    x = [0]
    count_iter = 0
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    sys.exit(qapp.exec_())

enter image description here

答案 1 :(得分:2)

我将在自己的类中亲自分离程序的不同部分,并使用几种垂直和水平布局来放置小部件。如果要添加功能,这特别有帮助。特别是对于大型应用程序,这有助于使经常变得一团糟的MainWindow变得更简单,更易于理解

import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure

def onclick(event):
    global clicks
    clicks.append(event.xdata)
    return

class TableWidget(QtWidgets.QWidget):
    def __init__(self,parent,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.parent = parent
        layout = QtWidgets.QVBoxLayout(self)

        self.table_clicks = QtWidgets.QTableWidget()
        self.table_clicks.setRowCount(0)
        self.table_clicks.setColumnCount(2)
        layout.addWidget(self.table_clicks)

        button_widget = QtWidgets.QWidget(self)
        layout.addWidget(button_widget)
        button_layout = QtWidgets.QHBoxLayout(button_widget)

        button_stop = QtWidgets.QPushButton('Stop', self)
        button_layout.addWidget(button_stop)
        button_stop.clicked.connect(self.parent.plot_widget.button_pressed)

        button_start = QtWidgets.QPushButton('Start', self)
        button_layout.addWidget(button_start)
        button_start.clicked.connect(self.parent.plot_widget.button_pressed)

class PlotWidget(QtWidgets.QWidget):
    def __init__(self,parent,*args,**kwargs):
        super().__init__(parent,*args,**kwargs)
        self.parent = parent

        layout = QtWidgets.QVBoxLayout(self)

        dynamic_canvas = FigureCanvas(Figure(figsize=(10, 10)))
        layout.addWidget(dynamic_canvas)

        self._dynamic_ax = dynamic_canvas.figure.subplots()
        dynamic_canvas.figure.canvas.mpl_connect('button_press_event', onclick)
        self._dynamic_ax.grid()
        self._timer = dynamic_canvas.new_timer(
            100, [(self._update_window, (), {})])
        self._timer.start()


    def button_pressed(self):
        if self.sender().text() == 'Stop':
            self._timer.stop()
        if self.sender().text() == 'Start':
            self._timer.start()

    def _update_window(self):
        self._dynamic_ax.clear()
        global x, y1, y2, y3, N, count_iter, last_number_clicks
        x.append(x[count_iter] + 0.01)
        y1.append(np.random.random())
        idx_inf = max([count_iter-N, 0])
        if last_number_clicks < len(clicks):
            for new_click in clicks[last_number_clicks:(len(clicks))]:
                rowPosition = self.parent.table_widget.table_clicks.rowCount()
                self.parent.table_widget.table_clicks.insertRow(rowPosition)
                self.parent.table_widget.table_clicks.setItem(rowPosition,0, QtWidgets.QTableWidgetItem(str(new_click)))
                self.parent.table_widget.table_clicks.setItem(rowPosition,1, QtWidgets.QTableWidgetItem("Descripcion"))
            last_number_clicks = len(clicks)
        self._dynamic_ax.plot(x[idx_inf:count_iter], y1[idx_inf:count_iter],'-o', color='b')
        count_iter += 1
        self._dynamic_ax.figure.canvas.draw()


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(ApplicationWindow, self).__init__()
        self._title = 'Prueba real-time'
        self.setWindowTitle(self._title)
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)
        main_layout = QtWidgets.QHBoxLayout(self._main)

        self.plot_widget = PlotWidget(self)
        main_layout.addWidget(self.plot_widget)


        self.table_widget = TableWidget(self)
        main_layout.addWidget(self.table_widget)


#%%
if __name__ == "__main__":
    pressed_key = {}
    clicks = []
    last_number_clicks = len(clicks)
    N = 25
    y1 = [np.random.random()]
    x = [0]
    count_iter = 0
    qapp = QtWidgets.QApplication(sys.argv)
    app = ApplicationWindow()
    app.show()
    qapp.exec_()