Python 如何防止PyQt网格元素调整大小并保持所有小部件的间距均匀?

Python 如何防止PyQt网格元素调整大小并保持所有小部件的间距均匀?,python,pyqt,pyqtgraph,Python,Pyqt,Pyqtgraph,我在保持gridLayout中所有元素的大小时遇到了一些问题 局部地,内部小部件在我更新绘图数据(宽度和高度)后完全改变大小,尤其是在最大化/最小化或更改窗口大小后: 运行#1 运行#2 我希望实现的是,无论窗户的尺寸如何,尺寸都要保持均匀分布: 期望输出: 这是我为填充网格所写内容的总结,以及我如何更新CSS以使边框改变颜色。我注意到,如果我不更改CSS,那么这个问题就不明显了 class ResultsViewer(QtGui.QWidget): plots = {} #the

我在保持gridLayout中所有元素的大小时遇到了一些问题

局部地,内部小部件在我更新绘图数据(宽度和高度)后完全改变大小,尤其是在最大化/最小化或更改窗口大小后:

运行#1 运行#2

我希望实现的是,无论窗户的尺寸如何,尺寸都要保持均匀分布:

期望输出:

这是我为填充网格所写内容的总结,以及我如何更新CSS以使边框改变颜色。我注意到,如果我不更改CSS,那么这个问题就不明显了

class ResultsViewer(QtGui.QWidget):
    plots = {} #the currently displayed plot widgets
    curves = {} #the currently displayed data that store the points

    def __init__(self):
        super(ResultsViewer, self).__init__()
        self.win = QtGui.QMainWindow()
        self.win.setCentralWidget(self)
        self.win.resize(800, 250)
        self.win.setWindowTitle("Cavity Results Viewer")
        self.grid = QtGui.QGridLayout(self)
        self.win.setWindowFlags(self.win.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)

    def reset_indicators(self):
        for plot in self.plots.keys():
            self.plots[plot].setStyleSheet("""
                                border-top: 5px solid rgba(0,0,0,0);
                                border-radius: 12px;
                                """)

    def set_indicator_border_color(self, cavnum, result):
        color = "lime" if result['decision'] else "red"
        self.plots[cavnum].setStyleSheet("""
                        border-top: 5px solid %s;
                        border-radius: 12px;
                        """ % color)

    def create_indicators(self, params):
        for c, cavnum in enumerate(params.keys()):
            r = (4 % (c + 1)) / 4 #every fourth column jump to next row

            box = QtGui.QHBoxLayout()

            plt = pg.PlotWidget()

            plt.setStyleSheet("""
                        border-top: 5px solid yellow;
                        border-radius: 12px;
                    """)
            curve_blue = plt.plotItem.plot(pen=None, symbol='o', symbolPen=None, symbolSize=8, symbolBrush=(100, 100, 255, 80)) #points for showing history data
            curve_green = plt.plotItem.plot(pen=None, symbol='o', symbolPen=None, symbolSize=8,symbolBrush=(100, 255, 100, 80))
            curve_blue_last = plt.plotItem.plot(pen=None, symbol='x', symbolPen=None, symbolSize=18, symbolBrush=(50, 50, 255, 255)) #points for showing the newest data
            curve_green_last = plt.plotItem.plot(pen=None, symbol='x', symbolPen=None, symbolSize=18,symbolBrush=(50, 255, 50, 255))

            plt.plotItem.setLabel('left', "Amplitude", units='A')
            plt.plotItem.setLabel('bottom', "Frequency", units='Hz')
            self.plots[cavnum] = plt
            self.curves[cavnum] = {"blue": curve_blue,
                                   "blue_last": curve_blue_last,
                                   "green": curve_green,
                                   "green_last": curve_green_last}
            box.addWidget(plt)
            self.grid.addLayout(box, r, c % 4)

    def update(self, cavnum, results):
        #update the plots and render all points
        ...
        ...
        self.set_indicator_border_color(cavnum, result)
