由设计师创建的小部件上的 PySide2 绘制 [英] PySide2 paint on widget created by designer
问题描述
此问题与 Win10 和 Python 3.6.6 上的 VS Code 有关.我是 Python 和 PySide2 的新手.
我在 StackOverflow 上阅读了很多关于此的主题,这可能是另一个主题的副本,但我无法绘制我的小部件.
我知道必须以某种方式覆盖小部件对象的paintEvent().大多数示例都在主窗口上进行了一些绘制,但我无法从 ui.file 将其传输到小部件上.
我在我的 .py 文件中创建了两个类,MainForm 和 Drawer.MainForm 包含 UI 的实现,我正在尝试绘制一个小部件(名为小部件").在我的 .ui 文件中有一个小部件和一个图形视图.我正在尝试在小部件上实现绘画.
paintEventTest.py 文件如下所示:
导入系统从 PySide2 导入 QtWidgets从 PySide2 导入 QtGui从 PySide2 导入 QtCore从 PySide2.QtUiTools 导入 QUiLoader从 PySide2.QtWidgets 导入(QApplication、QPushButton、QLineEdit、QTextEdit、QSpinBox、QMainWindow、QDesktopWidget、QTableWidget、QTableWidgetItem、QToolButton、QToolTip)从 PySide2.QtCore 导入 QFile、QObject、Qt类 MainForm(QMainWindow):def __init__(self, ui_file, parent=None):super(MainForm, self).__init__(parent)ui_file = QFile(ui_file)ui_file.open(QFile.ReadOnly)### 从设计器加载 UI 文件###加载器 = QUiLoader()self.ui_window = loader.load(ui_file)ui_file.close()self.ui_window.show()### 这不起作用(显然?)###小部件 = self.ui_window.widget抽屉 = 抽屉()drawer.paintEvent(小部件)类抽屉(QtWidgets.QWidget):defpaintEvent(self, e):'''方法paintEvent()被自动调用QPainter 类完成所有低级绘图在其方法 begin() 和 end() 之间编码'''qp = QtGui.QPainter()qp.begin(self)self.drawGeometry(qp)qp.end()def drawGeometry(self, qp):qp = QtGui.QPainter(self)qp.setPen(QtGui.QPen(Qt.green, 8, Qt.DashLine))qp.drawEllipse(40, 40, 400, 400)如果 __name__ == '__main__':app = QApplication(sys.argv)app.setStyle('融合')form = MainForm('./UI Designer/testUI.ui')sys.exit(app.exec_())
testUI.ui 看起来像这样,它在UI Designer"文件夹中实现:
我用上面的代码得到了这个.我不希望它工作,但真的不知道如何引用特定的小部件进行绘画.
QWidget::paintEngine: 不应再被调用QPainter::begin: 绘制设备返回引擎 == 0,类型:1QWidget::paintEngine: 不应再被调用QPainter::begin: 绘制设备返回引擎 == 0,类型:1QPainter::setPen:画家未激活QPainter::end:画家不活动,中止
我也对使用 graphicsscene 和 graphicsitem 在图形视图上绘制等效代码感兴趣.
正如您所指出的,paintEvent 应该只被覆盖.因此,一种选择是推广小部件,您可以在这些答案中看到几个示例:
按下添加按钮,然后按下升级按钮,生成以下 .ui 文件
另一方面,QUiLoader 只加载 Qt 默认提供的小部件,所以如果你想使用新的小部件,你必须覆盖 createWidget 方法:
main.py
import os导入系统从 PySide2 导入 QtCore、QtGui、QtWidgets、QtUiTools从 mywidget 导入抽屉类 UiLoader(QtUiTools.QUiLoader):def createWidget(self, className, parent=None, name=""):如果 className == "抽屉":小部件 = 抽屉(父)widget.setObjectName(name)返回小部件return super(UiLoader, self).createWidget(className, parent, name)类 MainForm(QtCore.QObject):def __init__(self, ui_file, parent=None):super(MainForm, self).__init__(parent)ui_file = QtCore.QFile(ui_file)ui_file.open(QtCore.QFile.ReadOnly)### 从设计器加载 UI 文件###加载器 = UiLoader()self.ui_window = loader.load(ui_file)ui_file.close()self.ui_window.show()如果 __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)app.setStyle("融合")文件 = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./UI Designer/testUI.ui")表单 = MainForm(文件)sys.exit(app.exec_())
This issue is with VS Code on Win10 and Python 3.6.6. I'm new both to Python and PySide2.
I've read a lot of topics on this here at StackOverflow and possibly this is a duplicate of another topic, but I'm not able to get my widget painted.
I understand that the paintEvent() of the widget object have to be overridden somehow. Most of the examples out there does some painting on the main window but I'm not able to transfer this on widgets from an ui.file.
I've created two classes in my .py-file, MainForm and Drawer. The MainForm contains implementation of UI and I'm trying to get a widget (named "widget") painted. In my .ui-file there's a widget and a graphicsview. I'm trying to implement painting on the widget.
paintEventTest.py file looks like this:
import sys from PySide2 import QtWidgets from PySide2 import QtGui from PySide2 import QtCore from PySide2.QtUiTools import QUiLoader from PySide2.QtWidgets import ( QApplication, QPushButton, QLineEdit, QTextEdit, QSpinBox, QMainWindow, QDesktopWidget, QTableWidget, QTableWidgetItem, QToolButton, QToolTip) from PySide2.QtCore import QFile, QObject, Qt class MainForm(QMainWindow): def __init__(self, ui_file, parent=None): super(MainForm, self).__init__(parent) ui_file = QFile(ui_file) ui_file.open(QFile.ReadOnly) ### Load UI file from Designer ### loader = QUiLoader() self.ui_window = loader.load(ui_file) ui_file.close() self.ui_window.show() ### THIS IS NOT WORKING (OBVIOUSLY?) ### widget = self.ui_window.widget drawer = Drawer() drawer.paintEvent(widget) class Drawer(QtWidgets.QWidget): def paintEvent(self, e): ''' the method paintEvent() is called automatically the QPainter class does all the low-level drawing coded between its methods begin() and end() ''' qp = QtGui.QPainter() qp.begin(self) self.drawGeometry(qp) qp.end() def drawGeometry(self, qp): qp = QtGui.QPainter(self) qp.setPen(QtGui.QPen(Qt.green, 8, Qt.DashLine)) qp.drawEllipse(40, 40, 400, 400) if __name__ == '__main__': app = QApplication(sys.argv) app.setStyle('Fusion') form = MainForm('./UI designer/testUI.ui') sys.exit(app.exec_())
testUI.ui looks like this and it's implemented in a folder "UI designer":
<?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>731</width> <height>633</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QGraphicsView" name="graphicsView"> <property name="minimumSize"> <size> <width>0</width> <height>200</height> </size> </property> </widget> </item> <item> <widget class="QWidget" name="widget" native="true"> <property name="minimumSize"> <size> <width>0</width> <height>250</height> </size> </property> <property name="maximumSize"> <size> <width>16777215</width> <height>300</height> </size> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>731</width> <height>21</height> </rect> </property> </widget> <widget class="QStatusBar" name="statusbar"/> </widget> <resources/> <connections/> </ui>
I'm getting this with code above. I'm not expecting it to work, but have really no idea on how to reference the specific widget to paint on.
QWidget::paintEngine: Should no longer be called QPainter::begin: Paint device returned engine == 0, type: 1 QWidget::paintEngine: Should no longer be called QPainter::begin: Paint device returned engine == 0, type: 1 QPainter::setPen: Painter not active QPainter::end: Painter not active, aborted
I'm also interested in equivalent code painting on the graphicsview with graphicsscene and graphicsitem.
解决方案As you point out, the paintEvent should only overridden. So one option is to promote the widget, you can see several examples in these answers:
- https://stackoverflow.com/a/46616419
- https://stackoverflow.com/a/52131384
- https://stackoverflow.com/a/47273625
- https://stackoverflow.com/a/46671439
You must have the following structure:
├── main.py ├── mywidget.py └── UI designer └── testUI.ui
In the mywidget.py file, implement the class you require:
mywidget.py
from PySide2 import QtCore, QtGui, QtWidgets class Drawer(QtWidgets.QWidget): def paintEvent(self, e): """ the method paintEvent() is called automatically the QPainter class does all the low-level drawing coded between its methods begin() and end() """ qp = QtGui.QPainter() qp.begin(self) self.drawGeometry(qp) qp.end() def drawGeometry(self, qp): qp.setPen(QtGui.QPen(QtCore.Qt.green, 8, QtCore.Qt.DashLine)) qp.drawEllipse(40, 40, 400, 400)
Then you have to open your .ui with Qt Designer, right click on the widget and select promote To... in the contextual menu, then fill in the dialog with the following:
press the add button and then the promote button generating the following .ui file
<?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>731</width> <height>633</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QGraphicsView" name="graphicsView"> <property name="minimumSize"> <size> <width>0</width> <height>200</height> </size> </property> </widget> </item> <item> <widget class="Drawer" name="widget" native="true"> <property name="minimumSize"> <size> <width>0</width> <height>250</height> </size> </property> <property name="maximumSize"> <size> <width>16777215</width> <height>300</height> </size> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>731</width> <height>23</height> </rect> </property> </widget> <widget class="QStatusBar" name="statusbar"/> </widget> <customwidgets> <customwidget> <class>Drawer</class> <extends>QWidget</extends> <header>mywidget</header> <container>1</container> </customwidget> </customwidgets> <resources/> <connections/> </ui>
On the other the QUiLoader only loads the widgets that Qt provides by default so if you want to use new widget you must overwrite the createWidget method:
main.py
import os import sys from PySide2 import QtCore, QtGui, QtWidgets, QtUiTools from mywidget import Drawer class UiLoader(QtUiTools.QUiLoader): def createWidget(self, className, parent=None, name=""): if className == "Drawer": widget = Drawer(parent) widget.setObjectName(name) return widget return super(UiLoader, self).createWidget(className, parent, name) class MainForm(QtCore.QObject): def __init__(self, ui_file, parent=None): super(MainForm, self).__init__(parent) ui_file = QtCore.QFile(ui_file) ui_file.open(QtCore.QFile.ReadOnly) ### Load UI file from Designer ### loader = UiLoader() self.ui_window = loader.load(ui_file) ui_file.close() self.ui_window.show() if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) app.setStyle("Fusion") file = os.path.join( os.path.dirname(os.path.realpath(__file__)), "./UI designer/testUI.ui" ) form = MainForm(file) sys.exit(app.exec_())
这篇关于由设计师创建的小部件上的 PySide2 绘制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!