標準のテキストウィジェットでは、ウィジェットの中でひとつのテキストスタイルしか使えません。
これはコードエディタとしてはいいのですが、WASTEには書式設定が組み込まれています。この機能が使えるテキストウィジェットがあったらいいですね。この機能を持ったW.TextEditor
のサブクラスを作りましょう。
まず最初に、いくつか役に立つモジュールをインポートして、それからメニューハンドラで使う2つの定数を定義します。
import W import Wapplication import WASTEconst import waste import FrameWork import Res import Fm import Menu # 訳:スタイルとサイズメニュー。スタイルの順番は重要です(ビット値と結びついています)。 STYLES = [ ("Bold", "B"), ("Italic", "I"), ("Underline", "U"), ("Outline", "O"), ("Shadow", ""), ("Condensed", ""), ("Extended", "") ] SIZES = [ 9, 10, 12, 14, 18, 24]
それでは、メインとなる定義です。ベースクラスとしてW.TextEditor
を使いますが、stylesとsoup変数を通して、テキストとともにスタイルとオブジェクトの情報を渡します。この変更に対処するために、set()
と
get()
メソッドを再定義します。また、open()
と
close()
は新たな情報を扱うということに注意してください。
使い勝手を考えて、settext()
とgettext()
メソッドを再定義し、
スタイル情報を気にせずにテキストを手に入れられるようにします。W.TextEditor
からの変更箇所を赤いハイライトで示します。
class StyledEditor(W.TextEditor): def __init__(self, possize, text = "", styles = None, soup = None, callback=None, wrap=1, inset=(4, 4), fontsettings=None, tabsettings=(32, 0), readonly=0): W.TextEditor.__init__(self, possize, text = "", callback=None, wrap=1, inset=(4, 4), fontsettings=None, tabsettings=(32, 0), readonly=0)
self.tempstyles = styles self.tempsoup = soup
def open(self): if not hasattr(self._parent, "_barx"): self._parent._barx = None if not hasattr(self._parent, "_bary"): self._parent._bary = None self._calcbounds() self.SetPort() viewrect, destrect = self._calctextbounds() flags = self._getflags() self.ted = waste.WENew(destrect, viewrect, flags) self.ted.WEInstallTabHooks() self.setfontsettings(self.fontsettings) self.settabsettings(self.tabsettings)
self.ted.WEInsert(self.temptext, self.tempstyles, self.tempsoup)
self.ted.WECalText() self.ted.WEResetModCount() if self.selection: self.setselection(self.selection[0], self.selection[1]) self.selection = None else: self.selview() self.temptext = None self.tempstyles = None self.tempsoup = None self.updatescrollbars() self.bind("pageup", self.scrollpageup) self.bind("pagedown", self.scrollpagedown) self.bind("top", self.scrolltop) self.bind("bottom", self.scrollbottom) self.selchanged = 0 def close(self):
self.tempstyles = None self.tempsoup = None
W.TextEditor.close(self) def set(self, text, style=None, soup=None): if not self.ted: self.temptext = text self.tempstyles = style self.tempsoup = soup else:
texthandle = Res.Resource(text) self.ted.WESetSelection(0, self.tedWEGetTextLength()) self.ted.WEDelete() self.ted.WEInsert(text, style, soup) self.ted.WESetSelection(0,0) self.ted.WECalText()
self.SetPort() viewrect, destrect = self._calctextbounds() self.ted.WESetViewRect(viewrect) self.ted.WESetDestRect(destrect) rgn = Qd.NewRgn() Qd.RectRgn(rgn, viewrect) Qd.EraseRect(viewrect) self.draw(rgn) self.updatescrollbars() def get(self): if not self.ted: return self.temptext, self.tempstyles, self.tempsoup else:
texthandle = Res.Resource('') styles = Res.Resource('') soup = Res.Resource('') self.ted.WECopyRange(0, self.tedWEGetTextLength(), texthandle, styles, soup)
return text.data, styles, soup def settext(self, text): return W.TextEditor.set(self, text) def gettext(self): return W.TextEditor.get(self)
メニューアイテムその他に反応して選択範囲のテキストのスタイルを設定できるような、広範に使えるルーチンが必要です。
setstyle()
がこの働きをします。
WASTEconst
のwhichがこのためのフラグで、これが
選択したテキストの変更したいスタイルをWASTEに伝えます。
Fontsettingsは標準のタプルで、
WASTEにフォント、スタイル、サイズ、カラーを伝えます。
def setstyle(self, which, fontsettings): self.ted.WESelView() self.ted.WESetStyle(which, fontsettings)
また、_getflags()
メソッドをオーバーライドして、WASTEに書式設定を持ったウィジェットが必要だと知らせます。
def _getflags(self): flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoOutlineHilite if self.readonly: flags = flags | WASTEconst.weDoReadOnly else: flags = flags | WASTEconst.weDoUndo return flags
最後に、もし実行可能な時にはフォント、スタイル、サイズのメニューに引っかかるよう設定する必要があります。これは、domenu_
メソッドがウィジェットの中の選択されたテキストに対するメニューを適切な状態に変え、can_
メソッドが選択範囲にどういうスタイルが設定されているかチェックマークを入れます。
これらが働くようにするためには、フォントメニュー(これには'.'
あるいは'%'
で始まらない全ての'FOND'
リソース - あるいはいくつか適切なサブセット - のリストが必要です);STYLES
変数にリスト化された項目(この順序は重要です)を持つスタイルメニュー;サイズメニュー(SIZES
リストにあるサイズはよい選択ですが、どんな数値が含まれていてもOKです)を作ります。
メニュー項目には全て適切な'setfont'
、'setface'
、'setsize'
のコールバックを持たせた方がいいでしょう。
def domenu_setfont(self, id, item, window, event): text = Menu.GetMenuHandle(id).GetMenuItemText(item) font = W.GetFNum(text) self.setstyle(WASTEconst.weDoFont, (font, 0, 0, (0, 0, 0))) def can_setfont(self, item): any, mode, (font, face, size, color) = self.ted.WEContinuousStyle(WASTEconst.weDoFont) if any and Fm.GetFontName(font) == item.menu.items[item.item-1][0]: item.check(1) else: item.check(0) return 1 def domenu_setface(self, id, item, window, event): face = (1 << (item-1)) self.setstyle(WASTEconst.weDoFace | WASTEconst.weDoToggleFace, (0, face, 0, (0, 0, 0))) def can_setface(self, item): any, mode, (font, face, size, color) = self.ted.WEContinuousStyle(WASTEconst.weDoFace) if any and item.menu.items[item.item-1][0] in getfaces(face): item.check(1) else: item.check(0) return 1 def domenu_setsize(self, id, item, window, event): size = Menu.GetMenuHandle(id).GetMenuItemText(item) self.setstyle(WASTEconst.weDoSize, (0, 0, int(size), (0, 0, 0))) def can_setsize(self, item): any, mode, (font, face, size, color) = self.ted.WEContinuousStyle(WASTEconst.weDoSize) if any and item.menu.items[item.item-1][0] == str(size): item.check(1) else: item.check(0) return 1
最後に、存在しない時にいろいろなメニューを設定するルーチンを定義します。 残念ながら、他の方法でメニューのコピーが得られそうなのに、手書きで作らなければなりません。 このウィジェットを使ったアプリケーションでできそうです。
def MakeFontMenu(menubar, where = 0): app = W.getapplication() fontmenu = Wapplication.Menu(menubar, "Font", where) for font in getfontnames(): item = FrameWork.MenuItem(fontmenu, font, "", "setfont") app._menustocheck.append(item) return fontmenu def MakeFaceMenu(menubar, where = 0): app = W.getapplication() facemenu = Wapplication.Menu(menubar, "Style", where) for face, key in STYLES: item = FrameWork.MenuItem(facemenu, face, key, "setface") app._menustocheck.append(item) return facemenu def MakeSizeMenu(menubar, where=0): app = W.getapplication() sizemenu = Wapplication.Menu(menubar, "Size", where) for size in SIZES: item = FrameWork.MenuItem(sizemenu, str(size), "", "setsize") app._menustocheck.append(item) return sizemenu def MakeNewMenus(): mbar = W.getapplication().menubar MakeFontMenu(mbar) MakeFaceMenu(mbar) MakeSizeMenu(mbar) def getfontnames(): fontnames = [] for i in range(1, Res.CountResources('FOND') + 1): r = Res.GetIndResource('FOND', i) name = r.GetResInfo()[2] if name[0] not in [".", "%"]: fontnames.append(name) fontnames.sort() return fontnames def getfaces(face): faces = [] for i in range(len(STYLES)): if face & (1 << i): faces.append(STYLES[i][0]) return faces
以上を一つにまとめたまとめたモジュールをstylededitor.pyで見ることができます。どのように動くか、IDEで"Run as __main__"を設定して実行してみてください。 IDEでフォント、スタイル、サイズのメニューを得るには、stylededitor.pyをモジュール化して(IDEで"modularize"を設定して)、コンソールウィンドウで以下のようにタイプします:
>>> import stylededitor >>> stylededitor.MakeNewMenus()
理想的には、書式のメニューに他の便利な項目、サイズメニューに"大きく"、"小さく"、"その他"、そしてスタイルメニューには"Plain"のオプションがあればいいでしょう。 また、カラーメニューやテキストを揃えるなどの設定を加えることができるでしょう。 もう少し工夫すれば、このウィジェットをSimpleTextと同等のパワーを持つシンプルなエディタや、 シンプルなHATMLビューアに作り上げることも可能でしょう。
domenu_
とコマンドを加えましょう。
domenu_setface
とcan_setface
を設定しましょう。
STYLES
のリストは順番に現れます(もし練習2を済ませたら、気が付いたでしょう)。もっとちゃんと動くように作り直しましょう。