#!python # -*- mode: python; Encoding: utf-8; coding: utf-8 -*- # Last updated: <2016/12/04 16:30:21 +0900> u""" CGツール用RubberBandを実装. 蟻の行進(Marching ant)が表示できるのか実験。 動作確認環境 : Windows10 x64 + Python 2.7.12 + PySide 1.2.4 """ import sys from PySide.QtCore import * # NOQA from PySide.QtGui import * # NOQA class CGRuberBand(QGraphicsPolygonItem, QObject): u"""CGツール用のラバーバンド. 2つのクラスを継承してる.""" def __init__(self, rect, parent=None, scene=None): """init.""" QGraphicsPolygonItem.__init__(self, parent) QObject.__init__(self, parent) # タイマーを使うために多重継承 self.start_pos = QPoint() self.end_pos = QPoint() # 背景用のQGraphics*Itemを用意する # 白黒の点線を描画するため、背景は白、自分自身は黒で線を描画する self.bg = QGraphicsPolygonItem(self) # 背景用のpen設定。白一色の線。 bg_pen = QPen(QBrush(Qt.white), 1, s=Qt.SolidLine, c=Qt.SquareCap, j=Qt.MiterJoin) self.bg.setPen(bg_pen) self.bg_pen = bg_pen scene.addItem(self.bg) # 点線用のpen設定。黒の点線。 pen = QPen(QBrush(Qt.black), 1, s=Qt.DashLine, c=Qt.SquareCap, j=Qt.MiterJoin) pen.setDashPattern([3, 3]) # Dash line のパターンを設定 self.setPen(pen) self.pen = pen # 内部を半透明で塗り潰すなら以下のコメントアウトを外す # self.brush = QBrush(QColor(48, 160, 255, 64)) # self.setBrush(self.brush) self.startTimer(25) # 一定時間毎に処理を呼んで点線をアニメさせる self.hide() # 発生時は非表示にしておく scene.addItem(self) # 自身をsceneに追加登録 def timerEvent(self, event): u"""一定時間毎に呼ばれる処理.""" if self.isVisible(): # 点線の描画開始位置を変化させて蟻の行進に見せる do = (self.pen.dashOffset() + 1) % 6 self.pen.setDashOffset(do) self.setPen(self.pen) def set_geometry(self, p0, p1): u"""範囲を設定.""" self.start_pos = QPoint(p0) self.end_pos = QPoint(p1) # ドット単位で正確に表示するためにwidthとheightを調整 rect = QRect(p0, p1) w = p1.x() - p0.x() h = p1.y() - p0.y() rect.setWidth(w) rect.setHeight(h) self.poly = QPolygonF(rect) self.bg.setPolygon(self.poly) self.setPolygon(self.poly) def show(self): u"""表示.""" self.bg.show() super(CGRuberBand, self).show() def hide(self): u"""非表示.""" self.bg.hide() super(CGRuberBand, self).hide() class GView(QGraphicsView): """Graphics View.""" def __init__(self, *argv, **keywords): """init.""" super(GView, self).__init__(*argv, **keywords) scene = QGraphicsScene(self) self.setScene(scene) # 背景画像 pm = QPixmap("./tmp_bg.png") pm_item = QGraphicsPixmapItem(pm) scene.addItem(pm_item) # ラバーバンドを生成 # sceneへの追加登録は、sceneを渡してラバーバンド側で行う self.rband = CGRuberBand(QRect(), parent=None, scene=self.scene()) self.start_pos = QPoint() self.end_pos = QPoint() self.selecting = False def mousePressEvent(self, event): u"""マウスボタンが押された.""" if event.button() == Qt.LeftButton: # 左ボタンが押された if not self.selecting: self.start_pos = event.pos() # クリックした座標を記憶 self.end_pos = self.start_pos self.set_rubberband_geometry(self.start_pos, self.end_pos) self.rband.show() self.selecting = True elif event.button() == Qt.RightButton: # 右ボタンが押された self.clear_rubberband_area() def mouseMoveEvent(self, event): u"""マウスカーソルが動いた.""" if self.selecting: self.end_pos = event.pos() self.set_rubberband_geometry(self.start_pos, self.end_pos) def mouseReleaseEvent(self, event): u"""マウスボタンが離された.""" if event.button() == Qt.LeftButton: self.selecting = False def set_rubberband_geometry(self, p0, p1): u"""ラバーバンドの範囲を設定.""" if p0.isNull(): return if p0 == p1: p0 = self.mapToScene(p0).toPoint() p1 = p0 self.rband.set_geometry(p0, p1) self.set_status("(%d, %d)" % (p0.x(), p0.y())) return # scene上の座標値に変換 p0 = self.mapToScene(p0).toPoint() p1 = self.mapToScene(p1).toPoint() self.rband.set_geometry(p0, p1) x0, y0 = p0.x(), p0.y() x1, y1 = p1.x(), p1.y() dx, dy = x1 - x0, y1 - y0 if dx >= 0: dx += 1 elif dx < 0: dx = -dx + 1 if dy >= 0: dy += 1 if dy < 0: dy = -dy + 1 s = "(%d, %d) - (%d, %d) : (%d x %d)" % (x0, y0, x1, y1, dx, dy) self.set_status(s) def clear_rubberband_area(self): u"""ラバーバンドの範囲をクリア.""" self.rband.hide() self.start_pos = QPoint() self.end_pos = QPoint() def set_status(self, str): u"""ステータスバー相当のテキストを設定.""" self.parent().set_status(str) class MyWidget(QWidget): u"""メインウインドウ相当.""" def __init__(self, *argv, **keywords): """init.""" super(MyWidget, self).__init__(*argv, **keywords) self.gview = GView(self) self.lbl = QLabel("Ready", self) l = QVBoxLayout() l.addWidget(self.gview) l.addWidget(self.lbl) self.setLayout(l) def set_status(self, str): u"""ステータスバー相当にテキストを設定.""" self.lbl.setText(str) if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.show() sys.exit(app.exec_())