首页 > 解决方案 > 在 Qt <= 5.12 中立即清除 `QGraphicsView`

问题描述

下面给出的代码来自另一个关于 SO 的问题。它显示 a QMainWindowwith 4QGraphicsView以在其中使用鼠标进行绘制,并显示 aQPushButton以清除 4 QGraphicsView

课堂上有一个clear()方法QPainterPath,但它是在 Qt 5.13中引入的,我正在使用 Qt 5.12。所以我编写了自己的方法来清除视图及其路径。

绘图工作正常,单击Clear按钮不会导致错误,但视图只会在下次绘图时被清除,而不是立即清除。

立即清除视图的更好解决方案是什么?


主文件

import sys

from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPathItem
from PyQt5.QtGui import QPainterPath, QPen
from PyQt5.QtCore import Qt
from PyQt5.uic import loadUi

# Based on code from https://stackoverflow.com/a/44248794/7481773


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi("mainwindow.ui", self)

        self.layouts = (self.verticalLayout_top_left, self.verticalLayout_top_right,
                        self.verticalLayout_bottom_left, self.verticalLayout_bottom_right)

        self._views = []

        for layout in self.layouts:
            graphics_view = GraphicsView()
            self._views.append(graphics_view)
            layout.addWidget(graphics_view)

        self.clear_button.clicked.connect(self.clear_views)

    def clear_views(self):
        for view in self._views:
            view.clear_view()


class GraphicsView(QGraphicsView):
    def __init__(self):
        super().__init__()
        self.start = None
        self.end = None

        self.setScene(QGraphicsScene())
        self.path = QPainterPath()
        self.item = GraphicsPathItem()
        self.scene().addItem(self.item)

        self.contents_rect = self.contentsRect()
        self.setSceneRect(0, 0, self.contents_rect.width(), self.contents_rect.height())
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def clear_view(self):
        # self.path.clear()  # Qt 5.13
        self.path = QPainterPath()
        self.scene().update()

    def mousePressEvent(self, event):
        self.start = self.mapToScene(event.pos())
        self.path.moveTo(self.start)

    def mouseMoveEvent(self, event):
        self.end = self.mapToScene(event.pos())
        self.path.lineTo(self.end)
        self.start = self.end
        self.item.setPath(self.path)


class GraphicsPathItem(QGraphicsPathItem):
    def __init__(self):
        super().__init__()
        pen = QPen()
        pen.setColor(Qt.black)
        pen.setWidth(5)
        self.setPen(pen)


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    app.exec_()
    del main_window, app


if __name__ == "__main__":
    main()

主窗口.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Paint and Clear</string>
  </property>
  <property name="locale">
   <locale language="English" country="UnitedKingdom"/>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="verticalLayout_top_left"/>
    </item>
    <item row="0" column="1">
     <layout class="QVBoxLayout" name="verticalLayout_top_right"/>
    </item>
    <item row="2" column="0">
     <layout class="QVBoxLayout" name="verticalLayout_bottom_left"/>
    </item>
    <item row="2" column="1">
     <layout class="QVBoxLayout" name="verticalLayout_bottom_right"/>
    </item>
    <item row="1" column="0" colspan="2">
     <widget class="QPushButton" name="clear_button">
      <property name="text">
       <string>Clear</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

标签: pythonpyqt5

解决方案


即使使用该clear()方法并不意味着视图已被清除,但您必须在项目中设置路径。正题,等价的方法是新建一个QPainterPath,另外不需要调用update()场景的方法。使用clear()或设置空QPainterPath在 python 端是等效的,但在 C++ 端它会导致优化应用程序的相同内存被重用。

def clear_view(self):
    self.path = QPainterPath()
    self.item.setPath(self.path)

如果你想同时兼容这两个版本,你可以使用 try-except:

def clear_view(self):
    try:
        self.path.clear()
    except AttributeError as e:
        self.path = QPainterPath()
    self.item.setPath(self.path)

推荐阅读