QWidget 没有显示

Posted

技术标签:

【中文标题】QWidget 没有显示【英文标题】:QtWidget is not showing 【发布时间】:2022-01-19 00:31:00 【问题描述】:

我正在尝试使用 PyQt5 显示两个小部件,第一个是 sin、cos 和 tan 函数的图。我正在使用pyqtgraph 并使用在question 的答案中找到的代码。我还使用了另一个使用 PyOpenGL 绘制立方体的小部件,以 link 中的示例为例。我试图在一个主窗口小部件中显示这两个小部件。我的方法如下

    获取主小部件。 在主窗口小部件中,使用 QVBoxLayout() 在 QVBoxLayout 中,在上面提到的两个小部件处

但是当我运行代码时,只显示使用pyqtgraph 的绘图,而不显示使用PyOpenGL 绘制的立方体。经过一点调试,我发现立方体小部件的高度默认设置为 0。我不确定为什么会发生这种情况。我试着打电话给glWidget.resize(640,480)。但它没有用。我是使用 PyQt 和 PyOpenGL 的新手。我想我遗漏了一些细节,如果我的假设是正确的,这些细节将使glWidget 的高度大于 0。另外我不确定这是否真的可以做到。我现在的代码如下,有点乱。

import sys
from OpenGL.GL.images import asWrapper

from PyQt5.QtWidgets import QApplication, QGridLayout
from PyQt5 import QtWidgets
import pyqtgraph as pg
from OpenGL.GL import *
from OpenGL.GLU import *
from PyQt5 import QtGui
from PyQt5.QtOpenGL import *
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
from PyQt5 import QtOpenGL
import OpenGL.GL as gl
from OpenGL import GLU  
from OpenGL.arrays import vbo

class TimeLine(QtCore.QObject):
    frameChanged = QtCore.pyqtSignal(int)

    def __init__(self, interval=60, loopCount=1, parent=None):
        super(TimeLine, self).__init__(parent)
        self._startFrame = 0
        self._endFrame = 0
        self._loopCount = loopCount
        self._timer = QtCore.QTimer(self, timeout=self.on_timeout)
        self._counter = 0
        self._loop_counter = 0
        self.setInterval(interval)

    def on_timeout(self):
        if self._startFrame <= self._counter < self._endFrame:
            self.frameChanged.emit(self._counter)
            self._counter += 1
        else:
            self._counter = 0
            self._loop_counter += 1
        
        if self._loopCount > 0:
            if self._loop_counter >= self.loopCount():
                self._timer.stop()
    
    def setLoopCount(self, loopCount):
        self._loopCount = loopCount
    
    def loopCount(self):
        return self._loopCounts
    interval = QtCore.pyqtProperty(int, fget=loopCount, fset=setLoopCount)

    def setInterval(self, interval):
        self._timer.setInterval(interval)
    
    def interval(self):
        return self._timer.interval()

    interval = QtCore.pyqtProperty(int, fget=interval, fset=setInterval)

    def setFrameRange(self, startFrame, endFrame):
        self._startFrame = startFrame
        self._endFrame = endFrame

    @QtCore.pyqtSlot()
    def start(self):
        self._counter = 0
        self._loop_counter = 0
        self._timer.start()

class GLWidget(QtOpenGL.QGLWidget):
    def __init__(self, parent = None):
        self.parent = parent
        QtOpenGL.QGLWidget.__init__(self, parent)
        self.resizeGL(640,800)
    
    def initializeGL(self):
        self.qglClearColor(QtGui.QColor(0,0,255))
        gl.glEnable(gl.GL_DEPTH_TEST)

        self.initGeometry()

        self.rotX = 0.0
        self.rotY = 0.0
        self.rotZ = 0.0

    def resizeGL(self, width, height):
        gl.glViewport(0, 0, width, height)
        gl.glMatrixMode(gl.GL_PROJECTION)
        gl.glLoadIdentity()
        print(width, height)
        aspect = width / float(height)

        GLU.gluPerspective(45.0, aspect, 1.0, 100.0)
        gl.glMatrixMode(gl.GL_MODELVIEW)

    def paintGL(self):
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glPushMatrix()

        gl.glTranslate(0.0, 0.0, -50.0)
        gl.glScale(20.0, 20.0, 20.0)
        gl.glRotate(self.rotX, 1.0, 0.0, 0.0)
        gl.glRotate(self.rotY, 0.0, 1.0, 0.0)
        gl.glRotate(self.rotZ, 0.0, 0.0, 1.0)
        gl.glTranslate(-0.5, -0.5, -0.5)

        gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
        gl.glEnableClientState(gl.GL_COLOR_ARRAY)

        gl.glVertexPointer(3, gl.GL_FLOAT, 0, self.vertVBO)
        gl.glColorPointer(3, gl.GL_FLOAT, 0, self.colorVBO)

        gl.glDrawElements(gl.GL_QUADS, len(self.cubeIdxArray), gl.GL_UNSIGNED_INT, self.cubeIdxArray)

        gl.glDisableClientState(gl.GL_VERTEX_ARRAY)
        gl.glDisableClientState(gl.GL_COLOR_ARRAY)

        gl.glPopMatrix()

    def initGeometry(self):
        self.cubeVtxArray = np.array(
                [[0.0, 0.0, 0.0],
                 [1.0, 0.0, 0.0],
                 [1.0, 1.0, 0.0],
                 [0.0, 1.0, 0.0],
                 [0.0, 0.0, 1.0],
                 [1.0, 0.0, 1.0],
                 [1.0, 1.0, 1.0],
                 [0.0, 1.0, 1.0]])
        self.vertVBO = vbo.VBO(np.reshape(self.cubeVtxArray,
                                          (1, -1)).astype(np.float32))
        self.vertVBO.bind()
        
        self.cubeClrArray = np.array(
                [[0.0, 0.0, 0.0],
                 [1.0, 0.0, 0.0],
                 [1.0, 1.0, 0.0],
                 [0.0, 1.0, 0.0],
                 [0.0, 0.0, 1.0],
                 [1.0, 0.0, 1.0],
                 [1.0, 1.0, 1.0],
                 [0.0, 1.0, 1.0 ]])
        self.colorVBO = vbo.VBO(np.reshape(self.cubeClrArray,
                                           (1, -1)).astype(np.float32))
        self.colorVBO.bind()

        self.cubeIdxArray = np.array(
                [0, 1, 2, 3,
                 3, 2, 6, 7,
                 1, 0, 4, 5,
                 2, 1, 5, 6,
                 0, 3, 7, 4,
                 7, 6, 5, 4 ])

    def setRotX(self, val):
        self.rotX = np.pi * val

    def setRotY(self, val):
        self.rotY = np.pi * val

    def setRotZ(self, val):
        self.rotZ = np.pi * val
    

