bravo's blog

多分ググっても出てこないようなプログラミング記事を目指します!

PyQtクラスを追加しやすくするEmacs Lisp

Emacs LispPyQtのクラスを作る部分を省力化

PyQtプログラミングをやっていると、クラスを追加するのに毎回同じような記述をするのが非常にかったるくなってきます。普通にコピペでも良いのですが、以下のようなEmacs lispでもう少しEmacsらしく使えるようにしてみました。

  1. .emacsに以下のコードを追記してEmacsを再起動などで.emacsをリロードします。
  2. py-modeの状態でM-x pyqt-new-classで呼び出すとミニバッファにNew Qt class:と表示されますので、新しいクラスの名前を入力してEnterを押します。
  3. 次にミニバッファにBase Qt class:と表示されますので、直前に入力したクラスの親クラスを入力してEnterを押します。
  4. 追加したいクラスの__init__()まで含めたコードが入力されます。

この状態でも特に不自由はしてないのですが、インデント幅が固定だったりと改善の余地はあります。

.emacsに追記するコード

(add-hook 'py-mode-hook
          '(lambda ()
             (defun pyqt-new-class
               (class-name base-class-name)
               (interactive "sNew Qt class: \nsBase Qt class: ")
               (insert
                (format "class %s(%s):\n    def __init__(self, parent=None):\n        super(%s, self).__init__(parent)\n"
                        class-name base-class-name class-name)))
             ))

実行画面

関数を呼び出し
f:id:bravo:20160207015506p:plain

追加クラス名を入力
f:id:bravo:20160207015508p:plain

追加クラスの親クラス名を入力
f:id:bravo:20160207015507p:plain

このように入力されます。
f:id:bravo:20160207015509p:plain

PyQtでの2Dグラフィックス その1 (QWidgetに直接描画する)

PyQtでの描画処理について

PyQtでグラフィックス処理をするには、主に以下のような方法があります。

  • QWidgetの描画処理をオーバーライド:単純なグラフィック描画や自作のウィジェットとして描画する
  • QGraphicsItemの使用:Qtに備わった複数のグラフィックを扱うフレームワーク
  • OpenGLを使用:OpenGLの命令を使う場合(執筆時点では未調査、存在確認だけです。)

QWidgetに直接描画する

paintEventメソッドのオーバーライド

QWidgetやQWidgetから派生したクラスでは、実際に描画する処理がpaintEvent()メソッドで行われます。 また、描画内容を変えたい時にはupdate()メソッドを呼び出すと、QWidget側が適切に更新します。

実際の描画は、painteEvent()内でQPainterやQPainterPathのインスタンスを生成し、描画命令をするだけです。 単純に線を引くdrawLine()や、連続した線を引くdrawPolyline()、長方形を描画するdrawRect()、などがあります。 描画命令は種類が豊富なので、公式のAPIが載ったドキュメントを参照ください。 検索サイト等で「QPainter (バージョン番号)」や「QPainterPath (バージョン番号)」で検索すればすぐに出てくると思います。

正方形を描画するサンプル

赤い線と黄色い塗りの正方形を描画します。

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class Widget(QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(Qt.red)
        painter.setBrush(Qt.yellow)
        painter.drawRect(10, 10, 100, 100)

def main():
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    w.raise_()
    app.exec_()

if __name__ == '__main__':
    main()

実行結果
f:id:bravo:20160207080309p:plain

マウスで描画するサンプル

真っ白なQWidgetを用意し、マウスでドラッグしている間に線を描画します。

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class Widget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.px = None
        self.py = None
        self.points = []
        self.psets = []

    def mousePressEvent(self, event):
        self.points.append(event.pos())
        self.update()

    def mouseMoveEvent(self, event):
        self.points.append(event.pos())
        self.update()

    def mouseReleaseEvent(self, event):
        self.pressed = False
        self.psets.append(self.points)
        self.points = []
        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(Qt.NoPen)
        painter.setBrush(Qt.white)
        painter.drawRect(self.rect())

        painter.setPen(Qt.black)

        # draw historical points
        for points in self.psets:
            painter.drawPolyline(*points)

        # draw current points
        if self.points:
            painter.drawPolyline(*self.points)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    w.raise_()
    sys.exit(app.exec_())

実行結果(描画部分はマウスで操作しています)
f:id:bravo:20160207082701p:plain

PR