import threading
import time
import os.path
+import sys
+import pickle
+import sqlite3
import wx
import view
import model
+SETTING_FILE = u"settings"
+SETTING_THRESHOLD1 = u"threshold1"
+SETTING_SKILLS = u"skills"
+
+def _get_base_dir():
+ u""" for pyinstaller 2.1 """
+ if getattr(sys, 'frozen', False):
+ # we are running in a |PyInstaller| bundle
+ basedir = sys._MEIPASS
+ else:
+ # we are running in a normal Python environment
+ basedir = os.path.dirname(__file__)
+ return basedir
class AmuletToolController(wx.App):
u""" アプリケーションの制御クラス """
def OnInit(self):
- self.frame_view = view.MainFrameView("view")
-
+ self._read_settings()
+ self.frame_view = view.MainFrameView(os.path.join(_get_base_dir(), "view", view.XRC_MAIN_FRAME))
+
self._init_events()
self.frame_view.Show()
+ self.frame_view.DisableNoteBook()
+
self._init_database()
+ self._set_views()
+
+ self.frame_view.EnableNoteBook()
return True
def _init_events(self):
# menu event
frame.Bind(wx.EVT_MENU, self.OnClose, id=self.frame_view.ID_MENU_ITEM_EXIT)
frame.Bind(wx.EVT_MENU, self.OnAboutBox, id=self.frame_view.ID_MENU_ITEM_ABOUT)
+ frame.Bind(wx.EVT_CLOSE, self.CloseHandler)
+
+ # button event
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSeedSearch, id=self.frame_view.ID_BUTTON_SEED_SEARCH)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSeedClear, id=self.frame_view.ID_BUTTON_SEED_CLEAR)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSkillSearchFromSeed, id=self.frame_view.ID_BUTTON_SKILL_FROM_SEED_SEARCH)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSkillSearch, id=self.frame_view.ID_BUTTON_SKILL_SEARCH)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSkillClear, id=self.frame_view.ID_BUTTON_SKILL_CLEAR)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickAmuletSearch, id=self.frame_view.ID_BUTTON_AMULET_SEARCH_SEARCH)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickAmuletClear, id=self.frame_view.ID_BUTTON_AMULET_SEARCH_CLEAR)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSkillSearchFromAmulet, id=self.frame_view.ID_BUTTON_AMULET_SEARCH_SKILL)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSettingOK, id=self.frame_view.ID_BUTTON_SETTING_OK)
+ frame.Bind(wx.EVT_BUTTON, self.OnClickSettingClear, id=self.frame_view.ID_BUTTON_SETTING_CLEAR)
+
+ # radio button event
+ frame.Bind(wx.EVT_RADIOBUTTON, self.OnClickAmuletRadio, id=self.frame_view.ID_RADIO_BUTTON_AMULET1)
+ frame.Bind(wx.EVT_RADIOBUTTON, self.OnClickAmuletRadio, id=self.frame_view.ID_RADIO_BUTTON_AMULET2)
+ frame.Bind(wx.EVT_RADIOBUTTON, self.OnClickAmuletRadio, id=self.frame_view.ID_RADIO_BUTTON_AMULET3)
+
+ # combo box event
+ frame.Bind(wx.EVT_COMBOBOX, self.OnClickAmuletCombo, id=self.frame_view.ID_COMBO_BOX_AMULET_SEARCH_SKILL1)
+ frame.Bind(wx.EVT_COMBOBOX, self.OnClickAmuletCombo, id=self.frame_view.ID_COMBO_BOX_AMULET_SEARCH_SKILL2)
def _init_database(self):
u""" DBの初期設定 """
if not os.path.exists(model.DB_FILE_NAME):
u""" DBが存在しない時は再生成する """
frame = self.frame_view.frame
- generator = model.DataBaseGenerator(model.DB_FILE_NAME)
- #generator = model.DataBaseGenerator()
-
- dlg_view = view.GaugeDialogView("view")
- def _loop():
- while t1.is_alive():
- dlg_view.gauge.Pulse()
- time.sleep(0.2)
- dlg_view.finish_generation()
-
- t1 = threading.Thread(target=generator.generate_db)
- t2 = threading.Thread(target=_loop)
- t1.start()
- t2.start()
-
- dlg_view.ShowModal()
- t1.join()
- t2.join()
- dlg_view.Destroy()
+ try:
+ generator = model.DataBaseGenerator(model.DB_FILE_NAME)
+ #generator = model.DataBaseGenerator()
+
+ dlg_view = view.GaugeDialogView(os.path.join(_get_base_dir(), "view", view.XRC_GAUGE_DIALOG))
+ def _loop():
+ while t1.is_alive():
+ dlg_view.gauge.Pulse()
+ time.sleep(0.2)
+ dlg_view.finish_generation()
+
+ t1 = threading.Thread(target=generator.generate_db)
+ t2 = threading.Thread(target=_loop)
+ t1.start()
+ t2.start()
+
+ dlg_view.ShowModal()
+ t1.join()
+ t2.join()
+ dlg_view.Destroy()
+ except sqlite3.Error as e:
+ self._show_error_dialog(u"データベース生成中にエラーが発生しました")
# access to db
- db_accessor = model.DataBaseAccessor(model.DB_FILE_NAME)
+ try:
+ self.db_accessor = model.DataBaseAccessor(model.DB_FILE_NAME)
+ except sqlite3.Error as e:
+ self._show_error_dialog(u"データベースが壊れています")
+
+ # get dictionaries
+ (self._skill_id2name_dict,
+ self._skill_name2id_dict,
+ self._amulet_id2name_dict,
+ self._amulet_name2id_dict,
+ self._amulet_id2skill1_id_dict,
+ self._amulet_id2skill2_id_dict) = self.db_accessor.get_dicts()
+
+
+ def _set_views(self):
+ u""" GUIにDBの各種値をセットする """
+ self._set_combo_views()
+ self._set_spin_views()
+ self._set_check_list_views()
+
+ def _set_combo_views(self):
+ u""" ComboBoxの値をセットする """
+ combo_dict = self.frame_view.combo_box_skill_dict
+
+ u" 各種お守りの第2スキル選択 "
+ for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
+ [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
+ amu_id = self._amulet_name2id_dict[amu_name]
+ skill_ids = self._amulet_id2skill2_id_dict[amu_id]
+ skill_names = [view.VAL_NO_SKILL] + [self._skill_id2name_dict[x] for x in skill_ids]
+ for combo in combo_dict[amu_key]:
+ combo.SetItems(skill_names)
+ combo.SetSelection(0)
+
+ # amulet search
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
+ self._set_skill_list_from_amulet(amu_id)
+ self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].SetValue(True)
+
+ def _set_skill_list_from_amulet(self, amulet_id):
+ u""" お守り種類の選択が変わった時の動作 """
+
+ for i, (combo, skill_dict) in enumerate(zip(
+ [self.frame_view.combo_box_amulet_search_skill1,
+ self.frame_view.combo_box_amulet_search_skill2],
+ [self._amulet_id2skill1_id_dict, self._amulet_id2skill2_id_dict]
+ )):
+ skill_ids = skill_dict[amulet_id]
+ skill_names = [self._skill_id2name_dict[_id] for _id in skill_ids]
+ if i == 0:
+ combo.SetItems(skill_names)
+ skill1_selected = skill_ids[0]
+ elif i == 1:
+ combo.SetItems([view.VAL_NO_SKILL] + skill_names)
+ else:
+ raise NotImplementedError(u"this iteration must be conducted only twice!")
+ combo.SetSelection(0) # combo1 = skill1, combo2 = No Skill
+ self._set_spin_range(amulet_id, skill1_selected, None)
+
+ def _set_spin_views(self):
+ u""" Spin Ctrlの最大最小をセット"""
+ min1, max1, min2, max2 = self.db_accessor.get_skill_minmax()
+ self.frame_view.spin_ctrl_amulet_search_slot_num.SetRange(view.SLOT_MIN, view.SLOT_MAX)
+ self.frame_view.spin_ctrl_highlight.SetRange(view.THRESHOLD1_MIN, view.THRESHOLD1_MAX)
+ self.frame_view.spin_ctrl_highlight.SetValue(self._highlight_threshold1)
+
+ def _set_check_list_views(self):
+ u""" CheckListBoxの値のセット """
+ checklist = self.frame_view.check_list_box_highlight_skill
+ checklist.SetItems(self._skill_name2id_dict.keys())
+ checklist.SetCheckedStrings([self._skill_id2name_dict[x] for x in self._highlight_skills])
+
+ def _set_spin_range(self, amulet_id, skill1_id, skill2_id):
+ u""" スキルに応じてSpinCtrlの最大最小をセットする """
+ if skill2_id is None:
+ minmax_dict = self.db_accessor.select_minmax_from_skill_ids(amulet_id, [skill1_id])
+ min1, max1 = minmax_dict[skill1_id][0], minmax_dict[skill1_id][1]
+ min2, max2 = 0, 0
+ else:
+ minmax_dict = self.db_accessor.select_minmax_from_skill_ids(amulet_id, [skill1_id, skill2_id])
+ min1, max1 = minmax_dict[skill1_id][0], minmax_dict[skill1_id][1]
+ min2, max2 = minmax_dict[skill2_id][2], minmax_dict[skill2_id][3]
+ self.frame_view.spin_ctrl_amulet_search_skill1_val.SetRange(min1, max1)
+ self.frame_view.spin_ctrl_amulet_search_skill2_val.SetRange(min2, max2)
+ def OnClickSeedSearch(self, evt):
+ u""" search seeds from selected skills """
+ amu_id2skill_id_list_dict = {}
+ for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
+ [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
+ amu_id = self._amulet_name2id_dict[amu_name]
+ ls = []
+ for combo in self.frame_view.combo_box_skill_dict[amu_key]:
+ name = combo.GetStringSelection()
+ if name not in self._skill_name2id_dict:
+ ls.append(None)
+ else:
+ ls.append(self._skill_name2id_dict[name])
+ amu_id2skill_id_list_dict[amu_id] = ls
+ seed_sets = self.db_accessor.select_seeds(amu_id2skill_id_list_dict)
+ self.frame_view.text_ctrl_seed_result.SetValue(u"""Seedの候補は{0}個です。""".format(len(seed_sets)))
+ if len(seed_sets) > 0:
+ self.frame_view.list_box_seed.SetItems([u"{0}".format(seed) for seed in seed_sets])
+ self.frame_view.list_box_seed.SetSelection(0)
+ self.frame_view.button_skill_from_seed_search.Enable()
+ else:
+ self.frame_view.list_box_seed.Clear()
+ def OnClickSeedClear(self, evt):
+ u""" reset seed search settings of combo box"""
+ combo_dict = self.frame_view.combo_box_skill_dict
+ for amu_key in [view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3]:
+ for combo in combo_dict[amu_key]:
+ combo.SetSelection(0)
+ self.frame_view.text_ctrl_seed_result.SetValue(u"")
+ self.frame_view.button_skill_from_seed_search.Disable()
+ self.frame_view.list_box_seed.Clear()
+
+ def OnClickSkillSearchFromSeed(self, evt):
+ u""" change page to skill search from seed"""
+ seed = self.frame_view.list_box_seed.GetStringSelection()
+ if seed.isdigit():
+ self.frame_view.text_ctrl_seed_select.SetValue(seed)
+ self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
+ self.OnClickSkillSearch(evt)
+
+ def OnClickSkillSearch(self, evt):
+ u""" skill search from seed"""
+ seed = self.frame_view.text_ctrl_seed_select.GetValue()
+ if seed.isdigit():
+ seed = int(seed)
+ skill_dict, threshold1_dict = self.db_accessor.select_skills_from_seeds([seed])
+
+ try:
+ text_ctrl_dict = self.frame_view.text_ctrl_seed_skill_dict
+ for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
+ [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
+ amu_id = self._amulet_name2id_dict[amu_name]
+ for txt_ctr, skill_id in zip(text_ctrl_dict[amu_key], skill_dict[amu_id][seed]):
+ txt_ctr.SetValue(self._skill_id2name_dict[skill_id])
+ for txt_ctr, threshold1 in zip(text_ctrl_dict[view.KEY_THRESHOLD1], threshold1_dict[seed]):
+ txt_ctr.SetValue(u"{0}".format(threshold1))
+ self._update_highlight()
+
+ except KeyError, e:
+ self._show_message_dialog(message=u"指定されたSeed値は存在しません")
+ else:
+ self._show_message_dialog(message=u"Seed値には数字を入力してください")
+
+ def OnClickSkillClear(self, evt):
+ u""" clear skills from seed """
+ text_ctrl_dict = self.frame_view.text_ctrl_seed_skill_dict
+ for amu_key, amu_name in zip([view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3],
+ [view.NAME_AMULET1, view.NAME_AMULET2, view.NAME_AMULET3]):
+ amu_id = self._amulet_name2id_dict[amu_name]
+ for txt_ctr in text_ctrl_dict[amu_key]:
+ txt_ctr.Clear()
+ for txt_ctr in text_ctrl_dict[view.KEY_THRESHOLD1]:
+ txt_ctr.Clear()
+ self.frame_view.list_box_seed_skill_amulet_prospect.Clear()
+
+ def OnClickAmuletRadio(self, evt):
+ u""" switch skill lists by amulet id """
+ btn_id = evt.GetId()
+ if btn_id == self.frame_view.ID_RADIO_BUTTON_AMULET1:
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
+ elif btn_id == self.frame_view.ID_RADIO_BUTTON_AMULET2:
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET2]
+ elif btn_id == self.frame_view.ID_RADIO_BUTTON_AMULET3:
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET3]
+ else:
+ return
+ self._set_skill_list_from_amulet(amu_id)
+
+ def OnClickAmuletCombo(self, evt):
+ u""" switch skill minmax by amulet id and skill_id"""
+ skill1_name = self.frame_view.combo_box_amulet_search_skill1.GetStringSelection()
+ skill2_name = self.frame_view.combo_box_amulet_search_skill2.GetStringSelection()
+ skill1_id = self._skill_name2id_dict[skill1_name]
+ if skill2_name in self._skill_name2id_dict:
+ skill2_id = self._skill_name2id_dict[skill2_name]
+ else:
+ skill2_id = None
+ if self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].GetValue():
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
+ elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET2].GetValue():
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET2]
+ elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET3].GetValue():
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET3]
+ else:
+ raise IndexError(u"amulet id is unknown")
+ self._set_spin_range(amu_id, skill1_id, skill2_id)
+
+ def OnClickAmuletSearch(self, evt):
+ u""" search seeds from amulet condition """
+ skill1_name = self.frame_view.combo_box_amulet_search_skill1.GetStringSelection()
+ skill2_name = self.frame_view.combo_box_amulet_search_skill2.GetStringSelection()
+ skill1_val = self.frame_view.spin_ctrl_amulet_search_skill1_val.GetValue()
+ skill2_val = self.frame_view.spin_ctrl_amulet_search_skill2_val.GetValue()
+ slot_val = self.frame_view.spin_ctrl_amulet_search_slot_num.GetValue()
+ if self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].GetValue():
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
+ elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET2].GetValue():
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET2]
+ elif self.frame_view.amulet2radio_button_dict[view.NAME_AMULET3].GetValue():
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET3]
+ else:
+ raise IndexError(u"amulet id is unknown")
+
+ list_box = self.frame_view.list_box_amulet_search_seeds
+ if skill1_name == skill2_name:
+ self._show_message_dialog(message=u"異なるスキルを選択してください")
+ elif skill1_val == 0:
+ self._show_message_dialog(message=u"第1スキルの値には0以外を指定してください")
+ else:
+ skill1_id = self._skill_name2id_dict[skill1_name]
+ if skill2_name in self._skill_name2id_dict:
+ skill2_id = self._skill_name2id_dict[skill2_name]
+ else:
+ skill2_id = None # for skill2 is 0
+ suff_dict = {}
+ tup = self.db_accessor.get_sufficient_value(
+ amu_id, skill1_id, skill2_id, skill1_val, skill2_val)
+ if tup is not None:
+ suff_val = tup[0]
+ seeds_set = self.db_accessor.select_seeds_from_sufficient_val(amu_id, suff_val, slot_val, skill2_id)
+ if len(seeds_set) > 0:
+ self.frame_view.text_ctrl_amulet_search_result.SetValue(
+ u"{0}個のSeedで出現するお守りです".format(len(seeds_set)))
+ list_box.SetItems([u"{0}".format(seed) for seed in seeds_set])
+ list_box.SetSelection(0)
+ self.frame_view.button_amulet_search_skill.Enable()
+ else:
+ self.frame_view.text_ctrl_amulet_search_result.SetValue(
+ u"指定されたお守りは見つかりませんでした")
+ self.frame_view.button_amulet_search_skill.Disable()
+ list_box.Clear()
+ else:
+ self.frame_view.text_ctrl_amulet_search_result.SetValue(
+ u"エラー。充足値が計算できません")
+ self.frame_view.button_amulet_search_skill.Disable()
+ list_box.Clear()
+
+ def OnClickAmuletClear(self, evt):
+ u""" clear amulet conditions """
+ amu_id = self._amulet_name2id_dict[view.NAME_AMULET1]
+ self._set_skill_list_from_amulet(amu_id)
+ self.frame_view.amulet2radio_button_dict[view.NAME_AMULET1].SetValue(True)
+ self.frame_view.button_amulet_search_skill.Disable()
+ self.frame_view.text_ctrl_amulet_search_result.SetValue(u"")
+ self.frame_view.list_box_amulet_search_seeds.Clear()
+
+ def OnClickSkillSearchFromAmulet(self, evt):
+ u""" change page to skill search from amulet"""
+ seed = self.frame_view.list_box_amulet_search_seeds.GetStringSelection()
+ if seed.isdigit():
+ self.frame_view.text_ctrl_seed_select.SetValue(seed)
+ self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
+ self.OnClickSkillSearch(evt)
+
+ def OnClickSettingOK(self, evt):
+ u""" get settings of setting tab """
+ self._highlight_threshold1 = self.frame_view.spin_ctrl_highlight.GetValue()
+ self._highlight_skills = set([self._skill_name2id_dict[x] for x in
+ self.frame_view.check_list_box_highlight_skill.GetCheckedStrings()
+ if x in self._skill_name2id_dict])
+ self._update_highlight()
+
+ def OnClickSettingClear(self, evt):
+ u""" reset settings of setting tab """
+ self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
+ self.frame_view.spin_ctrl_highlight.SetValue(view.HIGHLIGHT_THRESHOLD1)
+ self._highlight_skills = set()
+ for idx in self.frame_view.check_list_box_highlight_skill.GetChecked():
+ self.frame_view.check_list_box_highlight_skill.Check(idx, False)
+ self._update_highlight()
+
+ def _update_highlight(self):
+ u""" update highlight cells """
+ text_dict = self.frame_view.text_ctrl_seed_skill_dict
+ for key in [view.KEY_AMULET1, view.KEY_AMULET2, view.KEY_AMULET3]:
+ for text_ctrl in text_dict[key]:
+ val = text_ctrl.GetValue()
+ if (val in self._skill_name2id_dict and
+ self._skill_name2id_dict[val] in self._highlight_skills):
+ text_ctrl.SetBackgroundColour("Yellow")
+ else:
+ text_ctrl.SetBackgroundColour(wx.NullColor)
+ for text_ctrl in text_dict[view.KEY_THRESHOLD1]:
+ val = text_ctrl.GetValue()
+ if val.isdigit() and int(val) >= self._highlight_threshold1:
+ text_ctrl.SetBackgroundColour("Yellow")
+ else:
+ text_ctrl.SetBackgroundColour(wx.NullColor)
+
+
+ def _show_error_dialog(self, message=u"予期せぬエラーが発生しました", caption=u"エラー"):
+ u""" エラーダイアログを表示し、
+ OKボタンが押されたらアプリケーションを終了する
+ """
+ dlg = wx.MessageDialog(self.frame_view.frame,
+ message,
+ caption, wx.OK | wx.ICON_ERROR)
+ dlg.ShowModal()
+ dlg.Destroy()
+ wx.Exit()
+
+ def _show_message_dialog(self, message, caption=u"メッセージ"):
+ u""" メッセージダイアログを表示する
+ """
+ dlg = wx.MessageDialog(self.frame_view.frame,
+ message,
+ caption, wx.OK | wx.ICON_INFORMATION)
+ dlg.ShowModal()
+ dlg.Destroy()
+
+ def CloseHandler(self, evt):
+ dlg = wx.MessageDialog(parent = self.frame_view.frame,
+ message = u"終了します。よろしいですか?",
+ caption = u"終了確認",
+ style = wx.YES_NO)
+ result = dlg.ShowModal()
+ dlg.Destroy()
+ if result == wx.ID_YES:
+ self._write_settings()
+ wx.Exit()
+
def OnClose(self, evt):
self.frame_view.Close()
info = self.frame_view.GetAboutInfo()
wx.AboutBox(info)
+ def _write_settings(self):
+ with open(SETTING_FILE, mode="w") as f:
+ data = {SETTING_THRESHOLD1:self._highlight_threshold1,
+ SETTING_SKILLS:self._highlight_skills}
+ pickle.dump(data, f)
+
+ def _read_settings(self):
+ if os.path.exists(SETTING_FILE):
+ with open(SETTING_FILE, mode="r") as f:
+ try:
+ data = pickle.load(f)
+ self._highlight_threshold1 = data[SETTING_THRESHOLD1]
+ self._highlight_skills = data[SETTING_SKILLS]
+ except EOFError, e:
+ self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
+ self._highlight_skills = set()
+ else:
+ self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
+ self._highlight_skills = set()
+
if __name__ == "__main__":
app = AmuletToolController(False)
app.MainLoop()
+