class MainGui(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.resize(600,600)
        self.cube = GLWidget(self)
        self.setupUI()

    def setupUI(self):
        central_widget = QtWidgets.QWidget()
        central_layout = QtWidgets.QVBoxLayout()
        central_widget.setLayout(central_layout)

        self.setCentralWidget(central_widget)
        
        pg.setConfigOption('background',0.95)
        pg.setConfigOptions(antialias=True)
        
        self.plot = pg.PlotWidget()
        self.plot.setAspectLocked(lock = True, ratio = 0.01)
        #self.cube = GLWidget(self)
        #self.cube.resize(200,200)
        
        central_layout.addWidget(self.cube)
        central_layout.addWidget(self.plot)

        self._plots = [self.plot.plot([], [], pen=pg.mkPen(color=color, width=2)) for color in ('g', 'r', 'y')]
        self._timeline = TimeLine(loopCount = 0, interval = 10)
        self._timeline.setFrameRange(0,720)
        self._timeline.frameChanged.connect(self.generate_data)
        self._timeline.start()
    def plot_data(self, data):
        for plt, val in zip(self._plots, data):
            plt.setData(range(len(val)),val)

    @QtCore.pyqtSlot(int)
    def generate_data(self, i):
        ang = np.arange(i, i + 720)
        cos_func = np.cos(np.radians(ang))
        sin_func = np.sin(np.radians(ang))
        tan_func = sin_func/cos_func
        tan_func[(tan_func < -3) | (tan_func > 3)] = np.NaN
        self.plot_data([sin_func, cos_func, tan_func])


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    gui = MainGui()
    gui.show()
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

似乎 QGLWidget(顺便说一句,它已被弃用,而应该使用 QOpenGLWidget)没有实现 sizeHint(),因此它返回了无效的大小(QSize(-1, -1)),这意味着小部件可以调整为 0 宽度和/或高度。

由于 plot 小部件具有扩展尺寸策略(并动态重新实现 sizeHint()),结果是 gl 小部件完全隐藏,高度为 0。

一种可能的解决方案是将带有适当stretch 参数的小部件添加到布局中。

如果您希望两个小部件具有相同的高度,您可以执行以下操作:

    central_layout.addWidget(self.cube, stretch=1)
    central_layout.addWidget(self.plot, stretch=1)

请注意,拉伸是基于比率的(仅考虑整数),因此,如果您希望立方体具有绘图高度的一半:

    central_layout.addWidget(self.cube, stretch=1)
    central_layout.addWidget(self.plot, stretch=2)

或者,您可以将setMinimumHeight() 用于 gl 小部件,但由于绘图具有扩展策略(通常优先),因此 gl 小部件将始终具有该高度。更好的解决方案是为 gl 小部件设置扩展策略实现 QSizeHint,但请记住绘图小部件具有动态大小提示,因此它总是需要一些“大小优先级” ”。

class GLWidget(QtOpenGL.QGLWidget):
    def __init__(self, parent = None):
        QtOpenGL.QGLWidget.__init__(self, parent)
        self.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)

    def sizeHint(self):
        return QtCore.QSize(300, 150)

    # ...

__init__中应该不需要手动调用resizeGL(),还应该一直使用动态访问parent()函数来获取父级。

【讨论】:

我使用了拉伸值应用了第一个解决方案,并且成功了!

以上是关于QWidget 没有显示的主要内容,如果未能解决你的问题,请参考以下文章

QWidget 没有显示

QWidget 不显示 QLabel

有没有办法在禁用的 QWidget 上显示工具提示

PyQt QWidget的绝对位置

在父 QWidget 区域之外显示子 QWidget

PyQt - QWidget - 仅显示标题栏