QGraphicsItemで透明色を表す市松模様を描く
QGraphicsView, QGraphicsSene, QGraphicsItem, QScrollAreaの組合せ。
成果物
コード
main.py
#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PySide2 import QtCore, QtGui, QtWidgets class Window(QtWidgets.QWidget): def __init__(self): super(self.__class__, self).__init__() view = GraphicView() scroller = QtWidgets.QScrollArea() scroller.setWidget(view) layout = QtWidgets.QGridLayout() layout.addWidget(scroller, 0, 0) self.setLayout(layout) self.resize(view.width(), view.height()) self.setWindowTitle("QGraphics View Scene Item + QScrollArea") self.show() class GraphicView(QtWidgets.QGraphicsView): def __init__(self): QtWidgets.QGraphicsView.__init__(self) self.setWindowTitle("QGraphicsScene draw Grid") self.setScene(EditorScene(self)) class EditorScene(QtWidgets.QGraphicsScene): def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.size = 16 self.scale = 32 self.setSceneRect(0, 0, self.size*self.scale, self.size*self.scale) grid = GridItem() self.addItem(grid) bg = BackgroundItem() self.addItem(bg) drawable = DrawableItem() self.addItem(drawable) bg.setZValue(0) drawable.setZValue(1) grid.setZValue(9999) class DrawableItem(QtWidgets.QGraphicsRectItem): def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) def paint(self, painter, option, widget): painter.fillRect(widget.rect(), QtGui.QBrush( QtGui.QColor(0,0,0,0), QtCore.Qt.SolidPattern)) painter.fillRect(32, 32, 32, 32, QtGui.QBrush( QtGui.QColor(255,0,0,128), QtCore.Qt.SolidPattern)) painter.fillRect(64, 64, 32, 32, QtGui.QBrush( QtGui.QColor(255,0,0,128), QtCore.Qt.SolidPattern)) class BackgroundItem(QtWidgets.QGraphicsRectItem): def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.size = 16 self.scale = 32 self.colors = [QtGui.QColor(196,196,196,255), QtGui.QColor(232,232,232,255)] def paint(self, painter, option, widget): for i in range(self.size*self.size): x = (i % self.size) y = (i // self.size) color = QtGui.QColor(128,128,128,255) if 0 == (i % 2) and 0 == (x % 2) else QtGui.QColor(196,196,196,255) painter.fillRect(x * (self.scale), y * (self.scale), self.scale//2, self.scale//2, self.colors[0]) painter.fillRect(x * (self.scale)+self.scale//2, y * (self.scale)+self.scale//2, self.scale//2, self.scale//2, self.colors[0]) # painter.setBrush(QtGui.QBrush(self.colors[0], QtCore.Qt.SolidPattern)) # painter.drawRect(x * (self.scale), y * (self.scale), self.scale//2, self.scale//2) # painter.drawRect(x * (self.scale)+self.scale//2, y * (self.scale)+self.scale//2, self.scale//2, self.scale//2) painter.fillRect(x * (self.scale)+self.scale//2, y * (self.scale), self.scale//2, self.scale//2, self.colors[1]) painter.fillRect(x * (self.scale), y * (self.scale)+self.scale//2, self.scale//2, self.scale//2, self.colors[1]) # painter.setBrush(QtGui.QBrush(self.colors[1], QtCore.Qt.SolidPattern)) # painter.drawRect(x * (self.scale)+self.scale//2, y * (self.scale), self.scale//2, self.scale//2) # painter.drawRect(x * (self.scale), y * (self.scale)+self.scale//2, self.scale//2, self.scale//2) class GridItem(QtWidgets.QGraphicsRectItem): def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.size = 16 self.scale = 32 def paint(self, painter, option, widget): painter.fillRect(widget.rect(), QtGui.QBrush(QtGui.QColor(0,0,0,0), QtCore.Qt.SolidPattern)) lines = [] for y in range(self.size+1): lines.append(QtCore.QLine(0, y*self.scale, self.size*self.scale, y*self.scale)) for x in range(self.size+1): lines.append(QtCore.QLine(x*self.scale, 0, x*self.scale, self.size*self.scale)) painter.drawLines(lines) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = Window() sys.exit(app.exec_())
python3 main.py
構造
画像
- 市松模様(透明色の背景を表す)
- 描画対象(赤いドット)
- グリッド
クラス
ハマった所
QGraphicsItem
QGraphicsItemを直接継承すると正常に表示されず無限ループになってしまった。QGraphicsRectItemを継承すると成功した。
drawRect
, fillRect
の違い
透明色の背景を示す市松模様を描くのにQPainterのfillRect
を使った。drawRect
にすると主線(枠線)が描かれてしまう。
API | 主線 |
---|---|
drawRect |
有 |
fillRect |
無 |
painter.fillRect(x, y, w, h, QtGui.QColor(0,0,0,0))
painter.setBrush(QtGui.QBrush(QtGui.QColor(0,0,0,0), QtCore.Qt.SolidPattern)) painter.drawRect(x, y, w, h)
描画順序
- 市松模様(透明色の背景を表す)
- 描画対象(赤いドット)
- グリッド
QGraphicsItemのsetZValueを使う。値が大きいほど上に描かれる。
bg.setZValue(0) drawable.setZValue(1) grid.setZValue(9999)
所感
Qt5, PySide2, いいね!
- クラスの抽象化がキレイ
- 名前に統一性がある
- ドキュメントが読みやすい
Pythonの公式ライブラリAPIとドキュメントよりも遥かにキレイ。すばらしい。公式は見習って欲しい。特に名前の付け方。名前がキレイだと書いても読んでも気持ちいい。
ドキュメントも良い。要点を網羅している。Python公式のようにダラダラ書いておらず、名前、型、引数、戻り値さえ見れば大体わかるし使える。
今までPython書いてて一番楽しい。やっぱりキレイな抽象化と名前はとても大事。
対象環境
- Raspbierry pi 4 Model B
- Raspbian buster 10.0 2019-09-26 ※
- bash 5.0.3(1)-release
- Qt 5.11
- Python 3.7.3
- PySide2
$ uname -a Linux raspberrypi 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l GNU/Linux