MacOS8の導入により、標準のコントロールがたくさん付け加えられました。そのうちのひとつが
ベベルボタンです。これはふつうのボタンのように働きますが、ふつうの角の丸い矩形ではなく、3Dの外観を持った矩形です。これにははっきりとした外観の違いから3つの種類があります。W.Button
のサブクラスを作って、これら新しいコントロールを標準のプッシュボタンの代わりに表示してみましょう。
はじめに、W.Button
クラスを呼び出すためにW
モジュールを、Control Managerツールボックス呼び出すためにCtl
モジュールを、Control Managerの定数を呼び出すためにControls
モジュールをインポートします。また、デフォルトであることを示すリングを描くのにQd
モジュールが、Wbase
モジュールにあるベベルアピアランスを補助するルーチンが必要です。
import W import Ctl import Controls import Qd from Wbase import _darkencolor
はじめに定義するのは標準のベベルボタンで、Button
クラスのサブクラスとします:
class NormalBevelButton(W.Button):
ベベルボタンは種類ごとにそれぞれ固有のコントロール定義で作られます。 他の種類のベベルボタンを作るためにも、この操作を簡単にするためにクラス変数として コントロールID(procID)を定義します。
procID = Controls.kControlBevelButtonNormalBevelProc
たいていの
Button
メソッドは上で作った新しいボタンクラスで働きますが、Button.__init__
によって、コントロールID(procID)はpushButProc
であると決められてしまいます。
それで、これをオーバーライドして正しいコントロールIDを使えるようにします:
def __init__(self, possize, title = "BevelButton", callback = None): W.ControlWidget.__init__(self, possize, title, self.procID, callback, 0, 0, 1) self._isdefault = 0
ボタンがウィンドウのデフォルトボタンなら何が起こるかを変える必要もあります。ふつうはこれをアピアランスマネージャが自動的にしてくれますが、Ctl
モジュールではまだこれが実装されていません。かわりにデフォルトボタンの周りのリングを手書きで描かなければなりません。
これを扱うのはdrawfatframe()
メソッドです。
変更は全く簡単です(複雑に見えますが、それは描画コマンドのせいです):
def drawfatframe(self, onoff): # draw a platinum appearance default box color = (0xe000, 0xe000, 0xe000) (l, t, r, b) = Qd.InsetRect(self._bounds, -4, -4) if onoff: # Draw the frame Qd.FrameRect((l, t, r, b)) Qd.RGBForeColor(color) Qd.PaintRect((l+1, t+1, r-1, b-1)) Qd.RGBForeColor(_darkencolor(color)) Qd.MoveTo(l+2, b-2) Qd.LineTo(r-2, b-2) Qd.LineTo(r-2, t+2) Qd.RGBForeColor(_darkencolor(color)) (l, t, r, b) = Qd.InsetRect(self._bounds, -1, -1) Qd.PaintRect((l, t, r, b)) Qd.RGBForeColor((0, 0, 0)) Qd.RGBForeColor(_darkencolor(color)) Qd.MoveTo(l, b-1) Qd.LineTo(r-1, b-1) Qd.LineTo(r-1, t) else: # Erase the frame Qd.RGBForeColor((0xffff, 0xffff, 0xffff)) Qd.PaintRect((l, t, r, b)) Qd.RGBForeColor((0, 0, 0))
しかし、不精をしてQd.FrameRect
ではなく、Qd.PaintRect
を使って矩形を描きました。こうするとボタンの上に矩形を描きます。これを解決するには、いくつかルーチンを書き直して、デフォルトボタンであることを示すリングを描いた後にボタンを描くようにしなければなりません。
def enable(self, onoff): if self._control and self._enabled != onoff: self._enabled = onoff if self._isdefault and self._visible: self.SetPort() self.drawfatframe(onoff) self._control.HiliteControl((not onoff) and 255) def activate(self, onoff): self._activated = onoff if self._enabled: if self._isdefault and self._visible: self.SetPort() self.drawfatframe(onoff) self._control.HiliteControl((not onoff) and 255) def draw(self, visRgn = None): if self._visible: if self._isdefault and self._activated: self.drawfatframe(self._enabled) self._control.Draw1Control() def _setdefault(self, onoff): self._isdefault = onoff if self._control and self._enabled: self.SetPort() self.drawfatframe(onoff) self.draw()
さあ、これで面倒なところは全部終わりました。残り2つの種類のベベルボタンのクラス定義は 簡単です:
class SmallBevelButton(NormalBevelButton): ProcID = Controls.kControlBevelButtonSmallBevelProc class LargeBevelButton(NormalBevelButton): ProcID = Controls.kControlBevelButtonLargeBevelProc
以上を一つにまとめたまとめたモジュールをbevel.pyで見ることができます。どのように動くか、IDEで"Run as __main__"を設定して実行してみてください。
このボタンクラスを作るには方法がいくつも - 例えばデフォルトのリングを描くときの方法を考慮に入れると - あります。しかし、ここでは独自にウィジェットクラスを作るのに必要とされる手順を説明しました。
(XXX 正しいProcID
を渡したのに、私のコンピュータではSmallBevelButton
とLargeBevelButton
が
NormalBevelButton
と同じに見えます。アピアランスマネージャがどうしたわけか正しく設定していないのでしょうか?)
ControlWidget.__init__()
に渡す変数minvalueを
kControlBehaviorToggles
あるいは
kControlBehaviorSticky
に変えて、それぞれチェックボックスあるいはラジオボタンのようにふるまうようにしましょう。
RadioButton
とCheckBox
のサブクラスを作って、ベベルボタンを作ります。
drawfatframe()
ルーチンを変えてみましょう。