问题
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.
回答1:
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_())
来源:https://stackoverflow.com/questions/56491818/pyside2-paint-on-widget-created-by-designer