OSDN Git Service

5a5c9c46edea0d958a67a9966e4258818d19e778
[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_seed2()
51         self._init_notebook_skill2()
52         self._init_notebook_setting()
53         self._init_notebook_amulet()
54
55         self.frame_view.EnableNoteBook()
56         return True
57
58     def _init_events(self):
59         u"""イベント登録"""
60         frame = self.frame_view.frame
61
62         # menu event
63         frame.Bind(wx.EVT_MENU, self.OnClose, id=self.frame_view.ID_MENU_ITEM_EXIT)
64         frame.Bind(wx.EVT_MENU, self.OnAboutBox, id=self.frame_view.ID_MENU_ITEM_ABOUT)
65         frame.Bind(wx.EVT_CLOSE, self.CloseHandler)
66
67         # button event
68
69     def _init_database(self):
70         u""" DBの初期設定 """
71
72         if not os.path.exists(model.DB_FILE_NAME):
73             u""" DBが存在しない時は再生成する """
74             frame = self.frame_view.frame
75             try:
76                 generator = model.DataBaseGenerator(model.DB_FILE_NAME)
77                 #generator = model.DataBaseGenerator()
78
79                 dlg_view = view.GaugeDialogView(os.path.join(_get_base_dir(), u"view", view.XRC_GAUGE_DIALOG))
80                 def _loop():
81                     while t1.is_alive():
82                         dlg_view.gauge.Pulse()
83                         time.sleep(0.2)
84                     dlg_view.finish_generation()
85
86                 t1 = threading.Thread(target=generator.generate_db)
87                 t2 = threading.Thread(target=_loop)
88                 t1.start()
89                 t2.start()
90
91                 dlg_view.ShowModal()
92                 t1.join()
93                 t2.join()
94                 dlg_view.Destroy()
95             except sqlite3.Error as e:
96                 self._show_error_dialog(u"データベース生成中にエラーが発生しました")
97
98         # access to db
99         try:
100             self.db_accessor = model.DataBaseAccessor(model.DB_FILE_NAME)
101         except sqlite3.Error as e:
102             self._show_error_dialog(u"データベースが壊れています")
103
104         # get dictionaries
105         (self._skill_id2name_dict, 
106             self._skill_name2id_dict,
107             self._amulet_id2name_dict, 
108             self._amulet_name2id_dict,
109             self._amulet_id2skill1_id_dict,
110             self._amulet_id2skill2_id_dict) = self.db_accessor.get_dicts()
111
112     def _init_notebook_seed2(self):
113         u""" Seed2特定タブのviewの初期化 """
114         self.notebook_seed2_view = self.frame_view.notebook_seed2_view
115         self.notebook_seed2_view.bind_button_search(self.OnClickSeed2Search)
116         self.notebook_seed2_view.bind_button_clear(self.OnClickSeed2Clear)
117         self.notebook_seed2_view.bind_button_skill2(self.OnClickSkill2SearchFromSeed2)
118
119         u" 各種お守りの第2スキル選択 "
120         for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
121                 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
122             amu_id = self._amulet_name2id_dict[amu_name]
123             skill_ids = self._amulet_id2skill2_id_dict[amu_id]
124             skill_names = [view.VAL_NO_SKILL] + [self._skill_id2name_dict[x] for x in skill_ids]
125             self.notebook_seed2_view.set_skill_names(amu_key, skill_names)
126         self.notebook_seed2_view.set_skill_selected_idx(0)
127
128     def _init_notebook_skill2(self):
129         u""" Seed2によるSkill2一覧タブのviewの初期化 """
130         self.notebook_skill2_view = self.frame_view.notebook_skill2_view
131         self.notebook_skill2_view.bind_button_search(self.OnClickSkillSearch)
132         self.notebook_skill2_view.bind_button_clear(self.OnClickSkillClear)
133
134     def _update_notebook_amulet(self, amulet_id=None):
135         u""" お守り検索タブのviewの更新
136         amulet_id が Noneでない場合、お守りの種類に応じてComboboxを再設定する
137         また、選択中のスキルに応じて最大最小を設定する"""
138         #min1, max1, min2, max2 = self.db_accessor.get_skill_minmax()
139         #self.notebook_amulet_view.set_skill_minmax(min1, max1, min2, max2)
140
141         if amulet_id is not None:
142             for i, skill_dict in enumerate([self._amulet_id2skill1_id_dict, self._amulet_id2skill2_id_dict]):
143                 skill_ids = skill_dict[amulet_id]
144                 skill_names = [self._skill_id2name_dict[_id] for _id in skill_ids]
145                 if i == 0:
146                     self.notebook_amulet_view.set_items_skill1_combobox(skill_names)
147                     skill1_selected = skill_ids[0]
148                     self.notebook_amulet_view.set_selection_skill1_combobox(0)
149                 elif i == 1:
150                     self.notebook_amulet_view.set_items_skill2_combobox([view.VAL_NO_SKILL] + skill_names)
151                     self.notebook_amulet_view.set_selection_skill2_combobox(0)  # select No Skill
152         else:
153             amulet_name = self.notebook_amulet_view.get_selected_radio_name()
154             amulet_id = self._amulet_name2id_dict[amulet_name]
155
156         # settting min max
157         skill1_str = self.notebook_amulet_view.get_string_selection_skill1_combobox()
158         skill2_str = self.notebook_amulet_view.get_string_selection_skill2_combobox()
159         skill1_id = self._skill_name2id_dict[skill1_str] if skill1_str in self._skill_name2id_dict else None
160         skill2_id = self._skill_name2id_dict[skill2_str] if skill2_str in self._skill_name2id_dict else None
161
162         if skill2_id is None:
163             minmax_dict = self.db_accessor.select_minmax_from_skill_ids(amulet_id, [skill1_id])
164             min1, max1 = minmax_dict[skill1_id][0], minmax_dict[skill1_id][1]
165             min2, max2 = 0, 0
166         else:
167             minmax_dict = self.db_accessor.select_minmax_from_skill_ids(amulet_id, [skill1_id, skill2_id])
168             min1, max1 = minmax_dict[skill1_id][0], minmax_dict[skill1_id][1]
169             min2, max2 = minmax_dict[skill2_id][2], minmax_dict[skill2_id][3]
170         self.notebook_amulet_view.set_skill_minmax(min1, max1, min2, max2)
171
172     def _init_notebook_amulet(self):
173         u""" お守り検索タブの初期設定 """
174         self.notebook_amulet_view = self.frame_view.notebook_amulet_view
175         self.notebook_amulet_view.bind_radio_button(self.OnClickAmuletRadio)
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         amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
182         self.notebook_amulet_view.set_radio_value(True, view.NAME_AMULET1)
183         self._update_notebook_amulet(amu_id)
184
185     def _init_notebook_setting(self):
186         u""" 設定タブの初期設定 """
187         self.notebook_setting_view = self.frame_view.notebook_setting_view
188         self.notebook_setting_view.bind_button_ok(self.OnClickSettingOK)
189         self.notebook_setting_view.bind_button_clear(self.OnClickSettingClear)
190         self.notebook_setting_view.set_skill_strings(self._skill_name2id_dict.keys())
191         self._update_notebook_setting()
192
193     def _update_notebook_setting(self):
194         u"""設定タブの値更新"""
195         self.notebook_setting_view.set_threshold(self._highlight_threshold1, 
196                 self._highlight_threshold2)
197         self.notebook_setting_view.set_checked_strings([self._skill_id2name_dict[x] for x in self._highlight_skills])
198
199     def _update_highlight(self):
200         u""" update highlight cells """
201         skill_names = [self._skill_id2name_dict[x] for x in self._highlight_skills]
202         self.notebook_skill2_view.set_skill2_highlight(
203                 skill_names, self._highlight_threshold1, self._highlight_threshold2)
204
205     def OnClickSeed2Search(self, evt):
206         u""" search seed2s from selected skill2s """
207         amu_id2skill_id_list_dict = {}
208         for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
209                 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
210             amu_id = self._amulet_name2id_dict[amu_name]
211             names = self.notebook_seed2_view.get_selected_skill_names(amu_key)
212             ls = [self._skill_name2id_dict[name] if name in self._skill_name2id_dict else None 
213                     for name in names]
214             amu_id2skill_id_list_dict[amu_id] = ls
215         seed_sets = self.db_accessor.select_seeds(amu_id2skill_id_list_dict)
216         self.notebook_seed2_view.set_result_text(u"""Seedの候補は{0}個です。""".format(len(seed_sets)))
217
218         if len(seed_sets) > 0:
219             self.notebook_seed2_view.set_seed_lists([u"{0}".format(seed) for seed in seed_sets])
220             self.notebook_seed2_view.set_skill2_button_enable(True)
221         else:
222             self.notebook_seed2_view.clear_seed_list()
223             self.notebook_seed2_view.set_skill2_button_enable(False)
224
225     def OnClickSeed2Clear(self, evt):
226         u""" reset seed2 search settings of combo box"""
227         self.notebook_seed2_view.set_skill_selected_idx(0)
228         self.notebook_seed2_view.clear_seed_list()
229         self.notebook_seed2_view.set_result_text(u"")
230         self.notebook_seed2_view.set_skill2_button_enable(False)
231
232     def OnClickSkill2SearchFromSeed2(self, evt):
233         u""" change page to skill2 search from seed2"""
234         seed2 = self.notebook_seed2_view.get_selected_seed2()
235         if seed2 is not None:
236             self.notebook_skill2_view.set_seed2_value(seed2)
237             self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
238             self.OnClickSkillSearch(evt)
239
240     def OnClickSkillSearch(self, evt):
241         u""" skill search from seed"""
242         seed = self.notebook_skill2_view.get_seed2_value()
243         if seed is not None:
244             skill_dict, threshold1_dict = self.db_accessor.select_skills_from_seeds([seed])
245             try:
246                 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3], 
247                         [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
248                     amu_id = self._amulet_name2id_dict[amu_name]
249                     skill_names = [self._skill_id2name_dict[x] for x in skill_dict[amu_id][seed]]
250                     self.notebook_skill2_view.set_skill2_by_col_key(amu_key, skill_names)
251                 th_vals = [u"{0}".format(x) for x in threshold1_dict[seed]]
252                 self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD1, th_vals)
253             except KeyError, e:
254                 self._show_message_dialog(message=u"指定されたSeed値は存在しません")
255             finally:
256                 self._update_highlight()
257
258         else:
259             self._show_message_dialog(message=u"Seed値には数字を入力してください")
260
261     def OnClickSkillClear(self, evt):
262         u""" clear skills from seed """
263         self.notebook_skill2_view.clear_skill2_grid()
264         self.notebook_skill2_view.clear_skill2_highlight()
265         self.notebook_skill2_view.clear_items_amulet_prospects()
266
267     def OnClickAmuletRadio(self, evt):
268         u""" switch skill lists by amulet id """
269         #btn_id = evt.GetId()
270         amulet_name = self.notebook_amulet_view.get_selected_radio_name()
271         amu_id = self._amulet_name2id_dict[amulet_name]
272         self._update_notebook_amulet(amu_id)
273         self.notebook_amulet_view.set_result_text_ctrl_value(u"")
274         self.notebook_amulet_view.set_skill_button_enable(False)
275         self.notebook_amulet_view.clear_listbox_items()
276
277     def OnClickAmuletCombo(self, evt):
278         u""" switch skill minmax by amulet id and skill_id"""
279         self._update_notebook_amulet()
280
281     def OnClickAmuletSearch(self, evt):
282         u""" search seeds from amulet condition """
283         skill1_name, skill2_name = self.notebook_amulet_view.get_string_selection_skill_names()
284         skill1_val, skill2_val = self.notebook_amulet_view.get_skill_values()
285         slot_val = self.notebook_amulet_view.get_slot_value()
286         amulet_name = self.notebook_amulet_view.get_selected_radio_name()
287         amu_id = self._amulet_name2id_dict[amulet_name]
288
289         if skill1_name == skill2_name:
290             self._show_message_dialog(message=u"異なるスキルを選択してください")
291         elif skill1_val == 0:
292             self._show_message_dialog(message=u"第1スキルの値には0以外を指定してください")
293         else:
294             skill1_id = self._skill_name2id_dict[skill1_name]
295             if skill2_name in self._skill_name2id_dict:
296                 skill2_id = self._skill_name2id_dict[skill2_name]
297             else:
298                 skill2_id = None # for skill2 is 0
299             suff_dict = {}
300             tup = self.db_accessor.get_sufficient_value(
301                 amu_id, skill1_id, skill2_id, skill1_val, skill2_val)
302             if tup is not None:
303                 suff_val = tup[0]
304                 seeds_set = self.db_accessor.select_seeds_from_sufficient_val(amu_id, suff_val, slot_val, skill2_id)
305                 if len(seeds_set) > 0:
306                     self.notebook_amulet_view.set_result_text_ctrl_value(
307                         u"{0}個のSeedで出現するお守りです".format(len(seeds_set)))
308                     self.notebook_amulet_view.set_listbox_items(
309                             [u"{0}".format(seed) for seed in seeds_set])
310                     self.notebook_amulet_view.set_selection_listbox(0)
311                     self.notebook_amulet_view.set_skill_button_enable(True)
312                 else:
313                     self.notebook_amulet_view.set_result_text_ctrl_value(
314                         u"指定されたお守りは見つかりませんでした")
315                     self.notebook_amulet_view.set_skill_button_enable(False)
316                     self.notebook_amulet_view.clear_listbox_items()
317             else:
318                 self.notebook_amulet_view.set_result_text_ctrl_value(
319                     u"エラー。充足値が計算できません")
320                 self.notebook_amulet_view.set_skill_button_enable(False)
321                 self.notebook_amulet_view.clear_listbox_items()
322
323     def OnClickAmuletClear(self, evt):
324         u""" clear amulet conditions """
325         self.notebook_amulet_view.set_radio_value(True, view.NAME_AMULET1)
326         amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
327         self._update_notebook_amulet(amu_id)
328         self.notebook_amulet_view.set_skill_button_enable(False)
329         self.notebook_amulet_view.set_result_text_ctrl_value(u"")
330         self.notebook_amulet_view.clear_listbox_items()
331
332     def OnClickSkillSearchFromAmulet(self, evt):
333         u""" change page to skill search from amulet"""
334         seed = self.notebook_amulet_view.get_string_selection_listbox()
335         if seed.isdigit():
336             self.notebook_skill2_view.set_seed2_value(seed)
337             self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
338             self.OnClickSkillSearch(evt)
339
340     def OnClickSettingOK(self, evt):
341         u""" get settings of setting tab """
342         (self._highlight_threshold1, 
343                 self._highlight_threshold2) = self.frame_view.notebook_setting_view.get_threshold()
344         self._highlight_skills = set([self._skill_name2id_dict[x] for x in 
345                 self.frame_view.notebook_setting_view.get_checked_strings()
346                 if x in self._skill_name2id_dict])
347         self._update_highlight()
348
349     def OnClickSettingClear(self, evt):
350         u""" reset settings of setting tab """
351         self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
352         self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
353         self._highlight_skills = set()
354         self._update_notebook_setting()
355         self._update_highlight()
356
357
358     def _show_error_dialog(self, message=u"予期せぬエラーが発生しました", caption=u"エラー"):
359         u""" エラーダイアログを表示し、
360         OKボタンが押されたらアプリケーションを終了する
361         """
362         dlg = wx.MessageDialog(self.frame_view.frame, 
363             message,
364             caption, wx.OK | wx.ICON_ERROR)
365         dlg.ShowModal()
366         dlg.Destroy()
367         wx.Exit()
368     
369     def _show_message_dialog(self, message, caption=u"メッセージ"):
370         u""" メッセージダイアログを表示する
371         """
372         dlg = wx.MessageDialog(self.frame_view.frame, 
373             message,
374             caption, wx.OK | wx.ICON_INFORMATION)
375         dlg.ShowModal()
376         dlg.Destroy()
377
378     def CloseHandler(self, evt):
379         dlg = wx.MessageDialog(parent = self.frame_view.frame,
380                 message = u"終了します。よろしいですか?", 
381                 caption = u"終了確認", 
382                 style = wx.YES_NO)
383         result = dlg.ShowModal()
384         dlg.Destroy()
385         if result == wx.ID_YES:
386             self._write_settings()
387             wx.Exit()
388
389     def OnClose(self, evt):
390         self.frame_view.Close()
391
392     def OnAboutBox(self, evt):
393         info = self.frame_view.GetAboutInfo()
394         wx.AboutBox(info)
395
396     def _write_settings(self):
397         with open(SETTING_FILE, mode="w") as f:
398             data = {SETTING_THRESHOLD1:self._highlight_threshold1, 
399                     SETTING_THRESHOLD2:self._highlight_threshold2, 
400                     SETTING_SKILLS:self._highlight_skills}
401             pickle.dump(data, f)
402
403     def _read_settings(self):
404         if os.path.exists(SETTING_FILE):
405             with open(SETTING_FILE, mode="r") as f:
406                 try:
407                     data = pickle.load(f)
408                     self._highlight_threshold1 = data[SETTING_THRESHOLD1] 
409                     self._highlight_threshold2 = data[SETTING_THRESHOLD2] 
410                     self._highlight_skills = data[SETTING_SKILLS]
411                 except EOFError, e:
412                     self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
413                     self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
414                     self._highlight_skills = set()
415         else:
416             self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
417             self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
418             self._highlight_skills = set()
419
420 if __name__ == "__main__":
421     app = AmuletToolController(False)
422     app.MainLoop()
423