[更新]

这是一个功能齐全的示例:

import sys, time
import random
import numpy as np
import pyqtgraph as pg
from PyQt4 import QtCore, QtGui

class ResultsViewer(QtGui.QWidget):
    plots = {} #the currently displayed plot widgets
    curves = {} #the currently displayed data that store the points

    def __init__(self):
        super(ResultsViewer, self).__init__()
        self.win = QtGui.QMainWindow()
        self.win.setCentralWidget(self)
        self.win.resize(800, 250)
        self.win.setWindowTitle("Cavity Results Viewer")
        self.grid = QtGui.QGridLayout(self)
        self.win.setWindowFlags(self.win.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)

        self.win.show()

    def reset_indicators(self):
        #return #Uncomment this to skip modifying the CSS (resizing problem seems to go away!!!)
        for plot in self.plots.keys():
            self.plots[plot].setStyleSheet("""
                                border-top: 5px solid rgba(0,0,0,0);
                                border-radius: 12px;
                                """)

    def set_indicator_border_color(self, cavnum, result):
        #return #Uncomment this to skip modifying the CSS (resizing problem seems to go away!!!)
        color = "lime" if result['decision'] else "red"
        self.plots[cavnum].setStyleSheet("""
                        border-top: 5px solid %s;
                        border-radius: 12px;
                        """ % color)

    def create_indicators(self, params):
        for c, cavnum in enumerate(params.keys()):
            r = (4 % (c + 1)) / 4 #every fourth column jump to next row

            box = QtGui.QHBoxLayout()

            plt = pg.PlotWidget()

            plt.setStyleSheet("""
                        border-top: 5px solid yellow;
                        border-radius: 12px;
                    """)
            curve_blue = plt.plotItem.plot(pen=None, symbol='o', symbolPen=None, symbolSize=8, symbolBrush=(100, 100, 255, 80)) #points for showing history data
            curve_green = plt.plotItem.plot(pen=None, symbol='o', symbolPen=None, symbolSize=8,symbolBrush=(100, 255, 100, 80))


            plt.plotItem.setLabel('left', "Amplitude", units='A')
            plt.plotItem.setLabel('bottom', "Frequency", units='Hz')
            self.plots[cavnum] = plt
            self.curves[cavnum] = {"blue": curve_blue,
                                   "green": curve_green}
            box.addWidget(plt)
            self.grid.addLayout(box, r, c % 4)

    def update(self, cavnum, results):
        #update the plots and render all points
        max_history = 1000 #max points per plot to store

        result = results[cavnum]
        if result.has_key('peaks'):
            peaks = result['peaks']

            if peaks.has_key('amps'):
                amps = np.round(peaks['amps'], 2)
                freqs = np.round(peaks['freqs'], 2)

                x_blue, y_blue = self.curves[cavnum]['blue'].getData()
                x_blue = np.append(freqs, x_blue)[:max_history]
                y_blue = np.append(amps, y_blue)[:max_history]

                x_blue = x_blue[x_blue != np.array(None)] #remove any none from the initial getData
                y_blue = y_blue[y_blue != np.array(None)] #remove any none from the initial getData
                self.curves[cavnum]['blue'].setData(x_blue, y_blue)

        self.set_indicator_border_color(cavnum, result)

class MyThread(QtCore.QThread):
    update = QtCore.pyqtSignal(int, object)

    def __init__(self, resutls, parent=None):
        super(MyThread, self).__init__(parent)
        self.results = resutls #number of plots

    def run(self):
        while True:
            for cavnum in range(len(self.results.keys())):
                time.sleep(1)
                peaks = {'amps': np.random.rand(3,1), 'freqs': np.random.rand(3,1)}
                self.results[cavnum]['decision'] = bool(random.getrandbits(1))
                self.results[cavnum]['peaks'] = peaks
                self.update.emit(cavnum, self.results)


