GtkSharp TreeView Tutorial – Mono
まだ SeeMe for Linux を GTK# で作っていた頃の参考にしたトコ。
よく見ると自作クラスを型として GtkListStore を作成しているコードがあった。
Controlling how the model is used 以下のところね。
こういう型指定って PyGtk でもできるのかな?
可能であればもっと本体のコード量を減らすことができるんだが。
とりあえず Python で同じように SeeMe コードを書きかえてみた。
class Engine(): def __init__(self, deleted=True, name="New Item", key="", url="", query="", post=False, endsp=False, encode="utf8", stype=0, pos=0, nameid=0, verb=0, icon="", unique="", usetld=0): self.delete = deleted self.name = name self.key = key self.url = url self.query = query self.post = post self.endsep = endsp self.encode = encode self.stype = stype self.pos = pos self.nameid = nameid self.verb = verb self.icon = icon self.unique = unique self.usetld = usetld class SeeMe4(gtk.Window): def __init__(self, sset): gtk.Window.__init__(self) #... #self.default_liststore = gtk.ListStore(bool, str, str, str, str, bool, bool, str, int, int, int, int, str, str, int) self.custome_liststore = gtk.ListStore(Engine)
型指定の時点で駄目ジャン…
property にしてみたり小細工してみたりしたけど無駄な努力だった。
Python では変数宣言しただけでは型が決まっていないので当然なのかも。
ま、公式の Constructor 解説には C 言語と同じ型を全部書く方法しか書かれていない。
できないことは素直に諦めて、せめて GtkListStore への append をもう少し簡単にやれないか?
てか、そうしておかないと後々のメンテで沢山書き換えを行わなければいけなくなる。
後で書き換えが必要だろう箇所が少なければ少ないほどミスが減るのよね。
というかソレがオブジェクト指向最大のメリットなのだし。
よく考えたら SeeMe で GtkListStore の型は一つしか無い。
だったら GtkListStore サブクラスを作ってコンストラクタでとっとと型を指定。
ついでに Engine クラスを受け取る add メソッドを作れば簡単になるかな?
class Engine(): def __init__(self, deleted=True, name="New Item", key="", url="", query="", post=False, endsp=False, encode="utf8", stype=0, pos=0, nameid=0, verb=0, icon="", unique="", usetld=0): self.delete = deleted self.name = name self.key = key self.url = url self.query = query self.post = post self.endsep = endsp self.encode = encode self.stype = stype self.pos = pos self.nameid = nameid self.verb = verb self.icon = icon self.unique = unique self.usetld = usetld class CreateListStore(gtk.ListStore): def __init__(self): gtk.ListStore.__init__(self, bool, str, str, str, str, bool, bool, str, int, int, int, int, str, str, int) def add(self, en): return self.append( [en.delete, en.name, en.key, en.url, en.query, en.post, en.endsep, en.encode, en.stype, en.pos, en.nameid, en.verb, en.icon, en.unique, en.usetld] ) class SeeMe4(gtk.Window): def __init__(self, sset): gtk.Window.__init__(self) #... self.custome_liststore = CreateListStore() #... def read_default_searchini(self): self.default_liststore.clear() inipath = self.sset.default_path lngpath = self.sset.lang_path if os.path.exists(inipath): ini = inifile8.Inifile(inipath) lng = inifile8.InifileReader(lngpath) try: self.iniver = ini.read_int("Version", "File Version", 0) i = 0 while 1: i += 1 s = "Search Engine %i" % i if not ini.section_exists(s): break t = ini.read_str(s, "Key", "") if t == "": continue en = Engine(); en.nameid = ini.read_int(s, "Nameid", 0); if en.nameid != 0: # Get View Name from *.lng if en.nameid == 17171: en.name = lng.read_str("Translation", "1632215285", "") elif en.nameid == 17183: en.name = lng.read_str("Translation", "-1971470391", "") elif en.nameid == 71103: en.name = lng.read_str("Translation", "-1453429782", "") else: en.name = lng.read_str("Translation", str(en.nameid), "") if en.name == "": n = -1752227277 - en.nameid; en.name = lng.read_str("Translation", str(n), "") else: en.name = ini.read_str(s, "Name", "") en.key = t en.url = ini.read_str(s, "URL", "") en.query = ini.read_str(s, "Query", "") en.post = ini.read_bool(s, "Is post", False) en.endsep = ini.read_bool(s, "Has endseparator", False) en.encode = ini.read_str(s, "Encoding", "utf-8") en.stype = ini.read_int(s, "Search Type", 0) en.pos = ini.read_int(s, "Position", -1) en.verb = ini.read_int(s, "Verbtext", 0) en.delete = True #! en.unique = ini.read_str(s, "UNIQUEID", "") en.icon = ini.read_str(s, "ICON", "") en.usetld = ini.read_int(s, "UseTLD", 0) """self.default_liststore.append( [ en.delete, en.name, en.key, en.url, en.query, en.post, en.endsep, en.encode, en.type, en.pos, en.nameid, en.verb, en.icon, en.unique, en.usetld] )""" self.default_liststore.add(en) def on_item_new(self, widget, event=None): uid = self.create_uuid() num = self.deditor.get_radio_num() if num == 2: num = -1988219522 else: num = 0 en = Engine(verb=num, unique= uid) #it = self.custome_liststore.append( [True, "", "", "", "", False, False, "utf-8", 0, -1, 0, num, "", uid, 0] ) it = self.custome_liststore.add(en)
コードは全然短くならないけど Engine を直で渡して展開できるようになった。
デフォルト引数によってアトリビュートの型も大半が気にしないでいいという。
読み込みや新規アイテムで順番を気にする必要が無くなったのはデカい。
これなら後々で順番の入れ替えや追加の必要があっても型側で調節できる。
ただこのコードでは展開するために for ループにすると
for row in self.default_liststore: if row[0] == False: i += 1 s = "Search Engine %i" % i ini.write_str(s, "UNIQUEID", row[13])
添字アクセスにするしか無いんだなぁこれが、意味ネェ。
イテレーターは整数による順番という概念を使わないことに意義があるのだが。
この部分については WPF は徹底的にやっている感じ、よく考えて作られているよ。
ここをアトリビュートで取り出すサブクラスをどうすれば作れるか考え中。
ということで重複キーチェック追加や不具合修正で Linux 版更新。
キーボードショートカットを増やしたり不具合修正で Windows 版も更新。
「こんなことができると楽」
な方法を考えている時がアプリケーション作りで一番楽しいです。