PySide2 在设计师创建的小部件上绘制
PySide2 paint on widget created by designer
此问题与 Win10 和 Python 3.6.6 上的 VS Code 有关。我是 Python 和 PySide2 的新手。
我在 Whosebug 上阅读了很多关于此的主题,这可能与另一个主题重复,但我无法绘制我的小部件。
我知道必须以某种方式覆盖小部件对象的 paintEvent()。大多数示例都在主要 window 上进行了一些绘制,但我无法将其从 ui.file.
转移到小部件上
我在我的 .py 文件中创建了两个 类,MainForm 和 Drawer。 MainForm 包含 UI 的实现,我正在尝试绘制一个小部件(名为 "widget")。在我的 .ui 文件中有一个小部件和一个图形视图。我正在尝试在小部件上实现绘画。
paintEventTest.py 文件如下所示:
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 看起来像这样,它在文件夹 "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>
我用上面的代码得到了这个。我不期望它能工作,但真的不知道如何引用要在其上绘制的特定小部件。
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
我也对 equigraphicscene 和 graphicsitem 上的 graphicsview 上的有效代码绘画感兴趣。
正如您所指出的,paintEvent 应该只被覆盖。因此,一种选择是推广小部件,您可以在这些答案中看到几个示例:
您必须具有以下结构:
├── main.py
├── mywidget.py
└── UI designer
└── testUI.ui
在 mywidget.py 文件中,实现 class 你要求 uire:
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)
然后您必须使用 Qt Designer 打开您的 .ui,右键单击小部件并 select 在上下文菜单中提升至...,然后在对话框中填写以下内容:
按添加按钮,然后按推广按钮生成以下 .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>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>
另一方面,QUiLoader 仅加载 Qt 默认提供的小部件,因此如果您想使用新的小部件,则必须覆盖 createWidget 方法:
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_())
此问题与 Win10 和 Python 3.6.6 上的 VS Code 有关。我是 Python 和 PySide2 的新手。
我在 Whosebug 上阅读了很多关于此的主题,这可能与另一个主题重复,但我无法绘制我的小部件。
我知道必须以某种方式覆盖小部件对象的 paintEvent()。大多数示例都在主要 window 上进行了一些绘制,但我无法将其从 ui.file.
转移到小部件上我在我的 .py 文件中创建了两个 类,MainForm 和 Drawer。 MainForm 包含 UI 的实现,我正在尝试绘制一个小部件(名为 "widget")。在我的 .ui 文件中有一个小部件和一个图形视图。我正在尝试在小部件上实现绘画。
paintEventTest.py 文件如下所示:
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 看起来像这样,它在文件夹 "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>
我用上面的代码得到了这个。我不期望它能工作,但真的不知道如何引用要在其上绘制的特定小部件。
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
我也对 equigraphicscene 和 graphicsitem 上的 graphicsview 上的有效代码绘画感兴趣。
正如您所指出的,paintEvent 应该只被覆盖。因此,一种选择是推广小部件,您可以在这些答案中看到几个示例:
您必须具有以下结构:
├── main.py
├── mywidget.py
└── UI designer
└── testUI.ui
在 mywidget.py 文件中,实现 class 你要求 uire:
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)
然后您必须使用 Qt Designer 打开您的 .ui,右键单击小部件并 select 在上下文菜单中提升至...,然后在对话框中填写以下内容:
按添加按钮,然后按推广按钮生成以下 .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>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>
另一方面,QUiLoader 仅加载 Qt 默认提供的小部件,因此如果您想使用新的小部件,则必须覆盖 createWidget 方法:
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_())