调整大小后,paint方法无法绘制整个小部件

时间:2019-09-16 21:07:36

标签: python pyqt pyqt5

我有一个在Qt Designer中内置的PyQt窗口,并且正在编写自定义绘制方法。主窗口创建一个标签并将其设置为中央窗口小部件。然后,我重写了paint方法以绘制一个简单的柱形图。

在调整大小之前,小部件将正常工作。该小部件调用resize方法并按预期方式重新绘制,但是它使用与调整大小之前相同的大小矩形。有一个很大的黑色区域-调整大小的部分-没有被涂上油漆。

enter image description here

为了测试这一点,我抓住了小部件的矩形并绘制了一个浅蓝色填充和外部红线的大矩形。调整窗口大小时,外部矩形的一部分也会丢失。

调试语句表明新矩形的大小正确,并且width和height值正确输入到paint事件中。

但是当我调整大小时,这就是我看到的。为什么在黑色区域没有油漆?我已经检查了我的代码,并且油漆上没有硬编码限制。有隐藏的剪辑发生吗?

我完全找不到与此问题有关的任何问题,因此似乎我缺少了一些东西。这个类似的问题说在重新绘制之前使窗口无效,但这是针对C ++的: Graphics.DrawImage Doesn't Always Paint The Whole Bitmap?

我是否需要以某种方式使小部件无效?我找不到PyQt方法来做到这一点。

import sys
from PyQt5 import QtCore, QtGui, QtWidgets, uic
import PyQt5 as qt

import numpy as np

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.label = QtWidgets.QLabel()
        self.max_x = 600
        self.max_y = 400
        canvas = QtGui.QPixmap(self.max_x, self.max_y)
        self.label.setPixmap(canvas)
        self.setCentralWidget(self.label)

        np.random.seed(777)
        self.x_time = np.linspace(0, 12.56, 3000)
        rand_data = np.random.uniform(0.0, 1.0, 3000)
        self.data = np.sin(self.x_time) + rand_data

        pal = self.palette()
        pal.setColor(self.backgroundRole(), QtGui.QColor('black'))
        self.setPalette(pal)
        self.setAutoFillBackground(True)

    def resizeEvent(self, a0: QtGui.QResizeEvent):
        print("resizeEvent")
        max_x = self.size().width()
        max_y = self.size().height()
        self.draw(max_x, max_y)

    def mousePressEvent(self, a0: QtGui.QMouseEvent):
        print("mousePressEvent")

    def paintEvent(self, a0: QtGui.QPaintEvent):
        print("New window size = ", self.size())
        print("canvas size = ", self.label.size())
        max_x = self.label.size().width()
        max_y = self.label.size().height()
        self.draw(max_x, max_y)

    def draw(self, max_x, max_y):
        x_final = self.x_time[-1]

        data = self.data/np.max(np.abs(self.data))
        data = [abs(int(k*max_y)) for k in self.data]
        x_pos_all = [int(self.x_time[i]*max_x / x_final) for i in range(len(data))]

        # Find and use only the max y value for each x pixel location
        y_pos = []
        x_pos = list(range(max_x))
        cnt = 0
        for x_pixel in range(max_x):
            mx = 0.0
            v = x_pos_all[cnt]
            while cnt < len(x_pos_all) and x_pos_all[cnt] == x_pixel:
                if data[cnt] > mx:
                    mx = data[cnt]
                cnt += 1
            y_pos.append(mx)

        print("data = ")
        dat = np.array(data)
        print(dat[dat > 0].shape[0])

        painter = QtGui.QPainter(self.label.pixmap()) # takes care of painter.begin(self)

        pen = QtGui.QPen()

        rect = self.label.rect()
        print("rect = {}".format(rect))
        painter.fillRect(rect, QtGui.QColor('lightblue'))
        pen.setWidth(2)
        pen.setColor(QtGui.QColor('green'))

        for i in range(len(x_pos)):
            painter.setPen(pen)
            painter.drawLine(x_pos[i], max_y, x_pos[i], max_y - y_pos[i])

        pen.setWidth(5)
        pen.setColor(QtGui.QColor('red'))
        painter.setPen(pen)
        painter.drawRect(rect)

        painter.end()

app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

我希望随着窗口小部件的大小调整,paint事件将重新绘制窗口小部件的整个新大小,而不仅仅是原始大小。奇怪的是,当我调整大小时,图的部分(绿线)看起来像是在缩放,但是一切都被截断为原始窗口小部件的大小。

我该如何解决?

1 个答案:

答案 0 :(得分:2)

如果您使用的是QLabel,则不必重写paintEvent,因为它足以创建新的QPixmap并将其设置在QLabel中。

import sys
import numpy as np

from PyQt5 import QtCore, QtGui, QtWidgets


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.label = QtWidgets.QLabel()
        self.setCentralWidget(self.label)

        np.random.seed(777)
        self.x_time = np.linspace(0, 12.56, 3000)
        rand_data = np.random.uniform(0.0, 1.0, 3000)
        self.data = np.sin(self.x_time) + rand_data

        pal = self.palette()
        pal.setColor(self.backgroundRole(), QtGui.QColor("black"))
        self.setPalette(pal)
        self.setAutoFillBackground(True)

    def resizeEvent(self, a0: QtGui.QResizeEvent):
        self.draw()
        super().resizeEvent(a0)

    def draw(self):
        max_x, max_y = self.label.width(), self.label.height()
        x_final = self.x_time[-1]
        data = self.data / np.max(np.abs(self.data))
        data = [abs(int(k * max_y)) for k in self.data]
        x_pos_all = [int(self.x_time[i] * max_x / x_final) for i in range(len(data))]

        y_pos = []
        x_pos = list(range(max_x))
        cnt = 0
        for x_pixel in range(max_x):
            mx = 0.0
            v = x_pos_all[cnt]
            while cnt < len(x_pos_all) and x_pos_all[cnt] == x_pixel:
                if data[cnt] > mx:
                    mx = data[cnt]
                cnt += 1
            y_pos.append(mx)

        print("data = ")
        dat = np.array(data)
        print(dat[dat > 0].shape[0])

        pixmap = QtGui.QPixmap(self.size())
        painter = QtGui.QPainter(pixmap)

        pen = QtGui.QPen()
        rect = self.label.rect()
        print("rect = {}".format(rect))
        painter.fillRect(rect, QtGui.QColor("lightblue"))
        pen.setWidth(2)
        pen.setColor(QtGui.QColor("green"))

        painter.setPen(pen)
        for x, y in zip(x_pos, y_pos):
            painter.drawLine(x, max_y, x, max_y - y)

        pen.setWidth(5)
        pen.setColor(QtGui.QColor("red"))
        painter.setPen(pen)
        painter.drawRect(rect)
        painter.end()
        self.label.setPixmap(pixmap)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

更新

  1. 为什么放大后我不能缩小窗口? QMainWindow的布局将QMainWindow的最小大小作为centralWidget的minimumSizeHint的参考,在您的情况下,QLabel会将QPixmap的大小作为minimumSizeHint。如果您希望减小尺寸,则必须重写该方法:
class Label(QtWidgets.QLabel):
    def minimumSizeHint(self):
        return QtCore.QSize(1, 1)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.label = Label()
        self.setCentralWidget(self.label)
        # ...
  1. 为什么整个区域以前都没有被涂漆?因为您正在绘制QPixmap的副本:painter = QtGui.QPainter(self.label.pixmap()),而不是QLabel的已存储QPixmap,所以没有任何修改。