OSDN Git Service

6c9911bda3558e8d371134452ac88c02fccce0f1
[amulettoolsmh4/main.git] / amulettool.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # お守りスキルのSeed特定ツールのコントロールクラス
5 # 2013/12/04 written by kei9
6
7 import threading
8 import time
9 import os.path
10 import sys
11 import pickle
12 import sqlite3
13
14 import wx
15
16 import view
17 import model
18 import convertcoding
19
20 SETTING_FILE = u"settings"
21 SETTING_THRESHOLD1 = u"threshold1"
22 SETTING_THRESHOLD2 = u"threshold2"
23 SETTING_SKILLS = u"skills"
24
25 def _get_base_dir():
26     u""" for pyinstaller 2.1 """
27     if getattr(sys, 'frozen', False):
28         # we are running in a |PyInstaller| bundle
29         basedir = sys._MEIPASS
30     else:
31         # we are running in a normal Python environment
32         #1basedir = os.path.dirname(__file__)
33         basedir = os.path.dirname(os.path.abspath(__file__))
34     return convertcoding.convert_unicode(basedir)
35
36
37 class AmuletToolController(wx.App):
38     u""" アプリケーションの制御クラス """
39     def OnInit(self):
40         self._read_settings()
41         self.frame_view = view.MainFrameView(os.path.join(_get_base_dir(), u"view", view.XRC_MAIN_FRAME))
42
43         self._init_events()
44
45         self.frame_view.Show()
46
47         self.frame_view.DisableNoteBook()
48
49         self._init_database()
50         self._init_notebook_seed1()
51         self._init_notebook_seed2()
52         self._init_notebook_skill1()
53         self._init_notebook_skill2()
54         self._init_notebook_setting()
55         self._init_notebook_simulator()
56         self._init_notebook_amulet()
57
58         self.frame_view.EnableNoteBook()
59         return True
60
61     def _init_events(self):
62         u"""イベント登録"""
63         frame = self.frame_view.frame
64
65         # menu event
66         frame.Bind(wx.EVT_MENU, self.OnClose, id=self.frame_view.ID_MENU_ITEM_EXIT)
67         frame.Bind(wx.EVT_MENU, self.OnAboutBox, id=self.frame_view.ID_MENU_ITEM_ABOUT)
68         frame.Bind(wx.EVT_MENU, self.OnMemoBox, id=self.frame_view.ID_MENU_MEMO)
69         frame.Bind(wx.EVT_CLOSE, self.CloseHandler)
70
71         # button event
72
73     def _init_database(self):
74         u""" DBの初期設定 """
75
76         if not os.path.exists(model.DB_FILE_NAME):
77             u""" DBが存在しない時は再生成する """
78             frame = self.frame_view.frame
79             try:
80                 generator = model.DataBaseGenerator(model.DB_FILE_NAME)
81                 #generator = model.DataBaseGenerator()
82
83                 dlg_view = view.GaugeDialogView(os.path.join(_get_base_dir(), u"view", view.XRC_GAUGE_DIALOG))
84                 def _loop():
85                     while t1.is_alive():
86                         dlg_view.gauge.Pulse()
87                         time.sleep(0.2)
88                     dlg_view.finish_generation()
89
90                 t1 = threading.Thread(target=generator.generate_db)
91                 t2 = threading.Thread(target=_loop)
92                 t1.start()
93                 t2.start()
94
95                 dlg_view.ShowModal()
96                 t1.join()
97                 t2.join()
98                 dlg_view.Destroy()
99             except sqlite3.Error as e:
100                 self._show_error_dialog(u"データベース生成中にエラーが発生しました")
101
102         # access to db
103         try:
104             self.db_accessor = model.DataBaseAccessor(model.DB_FILE_NAME)
105         except sqlite3.Error as e:
106             self._show_error_dialog(u"データベースが壊れています")
107
108         self._minmax_dict = self.db_accessor.get_minmax_dict()
109         self._amulet_names = self.db_accessor.get_id_sorted_amulet_names()
110         self._skill_names = self.db_accessor.get_id_sorted_skill_names()
111
112     def _init_notebook_seed1(self):
113         u""" Seed1特定タブのviewの初期化 """
114         self.notebook_seed1_view = self.frame_view.notebook_seed1_view
115         self.notebook_seed1_view.bind_button_search(self.OnClickSeed1Search)
116         self.notebook_seed1_view.bind_button_clear(self.OnClickSeed1Clear)
117         self.notebook_seed1_view.bind_button_skill1(self.OnClickSkill1SearchFromSeed1)
118         self.notebook_seed1_view.set_skill1_button_enable(False)
119
120         u" 各種お守りの第1スキル選択のセット "
121         name2skill1s = {}
122         for name in self._amulet_names:
123             name2skill1s[name] = tuple([x for x in self._skill_names if x in self._minmax_dict[name][0]])
124         self.notebook_seed1_view.set_amuletname_skillnames_dict(self._amulet_names, name2skill1s)
125
126     def _init_notebook_seed2(self):
127         u""" Seed2特定タブのviewの初期化 """
128         self.notebook_seed2_view = self.frame_view.notebook_seed2_view
129         self.notebook_seed2_view.bind_button_search(self.OnClickSeed2Search)
130         self.notebook_seed2_view.bind_button_clear(self.OnClickSeed2Clear)
131         self.notebook_seed2_view.bind_button_skill2(self.OnClickSkill2SearchFromSeed2)
132
133         u" 各種お守りの第2スキル選択 "
134         for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
135                 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
136             skill_names = [view.VAL_NO_SKILL] + [x for x in self._skill_names 
137                     if x in self._minmax_dict[amu_name][1]]
138             self.notebook_seed2_view.set_skill_names(amu_key, skill_names)
139         self.notebook_seed2_view.set_skill_selected_idx(0)
140
141     def _init_notebook_skill2(self):
142         u""" Seed2によるSkill2一覧タブのviewの初期化 """
143         self.notebook_skill2_view = self.frame_view.notebook_skill2_view
144         self.notebook_skill2_view.bind_button_search(self.OnClickSkill2Search)
145         self.notebook_skill2_view.bind_button_clear(self.OnClickSkill2Clear)
146
147     def _init_notebook_skill1(self):
148         u""" Seed1によるSkill1一覧タブのviewの初期化 """
149         self.notebook_skill1_view = self.frame_view.notebook_skill1_view
150         self.notebook_skill1_view.bind_button_search(self.OnClickSkill1Search)
151         self.notebook_skill1_view.bind_button_clear(self.OnClickSkill1Clear)
152
153     def _init_notebook_simulator(self):
154         u""" Seed1,Seed2による錬金シミュレータータブのviewの初期化 """
155         self.notebook_simulator_view = self.frame_view.notebook_simulator_view
156         self.notebook_simulator_view.bind_button_search(self.OnClickSimulatorSearch)
157         self.notebook_simulator_view.bind_button_clear(self.OnClickSimulatorClear)
158
159
160     def _update_notebook_amulet(self, amulet_id=None):
161         u""" お守り検索タブのviewの更新
162         amulet_id が Noneでない場合、お守りの種類に応じてComboboxを再設定する
163         また、選択中のスキルに応じて最大最小を設定する"""
164         pass
165
166
167     def _update_notebook_amulet_seed2s(self):
168         u"""お守り検索タブのSeed2リストを更新する"""
169         pass
170
171     def _init_notebook_amulet(self):
172         u""" お守り検索タブの初期設定 """
173         self.notebook_amulet_view = self.frame_view.notebook_amulet_view
174         self.notebook_amulet_view.bind_radiobox_amulet(self.OnClickAmuletRadioAmulet)
175         self.notebook_amulet_view.bind_radiobox_threshold_type(self.OnClickAmuletRadioThresholdType)
176         self.notebook_amulet_view.bind_combobox(self.OnClickAmuletCombo)
177         self.notebook_amulet_view.bind_button_search(self.OnClickAmuletSearch)
178         self.notebook_amulet_view.bind_button_clear(self.OnClickAmuletClear)
179         self.notebook_amulet_view.bind_button_skill(self.OnClickSkillSearchFromAmulet)
180
181         self.notebook_amulet_view.set_radio_value(True, view.NAME_AMULET1)
182         self._update_notebook_amulet()
183
184     def _init_notebook_setting(self):
185         u""" 設定タブの初期設定 """
186         self.notebook_setting_view = self.frame_view.notebook_setting_view
187         self.notebook_setting_view.bind_button_ok(self.OnClickSettingOK)
188         self.notebook_setting_view.bind_button_clear(self.OnClickSettingClear)
189         self.notebook_setting_view.set_skill_strings(self._skill_names)
190         self._update_notebook_setting()
191
192     def _update_notebook_setting(self):
193         u"""設定タブの値更新"""
194         self.notebook_setting_view.set_threshold(self._highlight_threshold1,
195                 self._highlight_threshold2)
196         self.notebook_setting_view.set_checked_strings(self._highlight_skills)
197
198     def _update_highlight(self):
199         u""" update highlight cells """
200         self.notebook_skill2_view.set_skill2_highlight(
201                 self._highlight_skills, self._highlight_threshold1, self._highlight_threshold2)
202         self.notebook_skill1_view.update_highlight(self._highlight_skills)
203
204     u""" Seed1 view's event"""
205     def OnClickSeed1Search(self, evt):
206         u""" search seed1s from selected skill1s """
207         alchemy_type = self.notebook_seed1_view.get_tenun_radio_key()
208         amu_skill_name_list = self.notebook_seed1_view.get_selected_amulets_and_names()
209         if alchemy_type == view.KEY_TENUN555:
210             alchemy_type = model.KEY_TENUN555
211         elif alchemy_type == view.KEY_TENUN888:
212             alchemy_type = model.KEY_TENUN888
213         else:
214             raise KeyError(u"key '{0}' not found in alchemy type".format(alchemy_type))
215         seed1s = self.db_accessor.select_seed1s(amu_skill_name_list, alchemy_type)
216         if len(seed1s) == 0:
217             self.notebook_seed1_view.set_text_result(u"条件に一致するSeed1は見つかりません")
218             self.notebook_seed1_view.set_skill1_button_enable(False)
219         elif len(seed1s) == 1:
220             seed1 = [x for x in seed1s][0]
221             no, table_no, result_num = self.db_accessor.select_table_nos_from_seed1(seed1, alchemy_type)
222             self.notebook_seed1_view.set_text_result_by_seed1(seed1, table_no, no)
223             self.notebook_seed1_view.set_skill1_button_enable(True)
224         else:
225             self.notebook_seed1_view.set_text_result(
226                     u"Seed1は{0}件あります。条件を絞ってください".format(len(seed1s)))
227             self.notebook_seed1_view.set_skill1_button_enable(False)
228
229     def OnClickSeed1Clear(self, evt):
230         u""" clear seed1s from selected skill1s """
231         self.notebook_seed1_view.clear_combobox()
232         self.notebook_seed1_view.clear_text_result()
233
234     def OnClickSkill1SearchFromSeed1(self, evt):
235         u""" change page to skill1 search from seed1"""
236         seed1 = self.notebook_seed1_view.get_result_seed1()
237         alchemy_type = self.notebook_seed1_view.get_tenun_radio_key()
238         if seed1 is not None:
239             self.notebook_skill1_view.set_seed1_value(seed1)
240             self.notebook_skill1_view.set_tenun_radio_key(alchemy_type)
241             self.frame_view.note_book.SetSelection(view.SKILL1_SEARCH_PAGE)
242             self.OnClickSkill1Search(evt)
243
244     u""" Seed2 view's event """
245     def OnClickSeed2Search(self, evt):
246         u""" search seed2s from selected skill2s """
247         amu2skills_dict = {}
248         for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
249                 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
250             names = self.notebook_seed2_view.get_selected_skill_names(amu_key)
251             amu2skills_dict[amu_name] = [name if name in self._skill_names else None for name in names]
252         seed_sets = self.db_accessor.select_seed2s(amu2skills_dict)
253         self.notebook_seed2_view.set_result_text(u"""Seedの候補は{0}個です。""".format(len(seed_sets)))
254
255         if len(seed_sets) > 0:
256             self.notebook_seed2_view.set_seed_lists([u"{0}".format(seed) for seed in sorted(seed_sets)])
257             self.notebook_seed2_view.set_skill2_button_enable(True)
258         else:
259             self.notebook_seed2_view.clear_seed_list()
260             self.notebook_seed2_view.set_skill2_button_enable(False)
261
262     def OnClickSeed2Clear(self, evt):
263         u""" reset seed2 search settings of combo box"""
264         self.notebook_seed2_view.set_skill_selected_idx(0)
265         self.notebook_seed2_view.clear_seed_list()
266         self.notebook_seed2_view.set_result_text(u"")
267         self.notebook_seed2_view.set_skill2_button_enable(False)
268
269     def OnClickSkill2SearchFromSeed2(self, evt):
270         u""" change page to skill2 search from seed2"""
271         seed2 = self.notebook_seed2_view.get_selected_seed2()
272         if seed2 is not None:
273             self.notebook_skill2_view.set_seed2_value(seed2)
274             self.frame_view.note_book.SetSelection(view.SKILL2_SEARCH_PAGE)
275             self.OnClickSkill2Search(evt)
276
277     u""" Skill1 search from Seed1's event """
278     def OnClickSkill1Search(self, evt):
279         u""" skill1 search from seed1"""
280         seed1 = self.notebook_skill1_view.get_seed1_value()
281         alchemy_type = self.notebook_skill1_view.get_tenun_radio_key()
282         if seed1 is not None:
283             try:
284                 no, table_no, result_num = self.db_accessor.select_table_nos_from_seed1(seed1, alchemy_type)
285                 self.notebook_skill1_view.set_text_result_by_seed1(seed1, table_no, no)
286                 # list of (no, seed1)
287                 near_num = view.NEAR_SEED1_NUMBERS
288                 no_seed1_dict = self.db_accessor.select_near_seed1s_from_table_no(
289                         no, table_no, near_num, near_num, alchemy_type)
290
291                 # no -> (seed1, result_num, amu_names, skill_names)
292                 self.notebook_skill1_view.set_no2seed1s_dict(no_seed1_dict, no)
293                 self.notebook_skill1_view.update_highlight(self._highlight_skills)
294             except KeyError, e:
295                 self._show_message_dialog(message=u"指定されたSeed値は存在しません")
296         else:
297             self._show_message_dialog(message=u"Seed値には数字を入力してください")
298
299     def OnClickSkill1Clear(self, evt):
300         u""" clear skills from seed """
301         self.notebook_skill1_view.clear_skill1_grid()
302         self.notebook_skill1_view.clear_highlight()
303         self.notebook_skill1_view.clear_result_text()
304
305     u""" Skill2 search from Seed2's event """
306     def OnClickSkill2Search(self, evt):
307         u""" skill search from seed2"""
308         seed2 = self.notebook_skill2_view.get_seed2_value()
309         if seed2 is not None:
310             table_no, no, skill_dict, th1s, th2s = self.db_accessor.select_names_from_seed2(seed2)
311             try:
312                 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
313                         [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
314                     skill_names = skill_dict[amu_name]
315                     self.notebook_skill2_view.set_skill2_by_col_key(amu_key, skill_names)
316                 th_vals = [u"{0}".format(x) for x in th1s]
317                 self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD1, th_vals)
318                 th_vals = [u"{0}".format(x) for x in th2s]
319                 self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD2, th_vals)
320                 #inishie
321                 skill_name, th1, th2 = self.db_accessor.select_inishie_skill2_from_seed2(seed2)
322                 self.notebook_skill2_view.set_inishie(skill_name, th1, th2)
323                 # explanation
324                 self.notebook_skill2_view.set_result_text(
325                         u"SEED2: {2}, 通し番号: {1}, テーブルNo: {0}".format(table_no, no, seed2))
326             except KeyError, e:
327                 self._show_message_dialog(message=u"指定されたSeed値は存在しません")
328             finally:
329                 self._update_highlight()
330
331         else:
332             self._show_message_dialog(message=u"Seed値には数字を入力してください")
333
334     def OnClickSkill2Clear(self, evt):
335         u""" clear skills from seed """
336         self.notebook_skill2_view.clear_skill2_grid()
337         self.notebook_skill2_view.clear_skill2_highlight()
338         self.notebook_skill2_view.clear_result_text()
339         self.notebook_skill2_view.clear_inishie()
340
341     u""" Alchemy Simulator's event """
342     def OnClickSimulatorSearch(self, evt):
343         u""" alchemy simulation from seed1, seed2"""
344         seeds = self.notebook_simulator_view.get_seed_values()
345         alchemy_type = self.notebook_simulator_view.get_tenun_radio_key()
346         if seeds is not None:
347             seed1, seed2 = seeds
348             #try:
349             result = self.db_accessor.simulate_nazo(seed1, seed2)
350             self.notebook_simulator_view.set_values(view.KEY_NAZO, *result)
351             result = self.db_accessor.simulate_komyou(seed1, seed2)
352             self.notebook_simulator_view.set_values(view.KEY_KOMYOU, *result)
353             result = self.db_accessor.simulate_inishie(seed1, seed2)
354             self.notebook_simulator_view.set_values(view.KEY_INISHIE, *result)
355             result = self.db_accessor.simulate_tenun(seed1, seed2, alchemy_type)
356             self.notebook_simulator_view.set_tenun_values(result)
357             #except KeyError, e:
358             #   self._show_message_dialog(message=u"指定されたSeed値は存在しません")
359             #finally:
360             self.notebook_simulator_view.update_highlight(self._highlight_skills)
361         else:
362             self._show_message_dialog(message=u"Seed値には数字を入力してください")
363
364     def OnClickSimulatorClear(self, evt):
365         u""" clear simulator form """
366         self.notebook_simulator_view.clear_seed_values()
367         self.notebook_simulator_view.clear_grid()
368         self.notebook_simulator_view.clear_highlight()
369
370     u""" amulet search event """
371     def OnClickAmuletRadioAmulet(self, evt):
372         u""" switch skill lists by amulet id """
373         #btn_id = evt.GetId()
374         amulet_name = self.notebook_amulet_view.get_selected_amulet()
375         amu_id = self._amulet_name2id_dict[amulet_name]
376         self._update_notebook_amulet(amu_id)
377         self.notebook_amulet_view.set_result_text_ctrl_value(u"")
378         self.notebook_amulet_view.set_skill_button_enable(False)
379         self.notebook_amulet_view.clear_grid()
380
381     def OnClickAmuletRadioThresholdType(self, evt):
382         u""" switch seed lists by threshold_type """
383         self._update_notebook_amulet_seed2s()
384
385     def OnClickAmuletCombo(self, evt):
386         u""" switch skill minmax by amulet id and skill_id"""
387         self._update_notebook_amulet()
388
389     def OnClickAmuletSearch(self, evt):
390         u""" search seeds from amulet condition """
391         pass
392
393     def OnClickAmuletClear(self, evt):
394         u""" clear amulet conditions """
395         pass
396
397     def OnClickSkillSearchFromAmulet(self, evt):
398         u""" change page to skill search from amulet"""
399         seed = self.notebook_amulet_view.get_grid_selected_seed2()
400         if seed is not None:
401             self.notebook_skill2_view.set_seed2_value(seed)
402             self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
403             self.OnClickSkillSearch(evt)
404
405     u""" settings' event """
406     def OnClickSettingOK(self, evt):
407         u""" get settings of setting tab """
408         (self._highlight_threshold1, 
409                 self._highlight_threshold2) = self.frame_view.notebook_setting_view.get_threshold()
410         self._highlight_skills = set([x for x in self.frame_view.notebook_setting_view.get_checked_strings()
411                 if x in self._skill_names])
412         self._update_highlight()
413
414     def OnClickSettingClear(self, evt):
415         u""" reset settings of setting tab """
416         self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
417         self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
418         self._highlight_skills = set()
419         self._update_notebook_setting()
420         self._update_highlight()
421
422     def _show_error_dialog(self, message=u"予期せぬエラーが発生しました", caption=u"エラー"):
423         u""" エラーダイアログを表示し、
424         OKボタンが押されたらアプリケーションを終了する
425         """
426         dlg = wx.MessageDialog(self.frame_view.frame, 
427             message,
428             caption, wx.OK | wx.ICON_ERROR)
429         dlg.ShowModal()
430         dlg.Destroy()
431         wx.Exit()
432
433     def _show_message_dialog(self, message, caption=u"メッセージ"):
434         u""" メッセージダイアログを表示する
435         """
436         dlg = wx.MessageDialog(self.frame_view.frame, 
437             message,
438             caption, wx.OK | wx.ICON_INFORMATION)
439         dlg.ShowModal()
440         dlg.Destroy()
441
442     def CloseHandler(self, evt):
443         dlg = wx.MessageDialog(parent = self.frame_view.frame,
444                 message = u"終了します。よろしいですか?", 
445                 caption = u"終了確認", 
446                 style = wx.YES_NO)
447         result = dlg.ShowModal()
448         dlg.Destroy()
449         if result == wx.ID_YES:
450             self._write_settings()
451             wx.Exit()
452
453     def OnClose(self, evt):
454         self.frame_view.Close()
455
456     def OnAboutBox(self, evt):
457         info = self.frame_view.GetAboutInfo()
458         wx.AboutBox(info)
459
460     def OnMemoBox(self, evt):
461         dlg_view = view.MemoDialogView(os.path.join(_get_base_dir(), u"view", view.XRC_MEMO_DIALOG))
462         dlg_view.Show()
463
464     def _write_settings(self):
465         with open(SETTING_FILE, mode="w") as f:
466             data = {SETTING_THRESHOLD1:self._highlight_threshold1, 
467                     SETTING_THRESHOLD2:self._highlight_threshold2, 
468                     SETTING_SKILLS:self._highlight_skills}
469             pickle.dump(data, f)
470
471     def _read_settings(self):
472         if os.path.exists(SETTING_FILE):
473             with open(SETTING_FILE, mode="r") as f:
474                 try:
475                     data = pickle.load(f)
476                     self._highlight_threshold1 = data[SETTING_THRESHOLD1] 
477                     self._highlight_threshold2 = data[SETTING_THRESHOLD2] 
478                     self._highlight_skills = data[SETTING_SKILLS]
479                 except EOFError, e:
480                     self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
481                     self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
482                     self._highlight_skills = set()
483         else:
484             self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
485             self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
486             self._highlight_skills = set()
487
488 if __name__ == "__main__":
489     app = AmuletToolController(False)
490     app.MainLoop()
491