# create the dialog for zoom to point
class MainApp(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainApp, self).__init__(parent)
        # Set up the user interface from Designer.

        n = 8
        results = {}
        for x in range(n):
            results[x] = {}

        self.thread = MyThread(results)
        self.thread.update.connect(self.update)

        self.viewer = ResultsViewer()
        self.viewer.create_indicators(results)

        self.thread.start()

    def update(self, cavnum, data):
        self.viewer.update(cavnum, data)

if __name__ == "__main__":
    app = QtGui.QApplication([])
    widget = MainApp()
    widget.move(300, 300)
    widget.show()
sys.exit(app.exec_())

考虑一个更简单的例子,我找到了解决方案。 似乎将样式表设置为
PlotWidget
可以让小部件调整自身大小,从而与
QGridLayout
协商更多或更少的空间。样式不会改变小部件的大小并不重要,即使设置一些完全不相关的内容,如字体或无效样式,也会重现问题。使用普通的
QWidget
而不是
PlotWidget
不会产生此问题

因此,在任何情况下,解决方案都需要告诉
QGridLayout
不要更改其列和行的空间。 这可以使用

其中
stretch
对于所有行/列都需要相同且大于零。请参见下面的示例。相应地调整实际代码应该是相当直截了当的

import sys, time
import numpy as np
import pyqtgraph as pg
from PyQt4 import QtCore, QtGui

class MainApp(QtGui.QMainWindow):

    def __init__(self):
        super(MainApp, self).__init__()

        self.win =  QtGui.QWidget()
        self.setCentralWidget(self.win)
        self.resize(800, 250)
        self.grid = QtGui.QGridLayout()
        self.win.setLayout(self.grid)
        self.colors = ["yellow", "green", "red", "blue"]
        self.n = 8
        self.create_boxes()
        self.thread = MyThread()
        self.thread.update.connect(self.setBoxColor)
        self.thread.start()
        self.show()


    def create_boxes(self):
        self.boxes = []
        for i in range(self.n):
            r = (4 % (i + 1)) / 4 
            box = pg.PlotWidget() 
            #box = QtGui.QWidget() # problem does not appear when using QWidget
            self.boxes.append(box)
            self.setBoxColor(i,0)
            #########
            # The following two lines solve the problem!!!
            # comment them out to see old unwanted behaviour
            self.grid.setColumnStretch(i % 4, 1)
            self.grid.setRowStretch(r, 1)
            #########
            self.grid.addWidget(box, r, i % 4)



    def setBoxColor(self, boxnumber, color):
        stylesheet = """
                        border-top: 5px solid %s;
                        border-radius: 12px;
                        """ % self.colors[color]
        self.boxes[boxnumber].setStyleSheet(stylesheet)


class MyThread(QtCore.QThread):
    update = QtCore.pyqtSignal(int, int)

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

    def run(self):
        time.sleep(1)
        while True:
            boxnumber = np.random.randint(0,8)
            color = np.random.randint(0,4)
            self.update.emit(boxnumber, color)
            time.sleep(0.34)



if __name__ == "__main__":
    app = QtGui.QApplication([])
    widget = MainApp()
    widget.move(300, 300)
    widget.show()
    sys.exit(app.exec_())

你介意提供一份工作吗?否则,这里真的很难说什么。新添加的示例不适合我(在不同的地方,
results
存在多个问题,一旦问题得到解决,
。setData
就会抛出错误)。我修改了代码,使它能够实际运行并复制问题,找到它。我还没有发现问题,但至少在这段代码中,大家可以更容易地查看它。我的示例出现错误,你是对的。对此我很抱歉,我想我对它进行了编辑,忘记了做复制粘贴测试。非常感谢您查看代码并使其更具可读性!我还修复了我对我的示例所做的错误编辑。这无疑解决了我的问题。将来我会记住列和行拉伸方法。非常感谢。