这篇文章主要为大家详细介绍了python3 PyQt5图形项的自定义和交互,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文通过Python3 PyQt5实现《python Qt Gui 快速编程》 这本书的页面设计者应用程序,采用QGraphicsView,QGraphicsScene,QGraphicsItem,这个程序包含有多个文本,图片和框的页面。有些图形类在PyQt5已过时,所以本代码改动幅度比较大。主要的类或方法的改变如下:
QMatrix==QTransform
setMatrix==setTransform
rotate==setRotation
本例中,由于event.delta()已过时,还重写了车轮事件方法:
定义车轮事件(自身,事件):
#因子=1.41 * *(-事件增量()/240.0)
# factor=1.41 * *(-ABS(事件。startx()-事件。y())/240.0)
factor=event.angleDelta().y()/120.0
if event.angleDelta().y()/120.0 0:
因子=2
否则:
因子=0.5
自我尺度(因子,因子)
为了保持代码可读行,增加了一个类:
类GraphicsPixmapItem(QGraphicsPixmapItem):#由杨荣东添加
def __init__(self,pixmap):
super(QGraphicsPixmapItem,self).__init__(点阵图)
本例中还有包含菜单的按钮:
if text=='Align ':
menu=QMenu(自身)
对于文本,在(
('左对齐,第四季度.AlignLeft),
('右对齐,第四季度.AlignRight),
('顶部对齐,第四季度.AlignTop),
('底部对齐,第四季度.AlignBottom)):
包装器=func工具。部分(自我。设置对齐,arg)
自我包装.追加(包装)
menu.addAction(文本、包装)
button.setMenu(菜单)
本例中还针对qstyleoptiongraphicsitem。详细程度已过时,改写如下:
选项。levelofdetailfromtransform(self。转换())
下面为完整的代码:
#!/usr/bin/env python3
导入功能工具
随机导入
导入系统
来自PyQt5 .QtCore导入(QByteArray,QDataStream,QFile,QFileInfo,
QIODevice,QPoint,QPointF,QRectF,Qt)
来自PyQt5 .QtWidgets导入(QApplication,QDialog,
QDialogButtonBox,QFileDialog,QFontComboBox,
QGraphicsItem,QGraphicsPixmapItem,
QGraphicsScene,QGraphicsTextItem,QGraphicsView,QGridLayout,
QHBoxLayout,QLabel,QMenu,QMessageBox,QPushButton,QSpinBox,
QStyle、QTextEdit、QVBoxLayout)
来自PyQt5 .QtGui导入QFont、QCursor、QFontMetrics、QTransform、QPainter、QPen、QPixmap
来自PyQt5 .QtPrintSupport导入QPrinter,QPrintDialog
MAC=真
尝试:
来自PyQt5 .QtGui导入qt_mac_set_native_menubar
除了导入错误:
MAC=假
#PageSize=(595,842) # A4磅
PageSize=(612,792) #美国磅字母
点数=10
MagicNumber=0x70616765
文件版本=1
脏=假
类TextItemDlg(QDialog):
def __init__(自身,项目=无,位置=无,场景=无,父项=无):
超级(QDialog,self).__init__(父级)
self.item=项目
自我定位=位置
self.scene=场景
self.editor=QTextEdit()
自我。编辑。setacceptrichtext(False)
自我。编辑。setabchangesfocus(True)
editorLabel=QLabel('Text:')
编辑器标签。设置好友(自我。编辑)
自我。font combobox=qfont combobox()
自我。字体组合框。setcurrentfont(q font(' Times ',PointSize))
fontLabel=QLabel('Font:')
字体标签。设置好友(自我。字体组合框)
self.fontSpinBox=QSpinBox()
自我。字体微调框。设置校准(Qt .对齐|Qt .AlignVCenter)
self.fontSpinBox.setRange(6,280)
自我。字体微调框。setvalue(点数)
fontSizeLabel=QLabel('Size:')
字体大小标签。设置好友(自我。fontspinbox)
自我。button box=QDialogButtonBox(QDialogButtonBox .好的|
QDialogButtonBox .取消)
自我。按钮盒。按钮(QDialogButtonBox .好的)。setEnabled(False)
如果自我项目不为无:
自我。编辑。设置laintext(self。项目。toplaintext())
自我。字体组合框。setcurrentfont(self。项目。字体())
自我。字体微调框。setvalue(自身。项目。字体().pointSize())
layout=QGridLayout()
layout.addWidget(editorLabel,0,0)
layout.addWidget(self.editor,1,0,1,6)
layout.addWidget(fontLabel,2,0)
布局。添加小部件(自我。字体组合框,2,1,1,2)
layout.addWidget(fontSizeLabel,2,3)
布局。添加小部件(自我。fontspinbox,2,4,1,2)
布局。添加小部件(自我。按钮盒,3,0,1,6)
self.setLayout(布局)
自我。字体组合框。currentfontchanged。连接(自我。更新用户界面)
自我。fontspinbox。值已更改。连接(自我。更新用户界面)
自我。编辑。文本已更改。连接(自我。更新用户界面)
自我。按钮盒。接受。连接(自我。接受)
自我。按钮盒。被拒。连接
self.setWindowTitle('页面设计器- {0}文本项')。格式(
"如果自我项目不是其他项目,则添加"编辑"))
self.updateUi()
def updateUi(自身):
font=self。字体组合框。当前字体()
字体。setpointsize(self。fontspinbox。value())
self.editor.document().setDefaultFont(字体)
自我。按钮盒。按钮(QDialogButtonBox .好的)。setEnabled(
bool(self.editor.toPlainText()))
定义接受(自身):
如果自我项目为无:
self.item=TextItem(' ',self.position,self.scene)
font=self。字体组合框。当前字体()
字体。setpointsize(self。fontspinbox。value())
self.item.setFont(font)
自我。项目。设置laintext(self。编辑。toplaintext())
self.item.update()
全局脏
脏=真
QDialog.accept(自我)
类TextItem(QGraphicsTextItem):
def __init__(自身,文本,位置,场景,
font=QFont('Times ',PointSize),matrix=QTransform()):
超级(TextItem,self).__init__(文本)
self.setFlags(QGraphicsItem .ItemIsSelectable|
QGraphicsItem .ItemIsMovable)
self.setFont(字体)
self.setPos(位置)
self.setTransform(矩阵)
场景. clearSelection()
scene.addItem(self)
self.setSelected(真)
全局脏
脏=真
def parentWidget(self):
返回self.scene().视图()[0]
定义项目变更(自身、变更、变体):
如果换!=QGraphicsItem .ItemSelectedChange:
全局脏
脏=真
返回qgraphicstextitem。项目变更(自身、变更、变体)
def mouseDoubleClickEvent(self,Event):
dialog=TextItemDlg(self,self.parentWidget())
dialog.exec_()
类GraphicsPixmapItem(QGraphicsPixmapItem):#由杨荣东添加
def __init__(self,pixmap):
super(QGraphicsPixmapItem,self).__init__(点阵图)
类BoxItem(QGraphicsItem):
def __init__(self,position,scene,style=Qt .实线,
rect=None,matrix=QTransform()):
超级(BoxItem,self).__init__()
self.setFlags(QGraphicsItem .ItemIsSelectable|
QGraphicsItem .ItemIsMovable|
QGraphicsItem .ItemIsFocusable)
如果矩形为无:
rect=QRectF(-10 * PointSize,-PointSize,20 * PointSize,
2 *磅值)
self.rect=rect
自我风格=风格
self.setPos(位置)
self.setTransform(矩阵)
scene.clearSelection()
scene.addItem(self)
self.setSelected(真)
self.setFocus()
全局脏
脏=真
def parentWidget(self):
返回self.scene().视图()[0]
def boundingRect(self):
返回自校正(-2,-2,2,2)
定义绘画(自身、绘画、选项、小部件):
pen=QPen(self.style)
钢笔颜色(黑色)
pen.setWidth(1)
if option.state QStyle .状态_选定:
钢笔设置颜色(蓝色)
painter.setPen(钢笔)
painter.drawRect(self.rect)
定义项目变更(自身、变更、变体):
如果换!=QGraphicsItem .ItemSelectedChange:
全局脏
脏=真
返回QGraphicsItem.itemChange(自身、变更、变体)
def contextMenuEvent(self,Event):
wrapped=[]
menu=QMenu(self.parentWidget())
对于文本,参数输入(
('固,第四季度.实线),
('虚线,第四季度.仪表板线),
('点'的,第四季度.点线),
('虚线点,第四季度.虚线),
(' DashDotDotted ',Qt .DashDotDotLine)):
包装器=func工具。部分(自我。设置样式,参数)
wrapped.append(包装)
menu.addAction(文本,包装)
menu.exec_(event.screenPos())
def setStyle(self,Style):
自我风格=风格
self.update()
全局脏
脏=真
def keyPressEvent(self,Event):
因子=点数/4
已更改=假
if event.modifiers() Qt .变化修饰符:
if event.key()==Qt .向左键:
自我。直肠。设置正确(自我。直肠。右()-因子)
已更改=真
elif event.key()==Qt .右键:
自我。直肠。设置正确(自我。直肠。右()因子)
已更改=真
elif event.key()==Qt .向上键:
自我。直肠。设置底部(自我。直肠。底部()-因子)
已更改=真
elif event.key()==Qt .向下键:
自我。直肠。设置底部(自我。直肠。底部()因子)
已更改=真
如果更改:
self.update()
全局脏
脏=真
否则:
qgraphicsitem。按键事件(自身,事件)
类图形视图(QGraphicsView):
def __init__(self,parent=None):
超级(图形视图,自我).__init__(父级)
self.setDragMode(QGraphicsView .橡皮带拖动)
self.setRenderHint(QPainter .抗锯齿)
self.setRenderHint(QPainter .文本抗锯齿)
定义车轮事件(自身,事件):
#因子=1.41 * *(-事件增量()/240.0)
factor=event.angleDelta().y()/120.0
if event.angleDelta().y()/120.0 0:
因子=2
否则:
因子=0.5
自我尺度(因子,因子)
类主窗体(QDialog):
def __init__(self,parent=None):
超级(主窗体,自我).__init__(父级)
self.filename=' '
self.copiedItem=QByteArray()
self.pasteOffset=5
self.prevPoint=QPoint()
self.addOffset=5
self.borders=[]
self.printer=QPrinter(QPrinter .高分辨率)
自我。打印机。setpagesize(q打印机.信)
self.view=GraphicsView()
self.scene=QGraphicsScene(self)
自我。场景。sets center(0,0,PageSize[0],PageSize[1])
self.addBorders()
self.view.setScene(自我场景)
self.wrapped=[] #需要保持包装器活动
buttonLayout=QVBoxLayout()
对于文本,插入(
('添加文本,self.addText),
('添加框,self.addBox),
('添加像素图,self.addPixmap),
('对齐,无),
(' Copy ',self.copy),
('切自切),
(' Paste ',self.paste),
('删除.',self.delete),
(' Rotate ',self.rotate),
('打印.',self.print_),
('打开……'self.open),
(' Save ',self.save),
(' Quit ',self.accept)):
按钮=按钮(文本)
如果不是MAC:
setFocusPolicy(Qt .无焦点)
如果插槽不为无:
button.clicked.connect(插槽)
if text=='Align ':
menu=QMenu(自身)
对于文本,在(
('左对齐,第四季度.AlignLeft),
('右对齐,第四季度.AlignRight),
('顶部对齐,第四季度.AlignTop),
('底部对齐,第四季度.AlignBottom)):
包装器=func工具。部分(自我。设置对齐,arg)
自我包装.追加(包装)
menu.addAction(文本、包装)
button.setMenu(菜单)
如果text=='打印. ':
buttonLayout.addStretch(5)
if text=='Quit ':
buttonLayout.addStretch(1)
buttonLayout.addWidget(按钮)
buttonLayout.addStretch()
layout=QHBoxLayout()
layout.addWidget(self.view,1)
layout.addLayout(buttonLayout)
self.setLayout(布局)
fm=QFontMetrics(self.font())
自我。调整大小(自身。场景。宽度()FM。宽度('删除. ')) 50,
self.scene.height() 50)
self.setWindowTitle('页面设计器)
def addBorders(自身):
self.borders=[]
rect=QRectF(0,0,PageSize[0],PageSize[1])
自我。边框。追加(自我。场景。添加rect(rect,Qt.yellow))
边距=5.25 *磅值
自我。边框。追加(自我。场景。添加矩形(
rect.adjusted(margin,margin,-margin,-margin),
黄色))
def移除边框(自身):
而自我边界:
item=self.borders.pop()
self.scene.removeItem(item)
删除项目
定义拒绝(自身):
自我接受()
定义接受(自身):
self.offerSave()
QDialog.accept(自我)
def offerSave(self):
if(Dirty和qmessagebox。问题(自我,
页面设计器-未保存的更改,
保存未保存的更改吗?
QMessageBox .是|QMessageBox .否)==
QMessageBox .是):
self.save()
定义位置(自身):
点=自我。mapfromglobal(q光标。pos())
如果不是self.view.geometry().包含(点):
coord=random.randint(36,144)
point=QPoint(坐标,坐标)
否则:
如果point==self.prevPoint:
point=QPoint(self.addOffset,self.addOffset)
self.addOffset=5
否则:
self.addOffset=5
self.prevPoint=point
返回self.view.mapToScene(点)
def addText(自身):
dialog=TextItemDlg(position=self。位置(),
scene=self.scene,parent=self)
dialog.exec_()
定义添加框(自身):
BoxItem(self.position()、self.scene)
def addPixmap(自身):
path=(QFileInfo(self.filename).路径()
if self.filename else ' . ')
fname,filetype=qfiledialog。获取打开的文件名(自身、
页面设计器-添加像素图,路径,
像素映射文件(*。bmp * .jpg * .png * .xpm)')
如果不是名称:
返回
自我。createpixmapitem(qpix map(fname),self.position())
def createPixmapItem(self,pixmap,position,matrix=QTransform()):
item=GraphicsPixmapItem(pixmap)
item.setFlags(QGraphicsItem .ItemIsSelectable|
QGraphicsItem .ItemIsMovable)
item.setPos(位置)
item.setTransform(矩阵)
self.scene.clearSelection()
self.scene.addItem(项目)
item.setSelected(True)
全局脏
脏=真
退货项目
def selectedItem(self):
items=self.scene.selectedItems()
如果len(items)==1:
返回项目[0]
不返回
定义副本(自身):
item=self.selectedItem()
如果项目为无:
返回
self.copiedItem.clear()
self.pasteOffset=5
stream=QDataStream(self。复制设备.只写)
self.writeItemToStream(stream,item)
定义切割(自我):
item=self.selectedItem()
如果项目为无:
返回
self.copy()
self.scene.removeItem(item)
删除项目
定义粘贴(自身):
if self.copiedItem.isEmpty():
返回
stream=QDataStream(self。复制设备.只读)
self.readItemFromStream(stream,self.pasteOffset)
self.pasteOffset=5
定义setAlignment(自身,对齐):
#项以任意顺序返回
items=self.scene.selectedItems()
如果len(项目)=1:
返回
#收集坐标数据
leftXs,rightXs,topYs,bottomYs=[],[],[],[]
对于项目中的项目:
rect=item.sceneBoundingRect()
leftXs.append(rect.x())
右x。追加(rect。x()rect。宽度())
topYs.append(rect.y())
底部ys。追加(rect。y()矩形。高度())
#执行校准
如果对齐==Qt .对齐左侧:
xAlignment=min(leftXs)
对于我,枚举中的项(项):
项目。移动方式(x对齐-左xs[I],0)
否则如果对齐==Qt .对齐权:
xAlignment=max(rightXs)
对于我,枚举中的项(项):
项目。移动方式(x对齐-右xs[I],0)
否则如果对齐==Qt .AlignTop:
yAlignment=min(topYs)
对于我,枚举中的项(项):
item.moveBy(0,yAlignment - topYs[i])
否则如果对齐==Qt .对齐底部:
yAlignment=max(bottomYs)
对于我,枚举中的项(项):
item.moveBy(0,yAlignment - bottomYs[i])
全局脏
脏=真
定义旋转(自身):
对于self.scene.selectedItems()中的项目:
项目。设置旋转(项目。旋转()30度)
定义删除(自己):
items=self.scene.selectedItems()
if (len(items)和QMessageBox.question(self,
页面设计者-删除,
删除{0}项{1}?格式(len(items)、
s' if len(items)!=1 else ' '),
QMessageBox .是|QMessageBox .否)==
QMessageBox .是):
在…期间项目:
item=items.pop()
self.scene.removeItem(item)
删除项目
全局脏
脏=真
定义打印_(自我):
dialog=qprindialog(self。打印机)
if dialog.exec_():
painter=QPainter(自助打印机)
painter.setRenderHint(QPainter .抗锯齿)
painter.setRenderHint(QPainter .文本抗锯齿)
self.scene.clearSelection()
self.removeBorders()
self.scene .渲染(画家)
self.addBorders()
定义打开(自身):
self.offerSave()
path=(QFileInfo(self.filename).路径()
if self.filename else ' . ')
fname,filetype=qfiledialog。获取打开的文件名(自身、
页面设计器-打开,路径,
页面设计器文件(*。pgd)')
如果不是名称:
返回
self.filename=fname
fh=无
尝试:
fh=QFile(self.filename)
如果不是fh.open(QIODevice .只读):
引发IOError(str(fh.errorString()))
items=self.scene.items()
在…期间项目:
item=items.pop()
self.scene.removeItem(item)
删除项目
self.addBorders()
stream=QDataStream(fh)
stream.setVersion(QDataStream .Qt_5_7)
magic=stream.readInt32()
如果魔法!=魔术编号:
引发IOError('不是有效的100 . PGD文件)
fileVersion=stream.readInt16()
if fileVersion!=文件版本:
引发IOError('无法识别100 . PGD文件版本)
而不是fh.atEnd():
self.readItemFromStream(流)
除了IOError作为e:
QMessageBox.warning(self,'页面设计器-打开错误,
未能打开{0}: {1}。格式(self.filename,e))
最后:
如果消防栓(Fire Hydrant的缩写)不为无:
fh.close()
全局脏
脏=假
定义保存(自己):
如果不是self .文件名:
路径="."
fname,filetype=qfiledialog。getsave文件名(自身,
网页设计器-另存为,路径,
页面设计器文件(*。pgd)')
如果不是名称:
返回
self.filename=fname
fh=无
尝试:
fh=QFile(self.filename)
如果不是fh.open(QIODevice .只写):
引发IOError(str(fh.errorString()))
self.scene.clearSelection()
stream=QDataStream(fh)
stream.setVersion(QDataStream .Qt_5_7)
stream.writeInt32(MagicNumber)
stream.writeInt16(文件版本)
对于self.scene.items()中的项目:
self.writeItemToStream(stream,item)
除了IOError作为e:
QMessageBox.warning(self,'页面设计器-保存错误,
未能保存{0}: {1}。格式(self.filename,e))
最后:
如果消防栓(Fire Hydrant的缩写)不为无:
fh.close()
全局脏
脏=假
def readItemFromStream(self,Stream,offset=0):
类型=' '
position=QPointF()
matrix=QTransform()
rotateangle=0 #由杨荣东添加
type=stream.readQString()
流位置矩阵
如果偏移:
位置=QPointF(偏移量,偏移量)
如果type=='Text ':
text=' '
font=QFont()
text=stream.readQString()
流字体
rotateangle=stream.readFloat()
tx=TextItem(文本,位置,自身场景,字体,矩阵)
tx.setRotation(旋转角度)
elif type=='Box ':
rect=QRectF()
流矩形
style=Qt .PenStyle(stream.readInt16())
rotateangle=stream.readFloat()
bx=BoxItem(position,self.scene,style,rect,matrix)
bx.setRotation(rotateangle)
elif type=='Pixmap ':
pixmap=QPixmap()
流像素图
rotateangle=stream.readFloat()
px=自我。createpixmapitem(像素映射,位置,矩阵)
px.setRotation(rotateangle)
def writeItemToStream(self,Stream,item):
if isinstance(item,TextItem):
stream.writeQString('Text ')
streamitem.pos() item.transform()
溪流。writeqstring(item。toplaintext())
stream item.font()
溪流。写浮点型(项目。rotation())# add by杨荣东
elif isinstance(item,GraphicsPixmapItem):
stream.writeQString('Pixmap ')
流项目。pos()项目。transform()项。像素图()
溪流。写浮点型(项目。rotation())# add by杨荣东
elif isinstance(item,BoxItem):
stream.writeQString('Box ')
流项目。pos()项目。transform()项。矩形
stream.writeInt16(item.style)
溪流。写浮点型(项目。rotation())# add by杨荣东
app=QA应用程序(sys。argv)
form=MainForm()
rect=QApplication.desktop().可用几何法()
形式。resize(int(rect。width()* 0.6)、int(rect.height() * 0.9))
form.show()
app.exec_()
运行结果
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。