在Qt界面庫中,對於圖形的繪制,在前面介紹了一種使用QPainter實現普通二維圖形的繪制方法,該方法在paintEvent事件里編寫繪圖程序,其本質繪制的圖形是位圖,這種方法更適合於繪制復雜度不高的固定圖形,並且不能實現圖項的選擇、編輯、拖放、修改等交互功能。
對於需要繪制大量的、需要交互的圖形,可使用Graphics View繪圖架構,它是一種基於圖形項(Graphics Item)的模型/視圖模式,這種方式可以在一個場景中可繪制大量圖元項,且每個圖元項都是可選擇、可交互的。
在Graphics View繪圖架構中,主要涉及到下面三個類的使用:
1. 場景類(QGraphicsScene):該類提供繪圖場景(Scene),場景是不可見的,是一個抽象的管理圖形項的容器,可向場景中添加圖形項,獲取場景中的某個圖形項等;
2. 視圖類(QGraphicsView):該類提供繪圖的視圖(View)組件,用於顯示場景中的內容。可以為一個場景設置幾個視圖,用於對同一個數據集提供不同的觀察方式;
3. 圖形項類(QGraphicsItem):該類提供了一些基本的圖形元件,也可在此基礎上自定義圖形項,它支持各種事件的響應,如鼠標事件、鍵盤事件、拖放事件等,以實現圖形的交互功能
在Graphics View繪圖架構中涉及到了3個坐標系,即場景坐標、視圖坐標及圖形項坐標。其中,場景坐標類似於QPainter的邏輯坐標,一般以場景的中心為原點;視圖坐標是窗口界面的物理坐標,其左上角為原點坐標;圖形項坐標是局部邏輯坐標,通常以圖件的中心為原點
坐標實例
import sys from PyQt5.QtWidgets import QApplication,QGraphicsScene,QGraphicsView,QGraphicsRectItem,QMainWindow,QLabel,QGraphicsItem,QGraphicsEllipseItem from PyQt5.QtCore import Qt,pyqtSignal,QPoint,QRectF class QMyGraphicsView(QGraphicsView): sigMouseMovePoint=pyqtSignal(QPoint) #自定義信號sigMouseMovePoint,當鼠標移動時,在mouseMoveEvent事件中,將當前的鼠標位置發送出去 #QPoint--傳遞的是view坐標 def __init__(self,parent=None): super(QMyGraphicsView,self).__init__(parent) def mouseMoveEvent(self, evt): pt=evt.pos() #獲取鼠標坐標--view坐標 self.sigMouseMovePoint.emit(pt) #發送鼠標位置 QGraphicsView.mouseMoveEvent(self, evt) class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.resize(600,400) self.view=QMyGraphicsView() #創建視圖窗口 self.setCentralWidget(self.view) # 設置中央控件 self.statusbar=self.statusBar() #添加狀態欄 self.labviewcorrd=QLabel('view坐標:') self.labviewcorrd.setMinimumWidth(150) self.statusbar.addWidget(self.labviewcorrd) self.labscenecorrd=QLabel('scene坐標:') self.labscenecorrd.setMinimumWidth(150) self.statusbar.addWidget(self.labscenecorrd) self.labitemcorrd = QLabel('item坐標:') self.labitemcorrd.setMinimumWidth(150) self.statusbar.addWidget(self.labitemcorrd) rect=QRectF(-200,-100,400,200) self.scene=QGraphicsScene(rect) #創建場景 #參數:場景區域 #場景坐標原點默認在場景中心---場景中心位於界面中心 self.view.setScene(self.scene) #給視圖窗口設置場景 item1=QGraphicsRectItem(rect) #創建矩形---以場景為坐標 item1.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable|QGraphicsItem.ItemIsMovable) #給圖元設置標志 #QGraphicsItem.ItemIsSelectable---可選擇 #QGraphicsItem.ItemIsFocusable---可設置焦點 #QGraphicsItem.ItemIsMovable---可移動 #QGraphicsItem.ItemIsPanel--- self.scene.addItem(item1) #給場景添加圖元 for pos,color in zip([rect.left(),0,rect.right()],[Qt.red,Qt.yellow,Qt.blue]): item=QGraphicsEllipseItem(-50,-50,100,100) #創建橢圓--場景坐標 #參數1 參數2 矩形左上角坐標 #參數3 參數4 矩形的寬和高 item.setPos(pos,0) #給圖元設置在場景中的坐標(移動圖元)--圖元中心坐標 item.setBrush(color) #設置畫刷 item.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable|QGraphicsItem.ItemIsMovable) self.scene.addItem(item) self.scene.clearSelection() #【清除選擇】 self.view.sigMouseMovePoint.connect(self.slotMouseMovePoint) def slotMouseMovePoint(self,pt): self.labviewcorrd.setText('view坐標:{},{}'.format(pt.x(),pt.y())) ptscene=self.view.mapToScene(pt) #把view坐標轉換為場景坐標 self.labscenecorrd.setText('scene坐標:{:.0f},{:.0f}'.format(ptscene.x(),ptscene.y())) item=self.scene.itemAt(ptscene,self.view.transform()) #在場景某點尋找圖元--最上面的圖元 #返回值:圖元地址 #參數1 場景點坐標 #參數2 ???? if item != None: ptitem=item.mapFromScene(ptscene) #把場景坐標轉換為圖元坐標 self.labitemcorrd.setText('item坐標:{:.0f},{:.0f}'.format(ptitem.x(),ptitem.y())) if __name__ == "__main__": app = QApplication(sys.argv) ex = MainWindow() ex.show() sys.exit(app.exec_())
import sys
from PyQt5.QtWidgets import QWidget, QApplication,QGraphicsScene,QGraphicsView
import time
class Example(QWidget):
def __init__(self):
super().__init__()
self.resize(500,400)
scene=QGraphicsScene(self) #創建場景
self.t=scene.addText("Hello, world!") #在場景中添加文本
view=QGraphicsView(scene,self) #創建視圖窗口
view.move(10,10)
view.show() #顯示
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
添加文本圖元
font=QFont("華文琥珀",20,QFont.Bold)
t=scene.addText("Hello, world!",font) #在場景中添加文本圖元
#參數1 文本
#參數2 字體
添加矩形
pen = QPen() pen.setColor(Qt.blue) pen.setWidth(3) brush = QBrush(Qt.SolidPattern) brush.setColor(Qt.yellow) rect = QRectF(130,130,100,150) juxin=scene.addRect(30,30,100,100,pen,brush) #添加矩形圖元--方式一 #參數1--參數4 坐標和寬高---以View為坐標系 #參數5 畫筆 #參數6 畫刷 juxin = scene.addRect(rect, pen, brush) # 添加矩形圖元--方式二
rect = QRectF(0,0,100,100) item1 = QGraphicsRectItem(rect) # 創建矩形---以場景為坐標 scene.addItem(item1)
添加線段
pen = QPen() pen.setColor(Qt.blue) pen.setWidth(3) line=scene.addLine(0,0,50,50,pen) #添加直線--方式一 lf=QLineF(50,50,0,50) line = scene.addLine(lf, pen) #添加直線--方式二
添加橢圓
item = QGraphicsEllipseItem(10, 10, 100, 100) # 創建橢圓--場景坐標 # 參數1 參數2 矩形左上角坐標 # 參數3 參數4 矩形的寬和高 scene.addItem(item)
item=scene.addEllipse(10, 10, 100, 100,pen,brush)
rect=QRectF(10, 10, 100, 100)
item=scene.addEllipse(rect,pen,brush)
圖元的操作(縮放,平移,旋轉)
想要旋轉,平移,縮放窗口中的圖元,一般有兩種思路:
1. 操作View,概念類似於旋轉攝像頭,從而獲取對場景Scene的不同觀察結果,這樣操作的好處是,場景中的圖元本質上沒有發生任何的變化,僅僅改變了View的計算矩陣,效率非常高,並且,如果該Scene被多個View綁定觀察,對單獨一個view的操作,將不會影響到別的view的觀察結果
2.操作Scene中所有的圖元Item或者直接操作單個圖元Item,概念類似於真實改變了場景中的各個物體的擺放方式,該方法使用的場合位:當一個場景,被多個view觀察的時候,場景中的某一個變化操作,需要被這多個view同時觀察到
下面是操作圖元的方法
1. 圖元的縮放:
rect=QRectF(100, 100, 200, 100) item=scene.addEllipse(rect,pen,brush) item.setScale(0.5) #對圖元進行縮放 #參數為圖元大小的縮放比 #需要注意的是,該方法的縮放基准是固定的,也就是說,如果連續兩次調用該方法,但是參數的數字一樣的話,第二次的調用將不會改變圖元的大小 print(item.scale()) #返回圖元的縮放比
2.圖元的平移
item=scene.addRect(200,200,200,100,pen,brush) item.setPos(100,50) #移動圖元--方式一 #參數 移動的偏移量 #其傳參的x與y坐標值,屬於場景Scene的坐標,如果調用的時候,坐標來源於view的鼠標點擊事件的坐標,需要調用mapToScene()將view坐標值轉換到場景坐標 item.moveBy(100,50) #移動圖元--方式二
3.圖元的旋轉
import sys from PyQt5.QtWidgets import QWidget, QApplication,QGraphicsScene,QGraphicsView,QGraphicsRectItem,QGraphicsEllipseItem,QGraphicsItem,QPushButton from PyQt5.QtGui import QPen,QBrush from PyQt5.QtCore import Qt,QRectF,QLineF class Example(QWidget): def __init__(self): super().__init__() self.resize(500,400) scene=QGraphicsScene() #創建場景 scene.setSceneRect(0,0,600,500) #設置場景的坐標原點和寬高 pen = QPen() pen.setColor(Qt.blue) pen.setWidth(3) brush=QBrush(Qt.yellow) item1=scene.addRect(0,0,250,25,pen,brush) item=scene.addRect(100,100,200,100,pen,brush) item.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable) item.setPos(-100, -100) item.setTransformOriginPoint(0,0) #設置旋轉中心----???不按這點旋轉啊 #默認 圖元的左上角 #該方法傳參的坐標值為圖元自身的坐標系 item.setRotation(30) #旋轉圖元 #參數 角度 #正數 逆時針 #設置的值的基准也是不變的,也就是說,連續兩次調用該方法,都傳入相同的參數值,圖元只會旋轉一次 print(item.rotation()) #返回旋轉角度 view=QGraphicsView(scene,self) #創建視圖窗口 view.resize(500,400) view.show() #顯示 if __name__ == "__main__": app = QApplication(sys.argv) ex = Example() ex.show() sys.exit(app.exec_())
天子驕龍
item=QGraphicsEllipseItem(-50,-50,100,100) #創建橢圓--場景坐標
#參數1 參數2 矩形左上角坐標
#參數3 參數4 矩形的寬和高