OSDN Git Service

23ec9b9bf10c9832972bdeffbc725da89a3fa492
[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     def _update_notebook_amulet_seed2s(self):
160         u"""お守り検索タブのSeed2リストを更新する"""
161         pass
162
163     def _init_notebook_amulet(self):
164         u""" お守り検索タブの初期設定 """
165         self.notebook_amulet_view = self.frame_view.notebook_amulet_view
166         self.notebook_amulet_view.set_skillminmax_dict(self._skill_names, self._amulet_names, self._minmax_dict)
167         self.notebook_amulet_view.bind_button_search(self.OnClickAmuletSearch)
168         self.notebook_amulet_view.bind_button_clear(self.OnClickAmuletClear)
169         self.notebook_amulet_view.bind_button_skill2(self.OnClickSkill2SearchFromAmulet)
170
171         self.notebook_amulet_view.set_selected_amulet(True, view.NAME_AMULET1)
172
173     def _init_notebook_setting(self):
174         u""" 設定タブの初期設定 """
175         self.notebook_setting_view = self.frame_view.notebook_setting_view
176         self.notebook_setting_view.bind_button_ok(self.OnClickSettingOK)
177         self.notebook_setting_view.bind_button_clear(self.OnClickSettingClear)
178         self.notebook_setting_view.set_skill_strings(self._skill_names)
179         self._update_notebook_setting()
180
181     def _update_notebook_setting(self):
182         u"""設定タブの値更新"""
183         self.notebook_setting_view.set_threshold(self._highlight_threshold1,
184                 self._highlight_threshold2)
185         self.notebook_setting_view.set_checked_strings(self._highlight_skills)
186
187     def _update_highlight(self):
188         u""" update highlight cells """
189         self.notebook_skill2_view.set_skill2_highlight(
190                 self._highlight_skills, self._highlight_threshold1, self._highlight_threshold2)
191         self.notebook_skill1_view.update_highlight(self._highlight_skills)
192
193     u""" Seed1 view's event"""
194     def OnClickSeed1Search(self, evt):
195         u""" search seed1s from selected skill1s """
196         alchemy_type = self.notebook_seed1_view.get_tenun_radio_key()
197         amu_skill_name_list = self.notebook_seed1_view.get_selected_amulets_and_names()
198         if alchemy_type == view.KEY_TENUN555:
199             alchemy_type = model.KEY_TENUN555
200         elif alchemy_type == view.KEY_TENUN888:
201             alchemy_type = model.KEY_TENUN888
202         else:
203             raise KeyError(u"key '{0}' not found in alchemy type".format(alchemy_type))
204         seed1s = self.db_accessor.select_seed1s(amu_skill_name_list, alchemy_type)
205         if len(seed1s) == 0:
206             self.notebook_seed1_view.set_text_result(u"条件に一致するSeed1は見つかりません")
207             self.notebook_seed1_view.set_skill1_button_enable(False)
208         elif len(seed1s) == 1:
209             seed1 = [x for x in seed1s][0]
210             no, table_no, result_num = self.db_accessor.select_table_nos_from_seed1(seed1, alchemy_type)
211             self.notebook_seed1_view.set_text_result_by_seed1(seed1, table_no, no)
212             self.notebook_seed1_view.set_skill1_button_enable(True)
213         else:
214             self.notebook_seed1_view.set_text_result(
215                     u"Seed1は{0}件あります。条件を絞ってください".format(len(seed1s)))
216             self.notebook_seed1_view.set_skill1_button_enable(False)
217
218     def OnClickSeed1Clear(self, evt):
219         u""" clear seed1s from selected skill1s """
220         self.notebook_seed1_view.clear_combobox()
221         self.notebook_seed1_view.clear_text_result()
222
223     def OnClickSkill1SearchFromSeed1(self, evt):
224         u""" change page to skill1 search from seed1"""
225         seed1 = self.notebook_seed1_view.get_result_seed1()
226         alchemy_type = self.notebook_seed1_view.get_tenun_radio_key()
227         if seed1 is not None:
228             self.notebook_skill1_view.set_seed1_value(seed1)
229             self.notebook_skill1_view.set_tenun_radio_key(alchemy_type)
230             self.frame_view.note_book.SetSelection(view.SKILL1_SEARCH_PAGE)
231             self.OnClickSkill1Search(evt)
232
233     u""" Seed2 view's event """
234     def OnClickSeed2Search(self, evt):
235         u""" search seed2s from selected skill2s """
236         amu2skills_dict = {}
237         for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
238                 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
239             names = self.notebook_seed2_view.get_selected_skill_names(amu_key)
240             amu2skills_dict[amu_name] = [name if name in self._skill_names else None for name in names]
241         seed_sets = self.db_accessor.select_seed2s(amu2skills_dict)
242         self.notebook_seed2_view.set_result_text(u"""Seedの候補は{0}個です。""".format(len(seed_sets)))
243
244         if len(seed_sets) > 0:
245             self.notebook_seed2_view.set_seed_lists([u"{0}".format(seed) for seed in sorted(seed_sets)])
246             self.notebook_seed2_view.set_skill2_button_enable(True)
247         else:
248             self.notebook_seed2_view.clear_seed_list()
249             self.notebook_seed2_view.set_skill2_button_enable(False)
250
251     def OnClickSeed2Clear(self, evt):
252         u""" reset seed2 search settings of combo box"""
253         self.notebook_seed2_view.set_skill_selected_idx(0)
254         self.notebook_seed2_view.clear_seed_list()
255         self.notebook_seed2_view.set_result_text(u"")
256         self.notebook_seed2_view.set_skill2_button_enable(False)
257
258     def OnClickSkill2SearchFromSeed2(self, evt):
259         u""" change page to skill2 search from seed2"""
260         seed2 = self.notebook_seed2_view.get_selected_seed2()
261         if seed2 is not None:
262             self.notebook_skill2_view.set_seed2_value(seed2)
263             self.frame_view.note_book.SetSelection(view.SKILL2_SEARCH_PAGE)
264             self.OnClickSkill2Search(evt)
265
266     u""" Skill1 search from Seed1's event """
267     def OnClickSkill1Search(self, evt):
268         u""" skill1 search from seed1"""
269         seed1 = self.notebook_skill1_view.get_seed1_value()
270         alchemy_type = self.notebook_skill1_view.get_tenun_radio_key()
271         if seed1 is not None:
272             try:
273                 no, table_no, result_num = self.db_accessor.select_table_nos_from_seed1(seed1, alchemy_type)
274                 self.notebook_skill1_view.set_text_result_by_seed1(seed1, table_no, no)
275                 # list of (no, seed1)
276                 near_num = view.NEAR_SEED1_NUMBERS
277                 no_seed1_dict = self.db_accessor.select_near_seed1s_from_table_no(
278                         no, table_no, near_num, near_num, alchemy_type)
279
280                 # no -> (seed1, result_num, amu_names, skill_names)
281                 self.notebook_skill1_view.set_no2seed1s_dict(no_seed1_dict, no)
282                 self.notebook_skill1_view.update_highlight(self._highlight_skills)
283             except KeyError, e:
284                 self._show_message_dialog(message=u"指定されたSeed値は存在しません")
285         else:
286             self._show_message_dialog(message=u"Seed値には数字を入力してください")
287
288     def OnClickSkill1Clear(self, evt):
289         u""" clear skills from seed """
290         self.notebook_skill1_view.clear_skill1_grid()
291         self.notebook_skill1_view.clear_highlight()
292         self.notebook_skill1_view.clear_result_text()
293
294     u""" Skill2 search from Seed2's event """
295     def OnClickSkill2Search(self, evt):
296         u""" skill search from seed2"""
297         seed2 = self.notebook_skill2_view.get_seed2_value()
298         if seed2 is not None:
299             table_no, no, skill_dict, th1s, th2s = self.db_accessor.select_names_from_seed2(seed2)
300             try:
301                 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
302                         [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
303                     skill_names = skill_dict[amu_name]
304                     self.notebook_skill2_view.set_skill2_by_col_key(amu_key, skill_names)
305                 th_vals = [u"{0}".format(x) for x in th1s]
306                 self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD1, th_vals)
307                 th_vals = [u"{0}".format(x) for x in th2s]
308                 self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD2, th_vals)
309                 #inishie
310                 skill_name, th1, th2 = self.db_accessor.select_inishie_skill2_from_seed2(seed2)
311                 self.notebook_skill2_view.set_inishie(skill_name, th1, th2)
312                 # explanation
313                 self.notebook_skill2_view.set_result_text(
314                         u"SEED2: {2}, 通し番号: {1}, テーブルNo: {0}".format(table_no, no, seed2))
315             except KeyError, e:
316                 self._show_message_dialog(message=u"指定されたSeed値は存在しません")
317             finally:
318                 self._update_highlight()
319
320         else:
321             self._show_message_dialog(message=u"Seed値には数字を入力してください")
322
323     def OnClickSkill2Clear(self, evt):
324         u""" clear skills from seed """
325         self.notebook_skill2_view.clear_skill2_grid()
326         self.notebook_skill2_view.clear_skill2_highlight()
327         self.notebook_skill2_view.clear_result_text()
328         self.notebook_skill2_view.clear_inishie()
329
330     u""" Alchemy Simulator's event """
331     def OnClickSimulatorSearch(self, evt):
332         u""" alchemy simulation from seed1, seed2"""
333         seeds = self.notebook_simulator_view.get_seed_values()
334         alchemy_type = self.notebook_simulator_view.get_tenun_radio_key()
335         if seeds is not None:
336             seed1, seed2 = seeds
337             #try:
338             result = self.db_accessor.simulate_nazo(seed1, seed2)
339             self.notebook_simulator_view.set_values(view.KEY_NAZO, *result)
340             result = self.db_accessor.simulate_komyou(seed1, seed2)
341             self.notebook_simulator_view.set_values(view.KEY_KOMYOU, *result)
342             result = self.db_accessor.simulate_inishie(seed1, seed2)
343             self.notebook_simulator_view.set_values(view.KEY_INISHIE, *result)
344             result = self.db_accessor.simulate_tenun(seed1, seed2, alchemy_type)
345             self.notebook_simulator_view.set_tenun_values(result)
346             #except KeyError, e:
347             #   self._show_message_dialog(message=u"指定されたSeed値は存在しません")
348             #finally:
349             self.notebook_simulator_view.update_highlight(self._highlight_skills)
350         else:
351             self._show_message_dialog(message=u"Seed値には数字を入力してください")
352
353     def OnClickSimulatorClear(self, evt):
354         u""" clear simulator form """
355         self.notebook_simulator_view.clear_seed_values()
356         self.notebook_simulator_view.clear_grid()
357         self.notebook_simulator_view.clear_highlight()
358
359     u""" amulet search event """
360     def OnClickAmuletSearch(self, evt):
361         u""" search seeds from amulet condition """
362         search_type = self.notebook_amulet_view.get_selected_search_type()
363         if search_type == view.SEARCH_TYPE_SEED2:
364             skill1_name, skill2_name = self.notebook_amulet_view.get_skill_names()
365             skill1_val, skill2_val = self.notebook_amulet_view.get_skill_values()
366             slot_num = self.notebook_amulet_view.get_slot_value()
367             amulet_name = self.notebook_amulet_view.get_selected_amulet()
368             suff_val, threshold, th1_seed2s, th2_seed2s = self.db_accessor.simple_select_seed2s_from_names(
369                     amulet_name, skill1_name, skill1_val, skill2_name, skill2_val, slot_num)
370             self.notebook_amulet_view.set_result_only_seed2(suff_val, threshold, th1_seed2s, th2_seed2s)
371         elif search_type == view.SEARCH_TYPE_SEED1_SEED2:
372             pass
373         elif search_type == view.SEARCH_TYPE_FIXED_SEED2:
374             pass
375
376     def OnClickAmuletClear(self, evt):
377         u""" clear amulet conditions """
378         self.notebook_amulet_view.clear_grid()
379         self.notebook_amulet_view.clear_input_values()
380
381     def OnClickSkill2SearchFromAmulet(self, evt):
382         u""" change page to skill search from amulet"""
383         seed = self.notebook_amulet_view.get_grid_selected_seed2()
384         if seed is not None:
385             self.notebook_skill2_view.set_seed2_value(seed)
386             self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
387             self.OnClickSkillSearch(evt)
388
389     u""" settings' event """
390     def OnClickSettingOK(self, evt):
391         u""" get settings of setting tab """
392         (self._highlight_threshold1, 
393                 self._highlight_threshold2) = self.frame_view.notebook_setting_view.get_threshold()
394         self._highlight_skills = set([x for x in self.frame_view.notebook_setting_view.get_checked_strings()
395                 if x in self._skill_names])
396         self._update_highlight()
397
398     def OnClickSettingClear(self, evt):
399         u""" reset settings of setting tab """
400         self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
401         self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
402         self._highlight_skills = set()
403         self._update_notebook_setting()
404         self._update_highlight()
405
406     def _show_error_dialog(self, message=u"予期せぬエラーが発生しました", caption=u"エラー"):
407         u""" エラーダイアログを表示し、
408         OKボタンが押されたらアプリケーションを終了する
409         """
410         dlg = wx.MessageDialog(self.frame_view.frame, 
411             message,
412             caption, wx.OK | wx.ICON_ERROR)
413         dlg.ShowModal()
414         dlg.Destroy()
415         wx.Exit()
416
417     def _show_message_dialog(self, message, caption=u"メッセージ"):
418         u""" メッセージダイアログを表示する
419         """
420         dlg = wx.MessageDialog(self.frame_view.frame, 
421             message,
422             caption, wx.OK | wx.ICON_INFORMATION)
423         dlg.ShowModal()
424         dlg.Destroy()
425
426     def CloseHandler(self, evt):
427         dlg = wx.MessageDialog(parent = self.frame_view.frame,
428                 message = u"終了します。よろしいですか?", 
429                 caption = u"終了確認", 
430                 style = wx.YES_NO)
431         result = dlg.ShowModal()
432         dlg.Destroy()
433         if result == wx.ID_YES:
434             self._write_settings()
435             wx.Exit()
436
437     def OnClose(self, evt):
438         self.frame_view.Close()
439
440     def OnAboutBox(self, evt):
441         info = self.frame_view.GetAboutInfo()
442         wx.AboutBox(info)
443
444     def OnMemoBox(self, evt):
445         dlg_view = view.MemoDialogView(os.path.join(_get_base_dir(), u"view", view.XRC_MEMO_DIALOG))
446         dlg_view.Show()
447
448     def _write_settings(self):
449         with open(SETTING_FILE, mode="w") as f:
450             data = {SETTING_THRESHOLD1:self._highlight_threshold1, 
451                     SETTING_THRESHOLD2:self._highlight_threshold2, 
452                     SETTING_SKILLS:self._highlight_skills}
453             pickle.dump(data, f)
454
455     def _read_settings(self):
456         if os.path.exists(SETTING_FILE):
457             with open(SETTING_FILE, mode="r") as f:
458                 try:
459                     data = pickle.load(f)
460                     self._highlight_threshold1 = data[SETTING_THRESHOLD1] 
461                     self._highlight_threshold2 = data[SETTING_THRESHOLD2] 
462                     self._highlight_skills = data[SETTING_SKILLS]
463                 except EOFError, e:
464                     self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
465                     self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
466                     self._highlight_skills = set()
467         else:
468             self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
469             self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
470             self._highlight_skills = set()
471
472 if __name__ == "__main__":
473     app = AmuletToolController(False)
474     app.MainLoop()
475