ファイルコピーツールのオプション管理クラス
今日は、最近ちくちくと作っているツールの動作オプションを管理するクラスを作りました。
とはいっても、オプションを管理するほど多機能ではないんですけどね。(笑
とりあえず、ソースコードはこんな感じです。
# -*- coding: utf-8 -*- import types import xml.sax from xml.dom.minidom import Document from xml.sax.handler import ContentHandler PREF_KEY_SAVE_WINDW_LAYOUT = 'save_window_layout' PREF_KEY_WINDOW_POS = 'window_pos' PREF_KEY_WINDOW_SIZE = 'window_size' PREF_KEY_DEFAULT_DIRECTORY = 'default_dir' PREF_KEY_KEEP_FILE_DIRECTORY = 'keep_file_directory' PREF_KEY_VIEW_TREE_EXPANDED = 'expanded_tree' PREF_KEY_VIEW_DRAW_TREE_LINE = 'draw_tree_line' PREF_KEY_VIEW_SHOW_TOOLBAR = 'show_toolbar' PREF_KEY_LIST_FILTER_EXTENSIONS = 'filter_extentions' PREF_KEY_TYPE = 't' PREF_KEY_VALUE = 'v' ELEMENT_NAME_ROOT = 'fcp_prefs' ELEMENT_NAME_KEY = 'pref_key' ELEMENT_ATTR_VALUE_TYPE = 'type' ELEMENT_ATTR_VALUE = 'val' PREF_VAL_TYPE_BOOLEAN = 'BOOLEAN' PREF_VAL_TYPE_INT = 'INT' PREF_VAL_TYPE_STRING = 'STRING' PREF_VAL_TYPE_TUPLE = 'TUPLE' PREF_VAL_TYPE_LIST = 'LIST' # # class FCPPrefGenerator: ''' プリファレンスデータをXMLに変換するクラス ''' def __init__(self): ''' 初期化 ''' # XML DOMドキュメントを生成し、 # rootとなるタグを子供として追加しておく。 self.doc = Document() self.root = self.doc.createElement(ELEMENT_NAME_ROOT) self.doc.appendChild(self.root) # プリファレンスの設定項目を文字列に変換するための # 関数辞書。 self.genFuncDict = { PREF_VAL_TYPE_BOOLEAN: lambda a: str(a), PREF_VAL_TYPE_TUPLE: lambda a: self.seq_to_str(a), PREF_VAL_TYPE_LIST: lambda a: self.seq_to_str(a), PREF_VAL_TYPE_STRING: lambda a: a, PREF_VAL_TYPE_INT: lambda a: str(a)} def __del__(self): ''' 破棄 ''' self.doc.unlink() def seq_to_str(self, seq): ''' シーケンスの要素をカンマ区切りの文字列に変換する ''' if seq == None or len(seq) == 0: return '' return reduce(lambda a, b: str(a)+','+str(b), seq) def generate(self, prefDict, encode='utf-8'): ''' XMLを生成する ''' for k, vdict in prefDict.iteritems(): # プリファレンス設定項目ごとのタグを作成 elm = self.doc.createElement(k) # 値のタイプを属性に追加 valType = vdict[PREF_KEY_TYPE] elm.setAttribute(ELEMENT_ATTR_VALUE_TYPE, valType) # 値を属性に追加 val = vdict[PREF_KEY_VALUE] elm.setAttribute(ELEMENT_ATTR_VALUE, self.genFuncDict[valType](val).encode('utf-8')) self.root.appendChild(elm) # xmlテキストを生成 return self.doc.toprettyxml(encoding=encode) # # class FCPPrefHandler(ContentHandler): ''' XMLからプリファレンスを復元するXMLパーサ用ハンドラ ''' def __init__(self): ''' 初期化 ''' ContentHandler.__init__(self) # 展開結果を保持するための辞書 self.prefDict = {} # 設定項目名(XMLタグ名)リスト # このリストに登録されていないタグは無視。 self.tagNames = [PREF_KEY_SAVE_WINDW_LAYOUT, PREF_KEY_WINDOW_POS, PREF_KEY_WINDOW_SIZE, PREF_KEY_DEFAULT_DIRECTORY, PREF_KEY_KEEP_FILE_DIRECTORY, PREF_KEY_VIEW_TREE_EXPANDED, PREF_KEY_VIEW_DRAW_TREE_LINE, PREF_KEY_VIEW_SHOW_TOOLBAR, PREF_KEY_LIST_FILTER_EXTENSIONS] # XMLの属性(文字列)から設定項目ごとのデータ型に # 変換するための関数辞書 self.convFuncDict = {PREF_VAL_TYPE_INT: lambda a: a, PREF_VAL_TYPE_STRING: lambda a: a, PREF_VAL_TYPE_BOOLEAN: lambda a: {'True': True, 'False': False}[a], PREF_VAL_TYPE_TUPLE: lambda a: self.str_to_seq(a, types.TupleType), PREF_VAL_TYPE_LIST: lambda a: self.str_to_seq(a, types.ListType)} def str_to_seq(self, s, type): ''' カンマ区切りの文字列をリストまたはタプルに変換する。 ''' if s == None or len(s) == 0: return # カンマで区切った文字列を整数値のリストに変換 # 現状では整数のタプル以外は想定していない。 valList = [int(token) for token in s.split(',')] if type == types.ListType: return valList elif type == types.TupleType: return tuple(valList) return None def get_pref_dict(self): ''' パース処理結果を取得 ''' return self.prefDict def startElement(self, name, attrs): ''' 開始タグが出現するたびにパーサから呼び出される関数 ''' if name not in self.tagNames: return # タグの属性(値のタイプ、値)を文字列として取得 typeStr = attrs.getValue(ELEMENT_ATTR_VALUE_TYPE) val = attrs.getValue(ELEMENT_ATTR_VALUE) if typeStr in self.convFuncDict: # 値のタイプをもとに変換関数辞書で値を変換し、変換結果保持用 # の辞書に追加する。 self.prefDict[name] = {PREF_KEY_TYPE: typeStr, PREF_KEY_VALUE: self.convFuncDict[typeStr](val)} # # class FCPPreference: ''' pyFCPの動作オプションを保持するクラス。 ''' def __init__(self): ''' 初期化 ''' # プリファレンスデータは辞書として保持される。 # 各設定項目は以下の形となっている。 # {設定項目名: {値タイプキー: 値タイプ, 値キー: 値}} self.prefDict = { # ウインドウの座標、サイズを保存するかどうか PREF_KEY_SAVE_WINDW_LAYOUT: {PREF_KEY_TYPE: PREF_VAL_TYPE_BOOLEAN, PREF_KEY_VALUE: True}, # ウインドウ左肩座標 PREF_KEY_WINDOW_POS: {PREF_KEY_TYPE: PREF_VAL_TYPE_TUPLE, PREF_KEY_VALUE: (0, 0)}, # ウインドウサイズ PREF_KEY_WINDOW_SIZE: {PREF_KEY_TYPE: PREF_VAL_TYPE_TUPLE, PREF_KEY_VALUE: (500, 400)}, # ファイル選択ダイアログのデフォルトディレクトリ PREF_KEY_DEFAULT_DIRECTORY: {PREF_KEY_TYPE: PREF_VAL_TYPE_STRING, PREF_KEY_VALUE: './'}, # ファイル選択ダイアログのディレクトリを保持 PREF_KEY_KEEP_FILE_DIRECTORY: {PREF_KEY_TYPE: PREF_VAL_TYPE_BOOLEAN, PREF_KEY_VALUE: True}, # ツリーを常に展開状態にするかどうか PREF_KEY_VIEW_TREE_EXPANDED: {PREF_KEY_TYPE: PREF_VAL_TYPE_BOOLEAN, PREF_KEY_VALUE: True}, # ツリーにラインを描画するかどうか PREF_KEY_VIEW_DRAW_TREE_LINE: {PREF_KEY_TYPE: PREF_VAL_TYPE_BOOLEAN, PREF_KEY_VALUE: True}, # ツールバーを表示するかどうか PREF_KEY_VIEW_SHOW_TOOLBAR: {PREF_KEY_TYPE: PREF_VAL_TYPE_BOOLEAN, PREF_KEY_VALUE: False}, # フィルタ用拡張子リスト PREF_KEY_LIST_FILTER_EXTENSIONS: {PREF_KEY_TYPE: PREF_VAL_TYPE_LIST, PREF_KEY_VALUE: }} def get_value(self, key): ''' 設定値の取得 ''' if key not in self.prefDict: return None return self.prefDict[key][PREF_KEY_VALUE] def set_value(self, key, value): ''' 値の設定 ''' if key not in self.prefDict: print 'unknown key' return typeStr = { types.IntType: PREF_VAL_TYPE_INT, types.StringType: PREF_VAL_TYPE_STRING, types.BooleanType: PREF_VAL_TYPE_BOOLEAN, types.TupleType: PREF_VAL_TYPE_TUPLE, types.ListType: PREF_VAL_TYPE_LIST }[type(value)] # 渡された値の型をチェック(不要かもしれないけど) if typeStr != self.prefDict[key][PREF_KEY_TYPE]: print 'value type is incorrect' return self.prefDict[key][PREF_KEY_VALUE] = value def set_xml_text(self, text): ''' XMLから設定値の読み込み ''' parser = xml.sax.make_parser() handler = FCPPrefHandler() parser.setContentHandler(handler) try: parser.feed(text) except KeyError, err: print 'xml.sax KeyError : %s' % err.message self.prefDict = handler.get_pref_dict() def get_xml_text(self): ''' 設定値をXMLテキストとして書き出す ''' gen = FCPPrefGenerator() text = gen.generate(self.prefDict) return text # # 以下はテスト用コード # # def test_fcp_preference(): ''' プリファレンス生成テスト ''' pref = FCPPreference() keyList = [(PREF_KEY_WINDOW_POS, (10, 10)), (PREF_KEY_WINDOW_SIZE, (100, 100)), (PREF_KEY_DEFAULT_DIRECTORY, '../../../'), (PREF_KEY_KEEP_FILE_DIRECTORY, True), (PREF_KEY_VIEW_TREE_EXPANDED, False), (PREF_KEY_VIEW_DRAW_TREE_LINE, False), (PREF_KEY_VIEW_SHOW_TOOLBAR, True), (PREF_KEY_LIST_FILTER_EXTENSIONS, ['.aaa', '.bbb'])] for k, v in keyList: print pref.get_value(k) for k, v in keyList: pref.set_value(k, v) for k, v in keyList: print pref.get_value(k) # # def test_fcp_pref_xml(): ''' プリファレンスのデータをXMLへ変換するテスト ''' pref = FCPPreference() gen = FCPPrefGenerator() xmlText = gen.generate(pref.prefDict) print xmlText return xmlText # # def test_fcp_pref_handler(text): ''' XMLからぷリファレンスデータを復元するテスト ''' parser = xml.sax.make_parser() handler = FCPPrefHandler() parser.setContentHandler(handler) try: parser.feed(text) prefDict = handler.get_pref_dict() for k, v in prefDict.iteritems(): print k, v except KeyError, err: print 'xml.sax KeyError : %s' % err.message # # if __name__ == '__main__': import xml.sax test_fcp_preference() xmlText = test_fcp_pref_xml() test_fcp_pref_handler(xmlText)
上のソースコードは3つのクラスを含んでいます。
FCPPreference : オプション情報を保持させるためのクラス
FCPPrefHandler: XMLを読み込んでオプション情報を復元するためのクラス
FCPPrefGenerator: オプション情報をXMLテキストに変換するためのクラス
オプション情報→XML にはDOMのモジュールを使用し、
XML→オプション情報 にはSAXモジュールを使用しています。
今回はlambda式を沢山使っています。(あとで見直したときに、なんだっけね。。とならないようにせねば(笑))
ソースはあまりきれいではないかもしれませんが、今回は結構すっきり書けました。
設定項目が増えたりしても簡単に追加できそうです。
つづく。