1

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

在调整大小之前,该小部件运行良好。小部件调用 resize 方法并按预期重新绘制,但它使用与调整大小之前相同大小的矩形。有一个很大的黑色区域——调整大小的部分——没有被涂上。

在此处输入图像描述

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

调试语句显示新矩形的大小正确,并且宽度和高度值已正确输入到绘制事件中。

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

我找不到任何关于这个问题的问题,所以我似乎遗漏了一些东西。这个类似的问题说在重绘之前使窗口无效,但这是针对 C++ 的: Graphics.DrawImage 并不总是绘制整个位图?

我是否需要以某种方式使小部件无效?我找不到 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 事件将在小部件的整个新大小上重新绘制,而不仅仅是原始大小。奇怪的是,图形部分(绿线)在我调整大小时看起来像是在缩放,但一切都只是在原始小部件大小处被截断。

我该如何解决?

4

1 回答 1

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,所以没有任何内容被修改。
于 2019-09-17T02:50:15.517 回答