简体   繁体   English

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

[英]How to keep PyQt Grid elements from resizing and maintain even spacing of all widgets?

I am having some trouble keeping the size even across all elements in my gridLayout. 我在gridLayout中的所有元素中保持大小都有些麻烦。

Ocasionally the inner widgets just completely change size after I update the plot data (on both width and height), and especially after maximizing/minimizing or changing the window size: 在更新绘图数据(宽度和高度)之后,内部小部件偶尔会完全改变大小,特别是在最大化/最小化或更改窗口大小之后:

Run #1 运行#1 在此输入图像描述 Run #2 运行#2 在此输入图像描述

What I was hoping to achieve was for the size to be kept evenly distributed regardless of the dimensions of the window: 我希望实现的是,无论窗口的尺寸如何,都要保持均匀分布的大小:

Desired Output: 期望的输出: 在此输入图像描述

This is a summary of what I have written to populate the grid, and how I update the CSS to make the borders change color. 这是我编写的用于填充网格的内容的摘要,以及我如何更新CSS以使边框更改颜色。 I notice that if I do not change the CSS then this issue is not apparent. 我注意到,如果我不更改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)

[UPDATE] [UPDATE]

And this is a fully functional example: 这是一个功能齐全的例子:

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_())

Considering an even more simplified example made me find the solution. 考虑到一个更简化的例子让我找到了解决方案。 It seems that setting the styleSheet to the PlotWidget lets the widget resize itself and thereby negotiate for more or less space with the QGridLayout . 似乎将PlotWidget设置为PlotWidget可以让小部件自行调整大小,从而与QGridLayout协商更多或更少的空间。 It does not matter that the style wouldn't change the size of the Widget, even setting something completely unrelated like font or even invalid styles reproduces the problem. 风格不会改变Widget的大小并不重要,即使设置完全不相关的东西,比如字体甚至无效的样式也会重现问题。 Using a normal QWidget instead of a PlotWidget does not produce this problem. 使用普通的QWidget而不是PlotWidget不会产生这个问题。

In any case the solution therefore needs to be to tell QGridLayout not to change the space for its columns and rows. 因此,在任何情况下,解决方案都需要告诉QGridLayout不要更改其列和行的空间。 This can be done using 这可以使用
QGridLayout.setColumnStretch (self, int column, int stretch) and QGridLayout.setColumnStretch (self, int column, int stretch)
QGridLayout.setRowStretch (self, int row, int stretch)

where stretch needs to be the same for all rows/columns and larger than zero. 所有行/列的stretch需要相同且大于零。 See below for the minimal example. 请参阅下面的最小示例。 It should be rather straight forward to adapt the real code accordingly. 应该相当直接地相应地调整实际代码。

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_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM