LauncherEX でサブモードを変更するときに入力エリアが空だったらサブモードを循環させる
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- Python 2.5.1
- CraftLaunchEx 0.991
- LauncherEX 0.0.11
LauncherEX は、デフォルトで Ctrl-. に Shortcut_ChangeSubMode() がバインドされています。
この関数は、コマンド入力エリアの文字列を OPTION_EX_SUBMODE にセットします。
これを、コマンド入力エリアが空のときは、サブモードのリストで現在のモードの次の位置にあるモードを、OPTION_EX_SUBMODE にセットするようにします。
現在のサブモードがリストの末尾だったり、リストにない場合は、リストの先頭にあるモードをセットします。現在のサブモードがリストにない場合は、現在のモードをリストの末尾に追加してからモードを変更します。
クラス LauncherModeEX のメソッド Shortcut_ChangeSubMode を置き換えます。
'_OPTION_EX_SUBMODE_LIST' というオプションを追加します。数値にするとカブる可能性があるので、文字列にしてあります。
このオプションには、サブモードのリストをセットします。リストには、サブモードのファイル名から拡張子を除いた名前をセットします。
デフォルトでは『CraftLaunchEx のあるディレクトリパス\extension\clmode_lex\clsubmode_*.py』のパターンにマッチするファイルがセットされています。
コード
config.py などに記述します。
## LauncherEX import clmode_lex # `from clconst import *' より前に記述する from clapi import * from clconst import * import cloption cloption._set_option_func_table['_OPTION_EX_SUBMODE_LIST'] \ = cloption._SetOption_Simple cloption._get_option_func_table['_OPTION_EX_SUBMODE_LIST'] \ = cloption._GetOption_Simple import glob import os.path SetOption('_OPTION_EX_SUBMODE_LIST', [ os.path.splitext(os.path.split(x)[1])[0] for x in glob.glob('%s*.py' % os.path.join(GetAppDir(), r'extension\clmode_lex\clsubmode_')) ]) def _LauncherModeEXShortcutChangeSubMode(self): u"""サブモードを変更します""" import clcore from clmode_lex import OPTION_EX_SUBMODE s = GetValue() if s: SetOption(OPTION_EX_SUBMODE, s) SetValue('') else: lst = GetOption('_OPTION_EX_SUBMODE_LIST') submode = GetOption(OPTION_EX_SUBMODE) try: idx = (lst.index(submode) + 1) % len(lst) except ValueError: lst.append(submode) idx = 0 SetOption(OPTION_EX_SUBMODE, lst[idx]) clcore.RaiseNextWindow() clmode_lex.clLauncherEX.LauncherModeEX.Shortcut_ChangeSubMode \ = _LauncherModeEXShortcutChangeSubMode
以下のコードは、この関数をテストするためにシャレで作ったサブモード『保冷所』です。
アメリカの TV シリーズ『CSI:マイアミ』の登場人物であるホレイショ・ケインの顔文字『(`●ω●´)』に、台詞を言わせます。
台詞は、ホレイショ名ゼリフTOP10 から引用しました。
# -*- coding: utf-8 -*- u"""clModeLEXSubModeHoracio - 保冷所 This script is sub mode for LauncherEX. Horacio keeps on speaking his lines while CraftLaunchEx is unfocused. """ version = '0.0.0.0' import sys (major, platform) = sys.getwindowsversion()[0:4:3] winNT5OrLater = (platform == 2) and (major >= 5) del sys, major, platform # Not for export import ctypes class c_tchar(ctypes._SimpleCData): if winNT5OrLater: _type_ = 'u' # c_wchar else: _type_ = 'c' # c_char from ctypes.wintypes import BYTE, LONG # WinUser.h WM_GETFONT = 0x0031 if winNT5OrLater: SendMessage = ctypes.windll.user32.SendMessageW else: SendMessage = ctypes.windll.user32.SendMessageA GetDlgItem = ctypes.windll.user32.GetDlgItem GetSystemMetrics = ctypes.windll.user32.GetSystemMetrics GetDC = ctypes.windll.user32.GetDC # WinGDI.h LF_FACESIZE = 32 LOGPIXELSY = 90 class LOGFONT(ctypes.Structure): _fields_ = [ ('lfHeight', LONG), ('lfWidth', LONG), ('lfEscapement', LONG), ('lfOrientation', LONG), ('lfWeight', LONG), ('lfItalic', BYTE), ('lfUnderline', BYTE), ('lfStrikeOut', BYTE), ('lfCharSet', BYTE), ('lfOutPrecision', BYTE), ('lfClipPrecision', BYTE), ('lfQuality', BYTE), ('lfPitchAndFamily', BYTE), ('lfFaceName', c_tchar*LF_FACESIZE) ] GetDeviceCaps = ctypes.windll.gdi32.GetDeviceCaps if winNT5OrLater: GetObject = ctypes.windll.gdi32.GetObjectW else: GetObject = ctypes.windll.gdi32.GetObjectA from clapi import * from clconst import * format = u'(`●ω●´)<%s' oneLiners = [ u'俺は決してお前を許さない。必ずお前を塀の中へブチ込んでやる。分かったか?', u'我々 CSI は決して…そう、決してあきらめない。', u'スピードル、お前のおかげだ。', u'もう行け、いいな? 元気でな…さあ。', u'それが俺の仕事だ。俺は悪を始末する。', u'お前の人生を滅茶苦茶にしてやってもいいんだぞ。', u'復讐は正義ではない、犯罪で正義はなし得ない。', u'必要ない。この現場が語ってくれる。', u'お前みたいな蛆虫を一生ムショにブチ込んでやること、それが俺の究極のスリルだ!', u'私を…敵にしない方がいい。' ] interval = 200 atRandom = False repeatCount = 1 charNum = 11 fontName = u'MS Pゴシック' minWidth = 0 position = None # (x, y) fgColor = None # (R, G, B) bgColor = None # (R, G, B) import clmode import clcore import random class SubModeHoracio(clmode.BaseMode): def __init__(self, prevMode): clmode.BaseMode.__init__(self) self.__PrevMode = prevMode # identifier of the edit control: 1000 self.__PrevFont = _GetFont(GetDlgItem(GetHandle(), 1000)) self.__PrevMinWidth = GetOption(OPTION_EDIT_MIN_WIDTH) self.__PrevFGColor = GetOption(OPTION_FG_COLOR) self.__Format = format self.__OneLiners = oneLiners self.__OneLinersIndex = -1 self.__Interval = interval self.__Random = atRandom self.__RepeatCount = repeatCount if repeatCount > 0 else 1 self.__Counter = self.__RepeatCount self.__CharNum = charNum if charNum > 0 else 1 self.__CharIndex = -self.__CharNum self.__MaxCharIndex = -self.__CharNum self.__FontName = fontName self.__MinWidth = minWidth self.__Position = position self.__FGColor = fgColor self.__BGColor = bgColor def OnGetControl(self): if self.__FontName: SetFont(self.__FontName, *self.__PrevFont[1:]) self.__SetMinWidth() self.__SetPosition() if self.__FGColor: SetFgColor(*self.__FGColor) if self.__BGColor: SetBgColor(*self.__BGColor) SetStatusIndicator(u'保') AddTimerHandler(self.__Interval, self.OnTimer) self.OnTimer() def OnLoseControl(self): RemoveTimerHandler(self.OnTimer) SetValue('') SetFont(*self.__PrevFont) SetOption(OPTION_EDIT_MIN_WIDTH, self.__PrevMinWidth) def OnActivate(self, event): clmode.BaseMode.OnActivate(self, event) if event.active: SetPos(*GetOption(OPTION_ORIGINAL_POSITION)) SetFgColor(*self.__PrevFGColor) PopMode() clmode.Top().OnActivate(event) def OnTimer(self): if not IsActive(): self.__Speak() def __SetMinWidth(self): if self.__MinWidth: n = self.__MinWidth else: hwnd = GetHandle() hEdit = GetDlgItem(hwnd, 1000) lFont = _GetLogFont(hEdit) if lFont: (left, right) = GetWindowRect(hEdit)[0:3:2] n = (max([clcore.Edit_GetTextWidth(x) for x in [self.__Format % x[:self.__CharNum] for x in self.__OneLiners]]) + int(-lFont.lfHeight * 1.5) + left + GetWindowRect(hwnd)[2] - right + GetSystemMetrics(SM_CXEDGE) * 2) else: n = 0 SetOption(OPTION_EDIT_MIN_WIDTH, n) def __SetPosition(self): if self.__Position: (x, y) = self.__Position else: (x, y) = clcore.GetSystemParametersInfo(SPI_GETWORKAREA)[0:4:3] (top, bottom) = GetWindowRect(GetHandle())[1:4:2] y += top - bottom SetPos(x, y) def __SetNextOneLinersIndex(self): if self.__Counter < self.__RepeatCount: self.__Counter += 1 else: if self.__Random: try: self.__OneLinersIndex = random.choice([ i for i in xrange(0, len(self.__OneLiners)) if i != self.__OneLinersIndex ]) except IndexError: self.__OneLinersIndex = 0 else: self.__OneLinersIndex = \ (self.__OneLinersIndex + 1) % len(self.__OneLiners) self.__Counter = 1 def __Speak(self): if self.__CharIndex >= self.__MaxCharIndex: self.__SetNextOneLinersIndex() self.__CharIndex = -self.__CharNum self.__MaxCharIndex = len(self.__OneLiners[self.__OneLinersIndex]) self.__CharIndex += 1 if self.__CharIndex < 0: start = 0 else: start = self.__CharIndex end = self.__CharIndex + self.__CharNum if self.__OneLiners[self.__OneLinersIndex][start:end].endswith(' '): end += 1 self.__CharIndex += 1 SetValue(self.__Format % self.__OneLiners[self.__OneLinersIndex][start:end]) def OnSubmode(): PushMode(SubModeHoracio(clmode.Top())) from ctypes.wintypes import HGDIOBJ, SIZE def _GetLogFont(hwnd): hFont = SendMessage(hwnd, WM_GETFONT, 0, 0) if not hFont: return lFont = LOGFONT() if GetObject(hFont, ctypes.sizeof(LOGFONT), ctypes.byref(lFont)): return lFont def _GetFont(hwnd): lFont = _GetLogFont(hwnd) if lFont: faceName = lFont.lfFaceName if isinstance(faceName, str): faceName = unicode(string, 'mbcs') size = int(float(-lFont.lfHeight) / GetDeviceCaps(GetDC(hwnd), LOGPIXELSY) * 72) return (faceName, size, lFont.lfWeight, lFont.lfItalic)
xml-mode の CraftLaunchEx 用キーワード定義
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- xyzzy 0.2.2.235
- xml-mode 2.092
xml-mode で CraftLaunchEx のコマンド定義ファイル command.xml の要素/属性名に色をつけたり、補完できるようにします。
キーワード定義は CraftLaunchEx 0.991 のものです。
コード
(in-package "xml") (defvar *xml-mode-craftlaunchex-suffix* nil) (defvar *xml-mode-craftlaunchex-keyword-list* '((t "craftlaunch" "command-group" "shellexec" "activate" "activate-or-shellexec" "url" "script" "switch" "@name" "@verb" "@file" "@param" "@directory" "@swmode" "@window-class" "@window-class-re" "@window-title" "@window-title-re" "@url" "@encoding" "@paramlist" "@format" "@mod" ))) (defvar *xml-mode-craftlaunchex-guess-list* (let ((cmdelmlst '("shellexec" "activate" "activate-or-shellexec" "url" "script" "switch" ))) (list (cons "" '("craftlaunch")) (cons "command-group" cmdelmlst) (cons "switch" (remove "switch" cmdelmlst :test #'string=)) ))) (pushnew (list "CraftLaunchEx" nil nil nil nil nil *xml-mode-craftlaunchex-keyword-list* *xml-mode-craftlaunchex-guess-list*) *xml-doctypes* :test #'equal) (in-package "user")
設定例
*xml-mode-craftlaunchex-suffix* に command.xml のパスの正規表現をセットして、*xml-auto-doctype-alist* と *auto-mode-alist* に push します。
(setq xml::*xml-mode-craftlaunchex-suffix* (concat "^" (regexp-quote (merge-pathnames "../CLNCHEX/command.xml" (si:system-root))) "$")) (pushnew (list xml::*xml-mode-craftlaunchex-suffix* nil "CraftLaunchEx") *xml-auto-doctype-alist* :test #'equal) (pushnew (cons xml::*xml-mode-craftlaunchex-suffix* 'xml-mode) *auto-mode-alist* :test 'equal)
コマンドラインオプションを解析する
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- UWSC 4.3d
UWSC で、コマンドラインオプションを解析します。
オプションは、ハイフンで始まって 1 つの文字が続くものにしか対応していません。
コード
OPTION EXPLICIT // コマンドラインオプションを解析する // // 引数 : argv - 配列 [入力] 解析対象の配列 // 引数 : optstr - 文字列 [入力] 認識させたいオプション文字の集合 // 引数が必要な場合には文字の後ろに `:' を付ける // 引数 : opthash - 連想配列 [入力/出力] キーにオプション、値に引数をセットした // 連想配列がセットされる // オプションに引数がない場合は値に空文字がセットされる // 引数 : argary - 配列 [入力/出力] 解析対象のオプション以外の要素がセットされる // 引数 : opterr - 真偽値 [入力] TRUE を指定すると、エラーが発生した場合にメッ // セージボックスを出し、終了コード 1 で直ちにスクリプトを終了する // エラーになるのは以下の場合 // * 認識できないオプションがあった // * 引数が必要なオプションに引数が与えられなかった // FALSE を指定するとメッセージボックスを出さず、また、スクリプトも終了 // せずに処理を続ける // 省略時は TRUE を指定したものとして動作する // // `-' と `--' はオプションとみなされない。 // `--' 以降は解析対象から外され、`--' より後は全て argary にセットされる。 // 同じオプションは後のもので上書きされる。 // エラーが発生したオプションは、opthash と argary のどちらにもセットされない。 // 大文字小文字を区別したいときは `OPTION SAMESTR' を指定し、連想配列を宣言する // ときに `HASH_CASECARE' を指定する。 PROCEDURE getopt(argv[], optstr, VAR opthash[], VAR argary[], opterr=TRUE) DIM argc, n, i, j, isstop RESIZE(argary, -1) argc = LENGTH(argv) n = LENGTH(optstr) i = 0 // argv j = 0 // argary isstop = FALSE WHILE i < argc IFB isstop THEN // 解析中止 RESIZE(argary, j) argary[j] = argv[i] j = j + 1 ELSEIF (argv[i] = "--") THEN isstop = TRUE ELSE DIM k = 1 // optstr WHILE k <= n DIM c, argflg c = COPY(optstr, k, 1) argflg = FALSE IFB COPY(optstr, k+1, 1) = ":" THEN argflg = TRUE k = k + 1 ENDIF IFB argv[i] = ("-" + c) THEN IFB argflg THEN // 引数が必要なオプション IFB argc - i = 1 THEN IFB opterr THEN MSGBOX(argv[i] + ": 引数が必要") EXITEXIT 1 ENDIF BREAK ELSEIF (argv[i+1] <> "-") AND (POS("-", argv[i+1]) = 1) THEN IFB opterr THEN MSGBOX(argv[i] + ": 引数が必要") EXITEXIT 1 ENDIF BREAK ENDIF opthash[argv[i]] = argv[i+1] i = i + 1 BREAK ENDIF opthash[argv[i]] = "" BREAK ELSEIF k = n THEN // optstr が最後までイった IFB (argv[i] <> "-") AND (POS("-", argv[i]) = 1) _ AND (LENGTH(argv[i]) = 2) AND !CHKNUM(argv[i]) THEN IFB opterr THEN MSGBOX(argv[i] + ": 無効なオプション") EXITEXIT 1 ENDIF BREAK ELSE RESIZE(argary, j) argary[j] = argv[i] j = j + 1 ENDIF ENDIF k = k + 1 WEND ENDIF i = i + 1 WEND FEND
使用例
上記のコードを『_getopt.uws』というファイル名で保存したとします。
// test.uws: テスト OPTION EXPLICIT OPTION LOGFILE = 2 CALL _getopt.uws HASHTBL opthash DIM argary[0], i getopt(PARAM_STR, "a:b", opthash, argary) IF opthash["-a", HASH_EXISTS] THEN PRINT "-a: "+opthash["-a"] IF opthash["-b", HASH_EXISTS] THEN PRINT "-b: "+opthash["-b"] FOR i=0 TO LENGTH(argary)-1 PRINT argary[i] NEXT
という内容のファイル『test.uws』を作成して、
UWSC test.uws -a aaa -b ccc d e f
というコマンドラインを実行すると、ログファイルに以下の文字列が印字されます。
-a: aaa -b: ccc d e f
HTML ヘルプを表示する
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- Python 2.5.1
- CraftLaunchEx 0.991
CraftLaunchEx で、HTML Help API を使用して HTML ヘルプを表示します。
コード
config.py などに記述します。
Windows 2000/XP では Unicode 版 (HtmlHelpW) を使用し、2000/XP 以外では ANSI 版 (HtmlHelpA) を使用します。
def _IsWinNT5OrLater(): import sys (major, platform) = sys.getwindowsversion()[0:4:3] # VER_PLATFORM_WIN32_NT: 2 return ((platform == 2) and (major >= 5)) winNT5OrLater = _IsWinNT5OrLater() def _T(string): if isinstance(string, str): string = unicode(string, 'utf-8') if not winNT5OrLater: return string.encode('mbcs') return string import ctypes class c_tchar_p(ctypes._SimpleCData): if winNT5OrLater: _type_ = 'Z' # c_wchar_p else: _type_ = 'z' # c_char_p # WinUser.h GetDesktopWindow = ctypes.windll.user32.GetDesktopWindow # HtmlHelp.h HH_DISPLAY_TOC = 0x0001 HH_KEYWORD_LOOKUP = 0x000D HH_CLOSE_ALL = 0x0012 from ctypes.wintypes import BOOL class HH_AKLINK(ctypes.Structure): _fields_ = [ ('cbStruct', ctypes.c_int), # sizeof this structure ('fReserved', BOOL), # must be FALSE (really!) ('pszKeywords', c_tchar_p), # semi-colon separated keywords ('pszUrl', c_tchar_p), # URL to jump to if no keywords found (may be NULL) ('pszMsgText', c_tchar_p), # Message text to display in MessageBox if pszUrl is NULL and no keyword match ('pszMsgTitle', c_tchar_p), # Message text to display in MessageBox if pszUrl is NULL and no keyword match ('pszWindow', c_tchar_p), # Window to display URL in ('fIndexOnFail', BOOL) # Displays index if keyword lookup fails. ] if winNT5OrLater: HtmlHelp = ctypes.windll.LoadLibrary('hhctrl.ocx').HtmlHelpW else: HtmlHelp = ctypes.windll.LoadLibrary('hhctrl.ocx').HtmlHelpA def HtmlHelpDisplayTOC(chmPath, data=None): u"""指定されたヘルプウィンドウでヘルプトピックを開く 引数 : chmPath - 文字列 - コンパイル済みヘルプまたはコンパイル済みヘルプファイ ル中のトピック 引数 : data - 数値 - コンパイル済みヘルプファイル中のトピックへのポインタ 戻り値: ヘルプウィンドウのハンドルを返す""" return HtmlHelp(GetDesktopWindow(), chmPath, HH_DISPLAY_TOC, data) def HtmlHelpKeywordLookup(chmPath, kwd): u"""コンパイル済みヘルプファイルからキーワードを検索する 引数 : chmPath - 文字列 - コンパイル済みヘルプファイル 引数 : kwd - 文字列 - 検索するキーワード 複数の項目はセミコロン `;' で区切る 戻り値: ヘルプウィンドウのハンドルを返す""" aklnk = HH_AKLINK(ctypes.sizeof(HH_AKLINK), False, kwd, None, None, None, None, True) return HtmlHelp(GetDesktopWindow(), chmPath, HH_KEYWORD_LOOKUP, ctypes.byref(aklnk)) def HtmlHelpCloseAll(): u"""呼び出しプログラムによって開かれたヘルプウィンドウをすべて閉じる 戻り値: なし""" HtmlHelp(None, None, HH_CLOSE_ALL, 0)
コマンド定義の例
『CraftLaunchEx Help』を表示します。引数があればキーワード検索します。
Control と Shift を押しながら実行すると、ヘルプウィンドウを閉じます。
<switch name='Help'> <script mod='' paramlist='*args' format=''> <![CDATA[ import os.path hwnd = FindWindow('HH Parent', 'CraftLaunchEx Help') chm = _T(os.path.join(GetAppDir(), 'clnch.chm')) if hwnd: SetForegroundWindow(hwnd) else: HtmlHelpDisplayTOC(chm + _T('::/chapter-01.html#%82%CD%82%B6%82%DF%82%C9')) if len(args): HtmlHelpKeywordLookup(chm, _T(args[0])) ]]> </script> <script mod='CS' paramlist='' format=''> <![CDATA[ HtmlHelpCloseAll() ]]> </script> </switch>
最初の script 要素の paramlist 属性値が『*args』となっているのは、
config.py に以下を書いてみたら、paramlist に *args とか書いて
余分な引数をタプルとして受け取れるようにできたよdef MyCmdScriptCall(self, mod, *args): if len(args) < len(self.format): raise CommandFailedException, 'parameter num mismatch.' param_objects = [] for (f, a) in zip(self.format, args): if f == 's': param_objects.append(a) elif f == 'i': param_objects.append(int(a)) elif f == 'f': param_objects.append(float(a)) param_objects.extend( args[len(self.format):] ) self.func( *param_objects ) CmdScript.__call__ = MyCmdScriptCallhttp://pc11.2ch.net/test/read.cgi/software/1118294183/533
で、引数をタプルで取得できるようにしているからです。
_T() は VC++ の _T マクロみたいなものです。Windows 2000/XP なら unicode に、2000/XP 以外なら mbcs にエンコードします。
CraftLaunchEx のウィンドウを移動する
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- Python 2.5.1
- CraftLaunchEx 0.991
CraftLaunchEx のウィンドウを移動させます。
フォーカスを失えば、初期位置に戻ります。
キーバインドと移動する方向は以下。
キー | 方向 |
---|---|
Alt-Shift-Up | 上 |
Alt-Shift-Right | 右 |
Alt-Shift-Down | 下 |
Alt-Shift-Left | 左 |
ハンドラの仮引数 n のデフォルト値をお好みの数値に変更して下さい。
デフォルトでは 10px ずつ移動します。
コード
config.py などに記述します。
from clapi import * from clconst import * def MoveCLnchExWindow(x, y): u"""CraftLaunchEx のウィンドウを移動する 引数 : x - 整数 - X 座標に加算するピクセル数 引数 : y - 整数 - Y 座標に加算するピクセル数 戻り値: なし""" (left, top) = GetWindowRect(GetHandle())[0:2] SetPos(left+x, top+y) def _MoveUpCLnchExWindow(n=10): u"""CraftLaunchEx のウィンドウを上へ移動する""" MoveCLnchExWindow(0, -n) SetKeyDownHandler(VK_UP, MODKEY_ALT | MODKEY_SHIFT, _MoveUpCLnchExWindow) def _MoveRightCLnchExWindow(n=10): u"""CraftLaunchEx のウィンドウを右へ移動する""" MoveCLnchExWindow(n, 0) SetKeyDownHandler(VK_RIGHT, MODKEY_ALT | MODKEY_SHIFT, _MoveRightCLnchExWindow) def _MoveDownCLnchExWindow(n=10): u"""CraftLaunchEx のウィンドウを下へ移動する""" MoveCLnchExWindow(0, n) SetKeyDownHandler(VK_DOWN, MODKEY_ALT | MODKEY_SHIFT, _MoveDownCLnchExWindow) def _MoveLeftCLnchExWindow(n=10): u"""CraftLaunchEx のウィンドウを左へ移動する""" MoveCLnchExWindow(-n, 0) SetKeyDownHandler(VK_LEFT, MODKEY_ALT | MODKEY_SHIFT, _MoveLeftCLnchExWindow)
LauncherEX で最後に実行したコマンドを実行する
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- Python 2.5.1
- CraftLaunchEx 0.991
- LauncherEX 0.0.10
LauncherEX をモード内のみで有効なキーバインドを設定できるようにする - anonymous苦労者 のコンストラクタ置き換えをしているものとします。
LauncherEX 0.0.11 以降は、コンストラクタの書き換えは不要になりました。
LauncherEXで、コマンド実行時に _SetLastCommand() で、インスタンス変数 _LastCommand に (コマンド名, モディファイアキー) のタプルを記録し、_RepeatLastCommand() で変数 _LastCommand を Execute() します。
_RepeatLastCommand() で AttributeError を握り潰しているのは、関数実行時に変数が存在しない場合があるからです。
コード
config.py などに記述します。
## LauncherEX import clmode_lex # `from clconst import *' より前に記述する from clapi import * from clconst import * # コマンドを記録する def _SetLastCommand(event): import clmode clmode.Top()._LastCommand = (event.str, event.mod) def _RepeatLastCommand(): u"""最後に実行したコマンドを実行する""" import clmode mode = clmode.Top() assert isinstance(mode, clmode_lex.clLauncherEX.LauncherModeEX) try: Execute(*mode._LastCommand) except AttributeError: pass # 初期化時 def _LEXHookOnInit(event): event.keys.update({ (ord('I'), MODKEY_ALT) : _RepeatLastCommand }) clmode_lex.AddHook(clmode_lex.lex_hook_on_init, _LEXHookOnInit) PopMode() PushMode(clmode_lex.clLauncherEX.LauncherModeEX()) # コマンド実行前 clmode_lex.AddHook(clmode_lex.lex_hook_on_before_command, _SetLastCommand) # コマンド実行後 clmode_lex.AddHook(clmode_lex.lex_hook_on_after_command, _SetLastCommand)
LauncherEX をモード内のみで有効なキーバインドを設定できるようにする
LauncherEX 0.0.11 以降は、コンストラクタの書き換えは不要になりました。
以下の環境で動作確認しました。
- Windows XP Home Edition SP2
- Python 2.5.1
- CraftLaunchEx 0.991
- LauncherEX 0.0.10
LauncherEX は CraftLaunchEx の LauncherMode を拡張するスクリプトです。
これをモード内のみで有効なキーバインドを設定できるようにします。
LauncherEX のコンストラクタを置き換えます。
lex_hook_on_init のフック関数で、引数 event の属性 keys にアクセスして、キーバインドとハンドラを設定します。
keys は『{ (仮想キーコード, モディファイアキー) : ハンドラ }』という辞書です。
コード
config.py などに記述します。
## LauncherEX import clmode_lex # `from clconst import *' より前に記述する from clapi import * from clconst import * # コンストラクタ def _LauncherModeEXInit(self): import clmode_launcher clmode_launcher.LauncherMode.__init__(self) self.cmd = '' self.sel = None import clevent event = clevent.Event() # 0xBE: VK_OEM_PERIOD # { (仮想キーコード, モディファイアキー) : ハンドラ } event.keys = { (0xBE, MODKEY_CTRL) : self.Shortcut_ChangeSubMode } err = clmode_lex.RunHook(clmode_lex.lex_hook_on_init, event) if err: raise err for (k, v) in event.keys.iteritems(): self.SetKeyDownHandler(k[0], k[1], v) clmode_lex.clLauncherEX.LauncherModeEX.__init__ = _LauncherModeEXInit def _Hoge(): print 'hoge' # 初期化時 def _LEXHookOnInit(event): event.keys.update({ # Alt-h に _Hoge() をバインド (ord('H'), MODKEY_ALT) : _Hoge }) clmode_lex.AddHook(clmode_lex.lex_hook_on_init, _LEXHookOnInit) # lex_hook_on_init を発火させるには PopMode() して PushMode() PopMode() PushMode(clmode_lex.clLauncherEX.LauncherModeEX())