| ■ | PyQt湯けむりウィジェットツアー(第6弾) |
| ■ | だらだらオプションツアー1 |
#! /usr/local/bin/python
import sys
from qt import *
class OeWidget(QWidget):
def __init__(self, *args):
apply(QWidget.__init__, (self,) + args)
self.resize(300, 324)
fmenu = QPopupMenu(self)
fmenu.insertItem(self.tr("終了"), self.bye, 0)
self.mb = QMenuBar(self)
self.mb.insertItem(self.tr("ファイル(&F)"), fmenu)
self.mb.setGeometry(0, 0, 300, 24)
self.setBackgroundMode(QWidget.NoBackground)
self.offscreenBuffer = None
self.px = self.py = None
def bye(self):
self.close()
def mousePressEvent(self, ev):
self.px = ev.x(); self.py = ev.y()
def mouseMoveEvent(self, ev):
pa = QPainter();
pa.begin(self.offscreenBuffer)
if self.px != None:
pen = QPen(QColor("blue"), 4, QWidget.SolidLine)
pa.setPen(pen)
# 線を描く
#pa.drawLine(self.px, self.py, ev.x(), ev.y())
# 長方形を描く
pa.drawRect(ev.x()-3, ev.y()-3, 6, 6)
self.px = ev.x(); self.py = ev.y()
pa.end()
self.repaint()
def resizeEvent(self, ev):
if self.offscreenBuffer == None:
self.offscreenBuffer = QPixmap(ev.size())
self.offscreenBuffer.fill(QWidget.white)
else:
temp = QPixmap(self.offscreenBuffer)
self.offscreenBuffer.resize(ev.size())
self.offscreenBuffer.fill(QWidget.white)
bitBlt(self.offscreenBuffer, 0, 0, temp)
def paintEvent(self, ev):
bitBlt(self, 0, 0, self.offscreenBuffer)
a = QApplication(sys.argv)
a.setFont(QFont("helvetica", 16, QFont.Normal))
a.setDefaultCodec(QTextCodec.codecForName("eucJP"))
w = OeWidget()
a.setMainWidget(w)
w.show()
a.exec_loop()
# end.
|
このサンプルを実行すると、メニューバーをもつ白地のウィンドウが現れ、 マウスの左ボタンを押したままひこずるとその跡に長方形が現れます。 書き方ですが、 Tkのキャンバス、Gtk+のドローイングエリアに当たるウィジェットは QtではQWidgetになります。普通QWidgetは灰地で塗りつぶされますが、 表示前に次のようにしてその機能を止めておきます。
self.setBackgroundMode(QWidget.NoBackground)そんでそんで、ええと、描画に関係するイベントは、 Qtおなじみのシグナルとスロットの機構は使わず、 すべて仮想関数を使います。つまり、QWidgetの「****Event」 という仮想関数をオーバーライドすることで行います。 最低限オーバーライドしておかなくてはいけない仮想関数は resizeEventとpaintEvent の2つです。 resizeEventは、
self.offscreenBuffer = QPixmap(ev.size())
def mouseMoveEvent(self, ev):
pa = QPainter();
pa.begin(self.offscreenBuffer)
if self.px != None:
pen = QPen(QColor("blue"), 4, QWidget.SolidLine)
pa.setPen(pen)
pa.drawRect(ev.x()-3, ev.y()-3, 6, 6)
self.px = ev.x(); self.py = ev.y()
pa.end()
self.repaint()
おおっと、2点ほど注意してください。
QPainterのbeginメソッドで指定する描画対象は、
表示したいQWidgetではなく、隠しバッファのほうです。
また、QPainterのendメソッドで一連の描画が終了したあかつきには、
忘れずにQWidget.repaint()を実行して強制的にpaintEventを発生させるように司令する必要があります。
def paintEvent(self, ev):
bitBlt(self, 0, 0, self.offscreenBuffer)
| ■ | だらだらオプションツアー2 |
#! /usr/local/bin/python
import sys
from qt import *
class DrawingToolManager:
def __init__(self, *args):
self.pen = QPen()
self.brush = QBrush()
self.tool = 'line'
self.width = 4
self.r = 10
class OeWidget(QWidget):
def __init__(self, *args):
apply(QWidget.__init__, (self,) + args)
self.resize(400, 424)
fmenu = QPopupMenu(self)
fmenu.insertItem(self.tr("終了"), self.bye, 0)
self.mb = QMenuBar(self)
self.mb.insertItem(self.tr("ファイル(&F)"), fmenu)
self.mb.setGeometry(0, 0, 400, 24)
self.setBackgroundMode(QWidget.NoBackground)
self.offscreenBuffer = None
self.px = self.py = None
self.dtm = DrawingToolManager()
def bye(self):
self.close()
def setting(self, k):
k = str(k)
# たびたびくどいですが、QStringのインスタンスとPythonの文字列
# は全くの別物なので、== で比べても絶対に真になりません。そこで、
# QStringのインスタンスをstr()関数でPython文字列に変換します。
if k == 'b':
self.dtm.pen.setColor(QColor("blue"))
elif k == 'r':
self.dtm.pen.setColor(QColor(128, 0, 64))
elif k == 'B':
self.dtm.brush.setColor(QColor("blue"))
elif k == 'R':
self.dtm.brush.setColor(QColor(128, 0, 64))
elif k == '2' or k == '3' or k == '4' or k == '5':
self.dtm.pen.setWidth(int(k))
elif k == 's':
self.dtm.pen.setStyle(QWidget.SolidLine)
elif k == 'd':
self.dtm.pen.setStyle(QWidget.DashLine)
elif k == 'o':
self.dtm.pen.setStyle(QWidget.DotLine)
elif k == 'n':
self.dtm.brush.setStyle(QWidget.NoBrush)
elif k == 'f':
self.dtm.brush.setStyle(QWidget.SolidPattern)
elif k == 'h':
self.dtm.brush.setStyle(QWidget.Dense4Pattern)
elif k == 'z':
self.dtm.tool = 'rectangle'
elif k == 'e':
self.dtm.tool = 'ellipse'
elif k == 'w':
self.dtm.tool = 'winfocusrect'
elif k == 'n':
self.dtm.tool = 'roundrect'
elif k == 'a':
self.dtm.tool = 'arc'
elif k == 'p':
self.dtm.tool = 'pie'
elif k == 'c':
self.dtm.tool = 'chord'
elif k == 'y':
self.dtm.tool = 'polygon'
elif k == 't':
self.dtm.tool = 'text'
def keyPressEvent(self, ev):
# keyは単純なキーコード、asciiはASCIIコード
print "key pressed",
if ev.key() != None: print ev.key(),
try:
if ev.text() != None: print ev.text(),
except TypeError:
pass
if ev.ascii() != None: print ev.ascii(),
print ""
self.setting(ev.text())
def mouseReleaseEvent(self, ev):
self.px = ev.pos().x(); self.py = ev.pos().y()
pa = QPainter();
pa.begin(self.offscreenBuffer)
pa.setPen(self.dtm.pen)
pa.setBrush(self.dtm.brush)
r = self.dtm.r
if self.dtm.tool == 'line':
pa.drawLine(self.px, self.py, 200, 200)
elif self.dtm.tool == 'rectangle':
pa.drawRect(self.px-r, self.py-r, r*2, r*2)
elif self.dtm.tool == 'ellipse':
pa.drawEllipse(self.px-r, self.py-r, r*2, r*2)
elif self.dtm.tool == 'winfocusrect':
pa.drawWinFocusRect(self.px-r, self.py-r, r*2, r*2)
elif self.dtm.tool == 'roundrect':
pa.drawRoundRect(self.px-r, self.py-r, r*2, r*2)
elif self.dtm.tool == 'arc':
pa.drawArc(self.px-r, self.py-r, r*2, r*2, 100*16, 160*16)
elif self.dtm.tool == 'pie':
pa.drawPie(self.px-r, self.py-r, r*2, r*2, 100*16, 160*16)
elif self.dtm.tool == 'chord':
pa.drawChord(self.px-r, self.py-r, r*2, r*2, 100*16, 160*16)
elif self.dtm.tool == 'polygon':
arr = QPointArray(4)
points = ( (self.px-r, self.py-r), (self.px-r, self.py+r),
(self.px+r, self.py+r), (self.px, self.py-r) )
for i in range(0, 4):
arr.setPoint(i, points[i][0], points[i][1])
pa.drawPolygon(arr)
elif self.dtm.tool == 'text':
pa.drawText(self.px-r, self.py-r, "ABC")
pa.end()
self.repaint()
def resizeEvent(self, ev):
if self.offscreenBuffer == None:
self.offscreenBuffer = QPixmap(ev.size())
self.offscreenBuffer.fill(QWidget.white)
else:
temp = QPixmap(self.offscreenBuffer)
self.offscreenBuffer.resize(ev.size())
self.offscreenBuffer.fill(QWidget.white)
bitBlt(self.offscreenBuffer, 0, 0, temp)
def paintEvent(self, ev):
bitBlt(self, 0, 0, self.offscreenBuffer)
a = QApplication(sys.argv)
a.setFont(QFont("helvetica", 16, QFont.Normal))
a.setDefaultCodec(QTextCodec.codecForName("eucJP"))
w = OeWidget()
a.setMainWidget(w)
w.show()
a.exec_loop()
# end.
|
図形のこだわりを出してくる前に、キーボードイベントの処理についてメモっておきましょう。 キーが押されたときに何か処理をさせる場合には、 keyPressEventをオーバーライドします。 この関数には引数としてQKeyEventクラスのインスタンスが渡されます。 実際にどのキーが押されたかを特定するために、 典型的にはその3つの関数を使い分けます。
さて、図形の輪郭線の色やスタイルを指定するには、 QPenの関数を使います。
pen = QPen()
pen.setColor(QColor("blue")) # 線を青色に
pen.setColor(QColor(128, 0, 64)) # 線の色をRGBで指定
pen.setWidth(5) # 線の幅をピクセルで指定
pen.setStyle(QWidget.SolidLine) # 線のスタイル。デフォルトはこの実線
pen.setStyle(QWidget.DashLine) # 破線
pen.setStyle(QWidget.DotLine) # ドット打ちじゃ
painter.setPen(pen)
一方、内部を塗りつぶす場合には、その色やパターニングを指定するのに
QBrushの関数どもを使います。
brush = QBrush()
brush.setColor(QColor("blue")) # 青で塗りつぶす
brush.setColor(QColor(128, 0, 64)) # 塗りつぶし色をRGBで指定
brush.setStyle(QWidget.NoBrush) # 塗りつぶしなし剃りのこしなし!
brush.setStyle(QWidget.SolidPattern) # 100%純色たっぷりで塗りつぶせ
brush.setStyle(QWidget.Dense4Pattern) # 濃度4(30%)で塗ってくれ
painter.setBrush(brush)
上の例のDense4Patternのような塗りつぶしの透明度を表す記号定数はいくつか定義されています。
これはQtのマニュアルのQBrushのクラスリファレンスからたどっていくと一覧があるので詳しい説明はそちらでご確認くださいね。
そして最後に、ではQtでどんな図形が描けるのか、というのを紹介していきます。
| ■ | 画像ファイルを読む 足跡をつけて生きる |
#! /usr/local/bin/python
import sys
from qt import *
class OeWidget(QWidget):
def __init__(self, *args):
apply(QWidget.__init__, (self,) + args)
self.resize(300, 324)
self.setCaption(self.tr("足跡をつけて生きる"))
fmenu = QPopupMenu(self)
fmenu.insertItem(self.tr("終了"), self.bye, 0)
self.mb = QMenuBar(self)
self.mb.insertItem(self.tr("ファイル(&F)"), fmenu)
self.mb.setGeometry(0, 0, 300, 24)
self.setBackgroundMode(QWidget.NoBackground)
self.offscreenBuffer = None
self.image = QImage("/usr/share/pixmaps/gnome-logo-icon.png")
def bye(self):
self.close()
def mousePressEvent(self, ev):
pa = QPainter();
pa.begin(self.offscreenBuffer)
pa.drawImage(ev.x(), ev.y(), self.image)
pa.end()
self.repaint()
def resizeEvent(self, ev):
if self.offscreenBuffer == None:
self.offscreenBuffer = QPixmap(ev.size())
self.offscreenBuffer.fill(QWidget.white)
else:
temp = QPixmap(self.offscreenBuffer)
self.offscreenBuffer.resize(ev.size())
self.offscreenBuffer.fill(QWidget.white)
bitBlt(self.offscreenBuffer, 0, 0, temp)
def paintEvent(self, ev):
# 大注意! bitBltは関数です。QWidgetやQPaintDeviceのメンバ関数(メソッド)
# ではないので、修飾は不要です。
bitBlt(self, 0, 0, self.offscreenBuffer)
a = QApplication(sys.argv)
a.setFont(QFont("helvetica", 16, QFont.Normal))
a.setDefaultCodec(QTextCodec.codecForName("eucJP"))
w = OeWidget()
a.setMainWidget(w)
w.show()
a.exec_loop()
# end.
|
Qtで扱う画像は、そのコンストラクタで画像ファイル名を指定したところの
QImageのインスタンスとして作るのが普通です。
使い方はとても簡単、QPainterのdrawImage
関数でその左上の座標とQImageのインスタンスを指定すればOKです。
Qtで読める画像フォーマットは、静的関数
QImage.inputFormats()を使えば確認できます。
通常のインストールでは、
PPM/PGM/PBM、XPM、PNGの各フォーマットは読めるようになっていると思います。
def mousePressEvent(self, ev):
image = QImage("/usr/share/pixmaps/gnome-logo-icon.png")
pa = QPainter();
pa.begin(self.offscreenBuffer)
pa.drawImage(ev.x(), ev.y(), image)
pa.end()
self.repaint()
セクションのサブメニューに戻る
(first uploaded 2001/02/24 last updated 2002/03/21)