Skip to content

Instantly share code, notes, and snippets.

@mieki256
Last active October 28, 2016 07:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mieki256/3c32427bacc009b89d4db40e67bc7636 to your computer and use it in GitHub Desktop.
Save mieki256/3c32427bacc009b89d4db40e67bc7636 to your computer and use it in GitHub Desktop.
PySide+QGraphicsViewでズーム表示する例。gview_zoom2.pyとの違いは、QGraphicsScene内のItemを拡大縮小することでズーム表示している点。
#!python
# -*- mode: python; Encoding: utf-8; coding: utf-8 -*-
# Last updated: <2016/10/28 09:10:18 +0900>
"""
PySide + QGraphicsView上でズーム表示する。
ホイールを回すか、ステータスバー上のボタンを押すとズームが変わる。
マウスカーソル位置にブラシ画像も表示する。
QGraphicsScene 内の各Itemを拡大縮小して対応する例。
動作確認環境 : Windows10 x64 + Python 2.7.11 + PySide 1.2.4
"""
import sys
from PySide.QtCore import *
from PySide.QtGui import *
brushFile = "brush.png"
bgFile = "bg.jpg"
canvasSize = (640, 480)
padding = 48
# ズーム倍率
zoomValue = 1.0
zoomValues = [1.0 / 32, 1.0 / 24, 1.0 / 20, 1.0 / 16, 1.0 / 12,
1.0 / 8, 1.0 / 6, 1.0 / 4, 1.0 / 3, 1.0 / 2,
1.0,
2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0, 20.0, 24.0, 32.0]
zoomIndex = 10
status = None
zoomDisp = None
gView = None
myApp = None
class DrawAreaScene(QGraphicsScene):
""" 描画ウインドウ用Scene """
def __init__(self, *argv, **keywords):
super(DrawAreaScene, self).__init__(*argv, **keywords)
global brushFile
global padding
global bgFile
global canvasSize
self.zoomv = 1.0
self.bgPixmap = QPixmap(bgFile)
canvasSize = (self.bgPixmap.width(), self.bgPixmap.height())
self.bgItem = QGraphicsPixmapItem(self.bgPixmap)
self.addItem(self.bgItem)
self.bgItem.setOffset(padding, padding)
# Scene にブラシ画像を追加
self.brushPixmap = QPixmap(brushFile)
self.brushItem = QGraphicsPixmapItem(self.brushPixmap)
self.addItem(self.brushItem)
def setVisibleBrush(self, flag):
""" ブラシ表示の有効無効切り替え """
self.brushItem.setVisible(flag)
def changeScale(self, scale):
""" ズーム変更 """
global padding
self.zoomv = scale
t = QTransform()
t.scale(self.zoomv, self.zoomv) # スケールだけ反映
self.bgItem.setTransform(t)
bt = QTransform()
bt.scale(self.zoomv, self.zoomv)
self.brushItem.setTransform(bt)
def changeBrushPos(self, x, y):
""" ブラシの表示位置を変更 """
global zoomValue
x = float(x) / zoomValue
y = float(y) / zoomValue
# ブラシが非表示なら表示を有効化
if not self.brushItem.isVisible():
self.setVisibleBrush(True)
# ブラシの表示位置を変更
pm = self.brushItem.pixmap()
xd = (pm.width() / 2)
yd = (pm.height() / 2)
self.brushItem.setOffset(int(x - xd), int(y - yd))
class DrawAreaView(QGraphicsView):
""" メインになるQGraphicsView """
def __init__(self, *argv, **keywords):
super(DrawAreaView, self).__init__(*argv, **keywords)
self.setBackgroundBrush(QColor(64, 64, 64, 255)) # 背景色を設定
self.setCacheMode(QGraphicsView.CacheBackground)
# 描画更新の仕方を選択
self.setViewportUpdateMode(QGraphicsView.SmartViewportUpdate)
# self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
# self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
self.oldCurPos = (0, 0)
self.button = Qt.NoButton
# Sceneを登録
scene = DrawAreaScene(self)
self.setScene(scene)
self.setSceneNewRect()
# 子のSceneに対してマウストラッキングを有効にすると
# マウスカーソル移動時に常時 mouseMoveEvent() が呼ばれるようになる
vp = self.viewport().setMouseTracking(True)
def mousePressEvent(self, event):
""" マウスボタンを押した """
x, y = self.getMousePos(event, "Click")
self.button = event.button()
self.oldCurPos = (x, y)
if event.buttons() & Qt.MidButton:
# 中ボタンならマウスカーソル変更
global myApp
myApp.setOverrideCursor(Qt.ClosedHandCursor)
def mouseReleaseEvent(self, event):
""" マウスボタンを離した """
x, y = self.getMousePos(event, "Release")
self.button = Qt.NoButton
# マウスカーソルを元に戻す
global myApp
myApp.restoreOverrideCursor()
def mouseMoveEvent(self, event):
""" マウスを動かしてる時に呼ばれる処理 """
if self.button == Qt.NoButton:
# マウスカーソル移動のみ
x, y = self.getMousePos(event, "Move")
self.changeBrushPos(x, y)
elif self.button == Qt.LeftButton:
# 左ドラッグ
x, y = self.getMousePos(event, "Drag")
self.changeBrushPos(x, y)
elif self.button == Qt.MidButton:
# 中ボタンドラッグ
x, y = self.getMousePos(event, "Drag")
# 前回の座標位置との差を求める
dx = self.oldCurPos[0] - x
dy = self.oldCurPos[1] - y
self.oldCurPos = (x, y)
# スクロールバーの位置を変更
ox = self.horizontalScrollBar().value()
oy = self.verticalScrollBar().value()
self.horizontalScrollBar().setValue(ox + dx)
self.verticalScrollBar().setValue(oy + dy)
else:
super(DrawAreaScene, self).mouseMoveEvent()
def getMousePos(self, event, msg):
""" マウス座標を取得 """
x = event.pos().x()
y = event.pos().y()
kind = ""
if event.buttons() & Qt.LeftButton:
kind = "Left "
if event.button() & Qt.MidButton:
kind += "Mid "
if event.button() & Qt.RightButton:
kind += "Right "
global status
status.showMessage("(%d , %d) %s %s" % (x, y, kind, msg))
return (x, y)
def changeBrushPos(self, x, y):
""" ブラシ表示位置を変更 """
vr = self.viewport().rect()
vw, vh = vr.width(), vr.height()
sr = self.scene().sceneRect()
sw, sh = sr.width(), sr.height()
if vw - sw < 0:
# Hスクロールバーが有る
x += self.horizontalScrollBar().value()
else:
# Hスクロールバーが無いのでviewportの真ん中を基準にして座標算出
x = (sw / 2) + (x - (vw / 2))
if vh - sh < 0:
# Vスクロールバーが有る
y += self.verticalScrollBar().value()
else:
# Vスクロールバーが無いのでviewportの真ん中を基準にして座標算出
y = (sh / 2) + (y - (vh / 2))
self.scene().changeBrushPos(int(x), int(y))
def resizeEvent(self, event):
""" リサイズ時に呼ばれる処理 """
super(DrawAreaView, self).resizeEvent(event)
self.setSceneNewRect()
def scrollContentsBy(self, dx, dy):
""" スクロールバー操作時に呼ばれる処理 """
# スクロール中、Scene内にブラシがあると
# 何故かゴミが残るので、ブラシを非表示にしている
self.scene().setVisibleBrush(False)
super(DrawAreaView, self).scrollContentsBy(dx, dy)
def setSceneNewRect(self):
""" Sceneの矩形を更新。キャンバス周辺に余白を設けたサイズを設定 """
global canvasSize
global padding
global zoomValue
w, h = canvasSize
w += padding * 2
h += padding * 2
w *= zoomValue
h *= zoomValue
rect = QRectF(0, 0, int(w), int(h))
# Sceneの矩形を更新。自動でスクロールバーの長さも変わってくれる
self.scene().setSceneRect(rect)
def wheelEvent(self, event):
""" マウスホイール回転時 """
d = 0
if event.delta() < 0:
# 下に回転
d = -1
else:
# 上に回転
d = 1
self.changeZoom(d)
def changeZoom(self, d):
""" ズーム変更 """
global zoomValues
global zoomIndex
global zoomValue
vr = self.viewport().rect()
vw, vh = vr.width(), vr.height()
sr = self.scene().sceneRect()
sw, sh = sr.width(), sr.height()
# 今までのスクロールバーの位置を取得
ox = float(self.horizontalScrollBar().value())
oy = float(self.verticalScrollBar().value())
w = sw - vw
h = sh - vh
ox /= w
oy /= h
# 新しいズーム値を得る
oldZoomIndex = zoomIndex
zoomIndex += d
if zoomIndex < 0:
zoomIndex = 0
if zoomIndex >= len(zoomValues):
zoomIndex = len(zoomValues) - 1
if zoomIndex == oldZoomIndex:
return
zoomValue = zoomValues[zoomIndex]
global zoomDisp
zoomDisp.setText("%d%s" % (zoomValue * 100, "%"))
self.scene().setVisibleBrush(False)
self.scene().changeScale(zoomValue)
self.setSceneNewRect()
self.viewport().update()
# 新しいスクロールバーの位置を設定
sr = self.scene().sceneRect()
sw, sh = sr.width(), sr.height()
if w < 0:
nx = 0.5 * (sw - vw)
else:
nx = ox * (sw - vw)
self.horizontalScrollBar().setValue(int(nx))
if h < 0:
ny = 0.5 * (sh - vh)
else:
ny = oy * (sh - vh)
self.verticalScrollBar().setValue(int(ny))
class MyMainWindow(QMainWindow):
""" メインウインドウ """
def __init__(self, *argv, **keywords):
super(MyMainWindow, self).__init__(*argv, **keywords)
self.setWindowTitle("Zoom and Mouse Tracking Test")
self.resize(640, 480)
self.initMenuBar() # メニューバー
self.initStatusBar() # ステータスバー
# 中央Widget
global gView
gView = DrawAreaView(self)
self.gView = gView
self.setCentralWidget(gView)
def initMenuBar(self):
""" メニューバー初期化 """
mb = QMenuBar()
file_menu = QMenu("&File", self)
exit_action = file_menu.addAction("&Close")
exit_action.setShortcut('Ctrl+Q')
exit_action.triggered.connect(qApp.quit)
mb.addMenu(file_menu)
self.setMenuBar(mb)
def initStatusBar(self):
""" ステータスバー初期化 """
global status
global zoomDisp
status = QStatusBar(self)
self.setStatusBar(status)
# 縮小ボタン
zoomOutBtn = QPushButton("-", self.statusBar())
zoomOutBtn.setFixedSize(24, 24)
# 拡大ボタン
zoomInBtn = QPushButton("+", self.statusBar())
zoomInBtn.setFixedSize(24, 24)
# 倍率表示
zoomDisp = QLabel("100%", self.statusBar())
zoomDisp.setFixedWidth(80)
zoomDisp.setFrameStyle(QFrame.Box | QFrame.Sunken)
zoomDisp.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
# ステータスバーに追加
self.statusBar().addPermanentWidget(zoomOutBtn)
self.statusBar().addPermanentWidget(zoomDisp)
self.statusBar().addPermanentWidget(zoomInBtn)
status.showMessage("Status Bar")
# ボタンが押されたときの処理を登録
zoomOutBtn.clicked.connect(self.zoomOut)
zoomInBtn.clicked.connect(self.zoomIn)
def zoomOut(self):
""" 縮小ボタンを押した時の処理 """
self.gView.changeZoom(-1)
def zoomIn(self):
""" 拡大ボタンを押した時の処理 """
self.gView.changeZoom(1)
def main():
""" メイン処理 """
# このあたりを指定すると描画が速くなるという話を見かけたが、
# "native"、"raster"、"opengl" を指定しても結果は変わらなかった…
QApplication.setGraphicsSystem("raster")
global myApp
myApp = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(myApp.exec_())
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment