1 # -*- coding: utf-8 -*-
3 # お守りスキルのSeed特定ツールのコントロールクラス
4 # 2013/12/04 written by kei9
18 SETTING_FILE = u"settings"
19 SETTING_THRESHOLD1 = u"threshold1"
20 SETTING_SKILLS = u"skills"
23 u""" for pyinstaller 2.1 """
24 if getattr(sys, 'frozen', False):
25 # we are running in a |PyInstaller| bundle
26 basedir = sys._MEIPASS
28 # we are running in a normal Python environment
29 basedir = os.path.dirname(__file__)
32 class AmuletToolController(wx.App):
33 u""" アプリケーションの制御クラス """
36 self.frame_view = view.MainFrameView(os.path.join(_get_base_dir(), "view", view.XRC_MAIN_FRAME))
40 self.frame_view.Show()
42 self.frame_view.DisableNoteBook()
47 self.frame_view.EnableNoteBook()
50 def _init_events(self):
52 frame = self.frame_view.frame
55 frame.Bind(wx.EVT_MENU, self.OnClose, id=self.frame_view.ID_MENU_ITEM_EXIT)
56 frame.Bind(wx.EVT_MENU, self.OnAboutBox, id=self.frame_view.ID_MENU_ITEM_ABOUT)
57 frame.Bind(wx.EVT_CLOSE, self.CloseHandler)
60 frame.Bind(wx.EVT_BUTTON, self.OnClickSeedSearch, id=self.frame_view.ID_BUTTON_SEED_SEARCH)
61 frame.Bind(wx.EVT_BUTTON, self.OnClickSeedClear, id=self.frame_view.ID_BUTTON_SEED_CLEAR)
62 frame.Bind(wx.EVT_BUTTON, self.OnClickSkillSearchFromSeed, id=self.frame_view.ID_BUTTON_SKILL_FROM_SEED_SEARCH)
63 frame.Bind(wx.EVT_BUTTON, self.OnClickSkillSearch, id=self.frame_view.ID_BUTTON_SKILL_SEARCH)
64 frame.Bind(wx.EVT_BUTTON, self.OnClickSkillClear, id=self.frame_view.ID_BUTTON_SKILL_CLEAR)
65 frame.Bind(wx.EVT_BUTTON, self.OnClickAmuletSearch, id=self.frame_view.ID_BUTTON_AMULET_SEARCH_SEARCH)
66 frame.Bind(wx.EVT_BUTTON, self.OnClickAmuletClear, id=self.frame_view.ID_BUTTON_AMULET_SEARCH_CLEAR)
67 frame.Bind(wx.EVT_BUTTON, self.OnClickSkillSearchFromAmulet, id=self.frame_view.ID_BUTTON_AMULET_SEARCH_SKILL)
68 frame.Bind(wx.EVT_BUTTON, self.OnClickSettingOK, id=self.frame_view.ID_BUTTON_SETTING_OK)
69 frame.Bind(wx.EVT_BUTTON, self.OnClickSettingClear, id=self.frame_view.ID_BUTTON_SETTING_CLEAR)
72 frame.Bind(wx.EVT_RADIOBUTTON, self.OnClickAmuletRadio, id=self.frame_view.ID_RADIO_BUTTON_AMULET1)
73 frame.Bind(wx.EVT_RADIOBUTTON, self.OnClickAmuletRadio, id=self.frame_view.ID_RADIO_BUTTON_AMULET2)
74 frame.Bind(wx.EVT_RADIOBUTTON, self.OnClickAmuletRadio, id=self.frame_view.ID_RADIO_BUTTON_AMULET3)
77 frame.Bind(wx.EVT_COMBOBOX, self.OnClickAmuletCombo, id=self.frame_view.ID_COMBO_BOX_AMULET_SEARCH_SKILL1)
78 frame.Bind(wx.EVT_COMBOBOX, self.OnClickAmuletCombo, id=self.frame_view.ID_COMBO_BOX_AMULET_SEARCH_SKILL2)
80 def _init_database(self):
83 if not os.path.exists(model.DB_FILE_NAME):
84 u""" DBが存在しない時は再生成する """
85 frame = self.frame_view.frame
87 generator = model.DataBaseGenerator(model.DB_FILE_NAME)
88 #generator = model.DataBaseGenerator()
90 dlg_view = view.GaugeDialogView(os.path.join(_get_base_dir(), "view", view.XRC_GAUGE_DIALOG))
93 dlg_view.gauge.Pulse()
95 dlg_view.finish_generation()
97 t1 = threading.Thread(target=generator.generate_db)
98 t2 = threading.Thread(target=_loop)
106 except sqlite3.Error as e:
107 self._show_error_dialog(u"データベース生成中にエラーが発生しました")
111 self.db_accessor = model.DataBaseAccessor(model.DB_FILE_NAME)
112 except sqlite3.Error as e:
113 self._show_error_dialog(u"データベースが壊れています")
116 (self._skill_id2name_dict,
117 self._skill_name2id_dict,
118 self._amulet_id2name_dict,
119 self._amulet_name2id_dict,
120 self._amulet_id2skill1_id_dict,
121 self._amulet_id2skill2_id_dict) = self.db_accessor.get_dicts()
124 def _set_views(self):
125 u""" GUIにDBの各種値をセットする """
126 self._set_combo_views()
127 self._set_spin_views()
128 self._set_check_list_views()
130 def _set_combo_views(self):
131 u""" ComboBoxの値をセットする """
132 combo_dict = self.frame_view.combo_box_skill_dict
135 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
136 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
137 amu_id = self._amulet_name2id_dict[amu_name]
138 skill_ids = self._amulet_id2skill2_id_dict[amu_id]
139 skill_names = [view.VAL_NO_SKILL] + [self._skill_id2name_dict[x] for x in skill_ids]
140 for combo in combo_dict[amu_key]:
141 combo.SetItems(skill_names)
142 combo.SetSelection(0)
145 amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
146 self._set_skill_list_from_amulet(amu_id)
147 self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].SetValue(True)
149 def _set_skill_list_from_amulet(self, amulet_id):
150 u""" お守り種類の選択が変わった時の動作 """
152 for i, (combo, skill_dict) in enumerate(zip(
153 [self.frame_view.combo_box_amulet_search_skill1,
154 self.frame_view.combo_box_amulet_search_skill2],
155 [self._amulet_id2skill1_id_dict, self._amulet_id2skill2_id_dict]
157 skill_ids = skill_dict[amulet_id]
158 skill_names = [self._skill_id2name_dict[_id] for _id in skill_ids]
160 combo.SetItems(skill_names)
161 skill1_selected = skill_ids[0]
163 combo.SetItems([view.VAL_NO_SKILL] + skill_names)
165 raise NotImplementedError(u"this iteration must be conducted only twice!")
166 combo.SetSelection(0) # combo1 = skill1, combo2 = No Skill
167 self._set_spin_range(amulet_id, skill1_selected, None)
169 def _set_spin_views(self):
170 u""" Spin Ctrlの最大最小をセット"""
171 min1, max1, min2, max2 = self.db_accessor.get_skill_minmax()
172 self.frame_view.spin_ctrl_amulet_search_slot_num.SetRange(view.SLOT_MIN, view.SLOT_MAX)
173 self.frame_view.spin_ctrl_highlight.SetRange(view.THRESHOLD1_MIN, view.THRESHOLD1_MAX)
174 self.frame_view.spin_ctrl_highlight.SetValue(self._highlight_threshold1)
176 def _set_check_list_views(self):
177 u""" CheckListBoxの値のセット """
178 checklist = self.frame_view.check_list_box_highlight_skill
179 checklist.SetItems(self._skill_name2id_dict.keys())
180 checklist.SetCheckedStrings([self._skill_id2name_dict[x] for x in self._highlight_skills])
182 def _set_spin_range(self, amulet_id, skill1_id, skill2_id):
183 u""" スキルに応じてSpinCtrlの最大最小をセットする """
184 if skill2_id is None:
185 minmax_dict = self.db_accessor.select_minmax_from_skill_ids(amulet_id, [skill1_id])
186 min1, max1 = minmax_dict[skill1_id][0], minmax_dict[skill1_id][1]
189 minmax_dict = self.db_accessor.select_minmax_from_skill_ids(amulet_id, [skill1_id, skill2_id])
190 min1, max1 = minmax_dict[skill1_id][0], minmax_dict[skill1_id][1]
191 min2, max2 = minmax_dict[skill2_id][2], minmax_dict[skill2_id][3]
192 self.frame_view.spin_ctrl_amulet_search_skill1_val.SetRange(min1, max1)
193 self.frame_view.spin_ctrl_amulet_search_skill2_val.SetRange(min2, max2)
195 def OnClickSeedSearch(self, evt):
196 u""" search seeds from selected skills """
197 amu_id2skill_id_list_dict = {}
198 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
199 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
200 amu_id = self._amulet_name2id_dict[amu_name]
202 for combo in self.frame_view.combo_box_skill_dict[amu_key]:
203 name = combo.GetStringSelection()
204 if name not in self._skill_name2id_dict:
207 ls.append(self._skill_name2id_dict[name])
208 amu_id2skill_id_list_dict[amu_id] = ls
209 seed_sets = self.db_accessor.select_seeds(amu_id2skill_id_list_dict)
210 self.frame_view.text_ctrl_seed_result.SetValue(u"""Seedの候補は{0}個です。""".format(len(seed_sets)))
212 if len(seed_sets) > 0:
213 self.frame_view.list_box_seed.SetItems([u"{0}".format(seed) for seed in seed_sets])
214 self.frame_view.list_box_seed.SetSelection(0)
215 self.frame_view.button_skill_from_seed_search.Enable()
217 self.frame_view.list_box_seed.Clear()
220 def OnClickSeedClear(self, evt):
221 u""" reset seed search settings of combo box"""
222 combo_dict = self.frame_view.combo_box_skill_dict
223 for amu_key in [view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3]:
224 for combo in combo_dict[amu_key]:
225 combo.SetSelection(0)
226 self.frame_view.text_ctrl_seed_result.SetValue(u"")
227 self.frame_view.button_skill_from_seed_search.Disable()
228 self.frame_view.list_box_seed.Clear()
230 def OnClickSkillSearchFromSeed(self, evt):
231 u""" change page to skill search from seed"""
232 seed = self.frame_view.list_box_seed.GetStringSelection()
234 self.frame_view.text_ctrl_seed_select.SetValue(seed)
235 self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
236 self.OnClickSkillSearch(evt)
238 def OnClickSkillSearch(self, evt):
239 u""" skill search from seed"""
240 seed = self.frame_view.text_ctrl_seed_select.GetValue()
243 skill_dict, threshold1_dict = self.db_accessor.select_skills_from_seeds([seed])
246 text_ctrl_dict = self.frame_view.text_ctrl_seed_skill_dict
247 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
248 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
249 amu_id = self._amulet_name2id_dict[amu_name]
250 for txt_ctr, skill_id in zip(text_ctrl_dict[amu_key], skill_dict[amu_id][seed]):
251 txt_ctr.SetValue(self._skill_id2name_dict[skill_id])
252 for txt_ctr, threshold1 in zip(text_ctrl_dict[view.KEY_THRESHOLD1], threshold1_dict[seed]):
253 txt_ctr.SetValue(u"{0}".format(threshold1))
254 self._update_highlight()
257 self._show_message_dialog(message=u"指定されたSeed値は存在しません")
259 self._show_message_dialog(message=u"Seed値には数字を入力してください")
261 def OnClickSkillClear(self, evt):
262 u""" clear skills from seed """
263 text_ctrl_dict = self.frame_view.text_ctrl_seed_skill_dict
264 for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
265 [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
266 amu_id = self._amulet_name2id_dict[amu_name]
267 for txt_ctr in text_ctrl_dict[amu_key]:
269 for txt_ctr in text_ctrl_dict[view.KEY_THRESHOLD1]:
271 self.frame_view.list_box_seed_skill_amulet_prospect.Clear()
273 def OnClickAmuletRadio(self, evt):
274 u""" switch skill lists by amulet id """
276 if btn_id == self.frame_view.ID_RADIO_BUTTON_AMULET1:
277 amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
278 elif btn_id == self.frame_view.ID_RADIO_BUTTON_AMULET2:
279 amu_id = self._amulet_name2id_dict[view.NAME_AMULET2]
280 elif btn_id == self.frame_view.ID_RADIO_BUTTON_AMULET3:
281 amu_id = self._amulet_name2id_dict[view.NAME_AMULET3]
284 self._set_skill_list_from_amulet(amu_id)
286 def OnClickAmuletCombo(self, evt):
287 u""" switch skill minmax by amulet id and skill_id"""
288 skill1_name = self.frame_view.combo_box_amulet_search_skill1.GetStringSelection()
289 skill2_name = self.frame_view.combo_box_amulet_search_skill2.GetStringSelection()
290 skill1_id = self._skill_name2id_dict[skill1_name]
291 if skill2_name in self._skill_name2id_dict:
292 skill2_id = self._skill_name2id_dict[skill2_name]
295 if self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].GetValue():
296 amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
297 elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET2].GetValue():
298 amu_id = self._amulet_name2id_dict[view.NAME_AMULET2]
299 elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET3].GetValue():
300 amu_id = self._amulet_name2id_dict[view.NAME_AMULET3]
302 raise IndexError(u"amulet id is unknown")
303 self._set_spin_range(amu_id, skill1_id, skill2_id)
305 def OnClickAmuletSearch(self, evt):
306 u""" search seeds from amulet condition """
307 skill1_name = self.frame_view.combo_box_amulet_search_skill1.GetStringSelection()
308 skill2_name = self.frame_view.combo_box_amulet_search_skill2.GetStringSelection()
309 skill1_val = self.frame_view.spin_ctrl_amulet_search_skill1_val.GetValue()
310 skill2_val = self.frame_view.spin_ctrl_amulet_search_skill2_val.GetValue()
311 slot_val = self.frame_view.spin_ctrl_amulet_search_slot_num.GetValue()
312 if self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].GetValue():
313 amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
314 elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET2].GetValue():
315 amu_id = self._amulet_name2id_dict[view.NAME_AMULET2]
316 elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET3].GetValue():
317 amu_id = self._amulet_name2id_dict[view.NAME_AMULET3]
319 raise IndexError(u"amulet id is unknown")
321 list_box = self.frame_view.list_box_amulet_search_seeds
322 if skill1_name == skill2_name:
323 self._show_message_dialog(message=u"異なるスキルを選択してください")
324 elif skill1_val == 0:
325 self._show_message_dialog(message=u"第1スキルの値には0以外を指定してください")
327 skill1_id = self._skill_name2id_dict[skill1_name]
328 if skill2_name in self._skill_name2id_dict:
329 skill2_id = self._skill_name2id_dict[skill2_name]
331 skill2_id = None # for skill2 is 0
333 tup = self.db_accessor.get_sufficient_value(
334 amu_id, skill1_id, skill2_id, skill1_val, skill2_val)
337 seeds_set = self.db_accessor.select_seeds_from_sufficient_val(amu_id, suff_val, slot_val, skill2_id)
338 if len(seeds_set) > 0:
339 self.frame_view.text_ctrl_amulet_search_result.SetValue(
340 u"{0}個のSeedで出現するお守りです".format(len(seeds_set)))
341 list_box.SetItems([u"{0}".format(seed) for seed in seeds_set])
342 list_box.SetSelection(0)
343 self.frame_view.button_amulet_search_skill.Enable()
345 self.frame_view.text_ctrl_amulet_search_result.SetValue(
346 u"指定されたお守りは見つかりませんでした")
347 self.frame_view.button_amulet_search_skill.Disable()
350 self.frame_view.text_ctrl_amulet_search_result.SetValue(
352 self.frame_view.button_amulet_search_skill.Disable()
355 def OnClickAmuletClear(self, evt):
356 u""" clear amulet conditions """
357 amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
358 self._set_skill_list_from_amulet(amu_id)
359 self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].SetValue(True)
360 self.frame_view.button_amulet_search_skill.Disable()
361 self.frame_view.text_ctrl_amulet_search_result.SetValue(u"")
362 self.frame_view.list_box_amulet_search_seeds.Clear()
364 def OnClickSkillSearchFromAmulet(self, evt):
365 u""" change page to skill search from amulet"""
366 seed = self.frame_view.list_box_amulet_search_seeds.GetStringSelection()
368 self.frame_view.text_ctrl_seed_select.SetValue(seed)
369 self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
370 self.OnClickSkillSearch(evt)
372 def OnClickSettingOK(self, evt):
373 u""" get settings of setting tab """
374 self._highlight_threshold1 = self.frame_view.spin_ctrl_highlight.GetValue()
375 self._highlight_skills = set([self._skill_name2id_dict[x] for x in
376 self.frame_view.check_list_box_highlight_skill.GetCheckedStrings()
377 if x in self._skill_name2id_dict])
378 self._update_highlight()
380 def OnClickSettingClear(self, evt):
381 u""" reset settings of setting tab """
382 self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
383 self.frame_view.spin_ctrl_highlight.SetValue(view.HIGHLIGHT_THRESHOLD1)
384 self._highlight_skills = set()
385 for idx in self.frame_view.check_list_box_highlight_skill.GetChecked():
386 self.frame_view.check_list_box_highlight_skill.Check(idx, False)
387 self._update_highlight()
389 def _update_highlight(self):
390 u""" update highlight cells """
391 text_dict = self.frame_view.text_ctrl_seed_skill_dict
392 for key in [view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3]:
393 for text_ctrl in text_dict[key]:
394 val = text_ctrl.GetValue()
395 if (val in self._skill_name2id_dict and
396 self._skill_name2id_dict[val] in self._highlight_skills):
397 text_ctrl.SetBackgroundColour("Yellow")
399 text_ctrl.SetBackgroundColour(wx.NullColor)
400 for text_ctrl in text_dict[view.KEY_THRESHOLD1]:
401 val = text_ctrl.GetValue()
402 if val.isdigit() and int(val) >= self._highlight_threshold1:
403 text_ctrl.SetBackgroundColour("Yellow")
405 text_ctrl.SetBackgroundColour(wx.NullColor)
408 def _show_error_dialog(self, message=u"予期せぬエラーが発生しました", caption=u"エラー"):
410 OKボタンが押されたらアプリケーションを終了する
412 dlg = wx.MessageDialog(self.frame_view.frame,
414 caption, wx.OK | wx.ICON_ERROR)
419 def _show_message_dialog(self, message, caption=u"メッセージ"):
422 dlg = wx.MessageDialog(self.frame_view.frame,
424 caption, wx.OK | wx.ICON_INFORMATION)
428 def CloseHandler(self, evt):
429 dlg = wx.MessageDialog(parent = self.frame_view.frame,
430 message = u"終了します。よろしいですか?",
433 result = dlg.ShowModal()
435 if result == wx.ID_YES:
436 self._write_settings()
439 def OnClose(self, evt):
440 self.frame_view.Close()
442 def OnAboutBox(self, evt):
443 info = self.frame_view.GetAboutInfo()
446 def _write_settings(self):
447 with open(SETTING_FILE, mode="w") as f:
448 data = {SETTING_THRESHOLD1:self._highlight_threshold1,
449 SETTING_SKILLS:self._highlight_skills}
452 def _read_settings(self):
453 if os.path.exists(SETTING_FILE):
454 with open(SETTING_FILE, mode="r") as f:
456 data = pickle.load(f)
457 self._highlight_threshold1 = data[SETTING_THRESHOLD1]
458 self._highlight_skills = data[SETTING_SKILLS]
460 self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
461 self._highlight_skills = set()
463 self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
464 self._highlight_skills = set()
466 if __name__ == "__main__":
467 app = AmuletToolController(False)