OSDN Git Service

add alchemy simulator
[amulettoolsmh4/main.git] / amulettool.py
index ae047f4..6c9911b 100644 (file)
@@ -1,4 +1,491 @@
+#!/usr/bin/python
 # -*- coding: utf-8 -*-
 
-# \82¨\8eç\82è\83X\83L\83\8b\82ÌSeed\93Á\92è\83N\83\89\83X
+# お守りスキルのSeed特定ツールのコントロールクラス
 # 2013/12/04 written by kei9
+
+import threading
+import time
+import os.path
+import sys
+import pickle
+import sqlite3
+
+import wx
+
+import view
+import model
+import convertcoding
+
+SETTING_FILE = u"settings"
+SETTING_THRESHOLD1 = u"threshold1"
+SETTING_THRESHOLD2 = u"threshold2"
+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
+        #1basedir = os.path.dirname(__file__)
+        basedir = os.path.dirname(os.path.abspath(__file__))
+    return convertcoding.convert_unicode(basedir)
+
+
+class AmuletToolController(wx.App):
+    u""" アプリケーションの制御クラス """
+    def OnInit(self):
+        self._read_settings()
+        self.frame_view = view.MainFrameView(os.path.join(_get_base_dir(), u"view", view.XRC_MAIN_FRAME))
+
+        self._init_events()
+
+        self.frame_view.Show()
+
+        self.frame_view.DisableNoteBook()
+
+        self._init_database()
+        self._init_notebook_seed1()
+        self._init_notebook_seed2()
+        self._init_notebook_skill1()
+        self._init_notebook_skill2()
+        self._init_notebook_setting()
+        self._init_notebook_simulator()
+        self._init_notebook_amulet()
+
+        self.frame_view.EnableNoteBook()
+        return True
+
+    def _init_events(self):
+        u"""イベント登録"""
+        frame = self.frame_view.frame
+
+        # 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_MENU, self.OnMemoBox, id=self.frame_view.ID_MENU_MEMO)
+        frame.Bind(wx.EVT_CLOSE, self.CloseHandler)
+
+        # button event
+
+    def _init_database(self):
+        u""" DBの初期設定 """
+
+        if not os.path.exists(model.DB_FILE_NAME):
+            u""" DBが存在しない時は再生成する """
+            frame = self.frame_view.frame
+            try:
+                generator = model.DataBaseGenerator(model.DB_FILE_NAME)
+                #generator = model.DataBaseGenerator()
+
+                dlg_view = view.GaugeDialogView(os.path.join(_get_base_dir(), u"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
+        try:
+            self.db_accessor = model.DataBaseAccessor(model.DB_FILE_NAME)
+        except sqlite3.Error as e:
+            self._show_error_dialog(u"データベースが壊れています")
+
+        self._minmax_dict = self.db_accessor.get_minmax_dict()
+        self._amulet_names = self.db_accessor.get_id_sorted_amulet_names()
+        self._skill_names = self.db_accessor.get_id_sorted_skill_names()
+
+    def _init_notebook_seed1(self):
+        u""" Seed1特定タブのviewの初期化 """
+        self.notebook_seed1_view = self.frame_view.notebook_seed1_view
+        self.notebook_seed1_view.bind_button_search(self.OnClickSeed1Search)
+        self.notebook_seed1_view.bind_button_clear(self.OnClickSeed1Clear)
+        self.notebook_seed1_view.bind_button_skill1(self.OnClickSkill1SearchFromSeed1)
+        self.notebook_seed1_view.set_skill1_button_enable(False)
+
+        u" 各種お守りの第1スキル選択のセット "
+        name2skill1s = {}
+        for name in self._amulet_names:
+            name2skill1s[name] = tuple([x for x in self._skill_names if x in self._minmax_dict[name][0]])
+        self.notebook_seed1_view.set_amuletname_skillnames_dict(self._amulet_names, name2skill1s)
+
+    def _init_notebook_seed2(self):
+        u""" Seed2特定タブのviewの初期化 """
+        self.notebook_seed2_view = self.frame_view.notebook_seed2_view
+        self.notebook_seed2_view.bind_button_search(self.OnClickSeed2Search)
+        self.notebook_seed2_view.bind_button_clear(self.OnClickSeed2Clear)
+        self.notebook_seed2_view.bind_button_skill2(self.OnClickSkill2SearchFromSeed2)
+
+        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]):
+            skill_names = [view.VAL_NO_SKILL] + [x for x in self._skill_names 
+                    if x in self._minmax_dict[amu_name][1]]
+            self.notebook_seed2_view.set_skill_names(amu_key, skill_names)
+        self.notebook_seed2_view.set_skill_selected_idx(0)
+
+    def _init_notebook_skill2(self):
+        u""" Seed2によるSkill2一覧タブのviewの初期化 """
+        self.notebook_skill2_view = self.frame_view.notebook_skill2_view
+        self.notebook_skill2_view.bind_button_search(self.OnClickSkill2Search)
+        self.notebook_skill2_view.bind_button_clear(self.OnClickSkill2Clear)
+
+    def _init_notebook_skill1(self):
+        u""" Seed1によるSkill1一覧タブのviewの初期化 """
+        self.notebook_skill1_view = self.frame_view.notebook_skill1_view
+        self.notebook_skill1_view.bind_button_search(self.OnClickSkill1Search)
+        self.notebook_skill1_view.bind_button_clear(self.OnClickSkill1Clear)
+
+    def _init_notebook_simulator(self):
+        u""" Seed1,Seed2による錬金シミュレータータブのviewの初期化 """
+        self.notebook_simulator_view = self.frame_view.notebook_simulator_view
+        self.notebook_simulator_view.bind_button_search(self.OnClickSimulatorSearch)
+        self.notebook_simulator_view.bind_button_clear(self.OnClickSimulatorClear)
+
+
+    def _update_notebook_amulet(self, amulet_id=None):
+        u""" お守り検索タブのviewの更新
+        amulet_id が Noneでない場合、お守りの種類に応じてComboboxを再設定する
+        また、選択中のスキルに応じて最大最小を設定する"""
+        pass
+
+
+    def _update_notebook_amulet_seed2s(self):
+        u"""お守り検索タブのSeed2リストを更新する"""
+        pass
+
+    def _init_notebook_amulet(self):
+        u""" お守り検索タブの初期設定 """
+        self.notebook_amulet_view = self.frame_view.notebook_amulet_view
+        self.notebook_amulet_view.bind_radiobox_amulet(self.OnClickAmuletRadioAmulet)
+        self.notebook_amulet_view.bind_radiobox_threshold_type(self.OnClickAmuletRadioThresholdType)
+        self.notebook_amulet_view.bind_combobox(self.OnClickAmuletCombo)
+        self.notebook_amulet_view.bind_button_search(self.OnClickAmuletSearch)
+        self.notebook_amulet_view.bind_button_clear(self.OnClickAmuletClear)
+        self.notebook_amulet_view.bind_button_skill(self.OnClickSkillSearchFromAmulet)
+
+        self.notebook_amulet_view.set_radio_value(True, view.NAME_AMULET1)
+        self._update_notebook_amulet()
+
+    def _init_notebook_setting(self):
+        u""" 設定タブの初期設定 """
+        self.notebook_setting_view = self.frame_view.notebook_setting_view
+        self.notebook_setting_view.bind_button_ok(self.OnClickSettingOK)
+        self.notebook_setting_view.bind_button_clear(self.OnClickSettingClear)
+        self.notebook_setting_view.set_skill_strings(self._skill_names)
+        self._update_notebook_setting()
+
+    def _update_notebook_setting(self):
+        u"""設定タブの値更新"""
+        self.notebook_setting_view.set_threshold(self._highlight_threshold1,
+                self._highlight_threshold2)
+        self.notebook_setting_view.set_checked_strings(self._highlight_skills)
+
+    def _update_highlight(self):
+        u""" update highlight cells """
+        self.notebook_skill2_view.set_skill2_highlight(
+                self._highlight_skills, self._highlight_threshold1, self._highlight_threshold2)
+        self.notebook_skill1_view.update_highlight(self._highlight_skills)
+
+    u""" Seed1 view's event"""
+    def OnClickSeed1Search(self, evt):
+        u""" search seed1s from selected skill1s """
+        alchemy_type = self.notebook_seed1_view.get_tenun_radio_key()
+        amu_skill_name_list = self.notebook_seed1_view.get_selected_amulets_and_names()
+        if alchemy_type == view.KEY_TENUN555:
+            alchemy_type = model.KEY_TENUN555
+        elif alchemy_type == view.KEY_TENUN888:
+            alchemy_type = model.KEY_TENUN888
+        else:
+            raise KeyError(u"key '{0}' not found in alchemy type".format(alchemy_type))
+        seed1s = self.db_accessor.select_seed1s(amu_skill_name_list, alchemy_type)
+        if len(seed1s) == 0:
+            self.notebook_seed1_view.set_text_result(u"条件に一致するSeed1は見つかりません")
+            self.notebook_seed1_view.set_skill1_button_enable(False)
+        elif len(seed1s) == 1:
+            seed1 = [x for x in seed1s][0]
+            no, table_no, result_num = self.db_accessor.select_table_nos_from_seed1(seed1, alchemy_type)
+            self.notebook_seed1_view.set_text_result_by_seed1(seed1, table_no, no)
+            self.notebook_seed1_view.set_skill1_button_enable(True)
+        else:
+            self.notebook_seed1_view.set_text_result(
+                    u"Seed1は{0}件あります。条件を絞ってください".format(len(seed1s)))
+            self.notebook_seed1_view.set_skill1_button_enable(False)
+
+    def OnClickSeed1Clear(self, evt):
+        u""" clear seed1s from selected skill1s """
+        self.notebook_seed1_view.clear_combobox()
+        self.notebook_seed1_view.clear_text_result()
+
+    def OnClickSkill1SearchFromSeed1(self, evt):
+        u""" change page to skill1 search from seed1"""
+        seed1 = self.notebook_seed1_view.get_result_seed1()
+        alchemy_type = self.notebook_seed1_view.get_tenun_radio_key()
+        if seed1 is not None:
+            self.notebook_skill1_view.set_seed1_value(seed1)
+            self.notebook_skill1_view.set_tenun_radio_key(alchemy_type)
+            self.frame_view.note_book.SetSelection(view.SKILL1_SEARCH_PAGE)
+            self.OnClickSkill1Search(evt)
+
+    u""" Seed2 view's event """
+    def OnClickSeed2Search(self, evt):
+        u""" search seed2s from selected skill2s """
+        amu2skills_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]):
+            names = self.notebook_seed2_view.get_selected_skill_names(amu_key)
+            amu2skills_dict[amu_name] = [name if name in self._skill_names else None for name in names]
+        seed_sets = self.db_accessor.select_seed2s(amu2skills_dict)
+        self.notebook_seed2_view.set_result_text(u"""Seedの候補は{0}個です。""".format(len(seed_sets)))
+
+        if len(seed_sets) > 0:
+            self.notebook_seed2_view.set_seed_lists([u"{0}".format(seed) for seed in sorted(seed_sets)])
+            self.notebook_seed2_view.set_skill2_button_enable(True)
+        else:
+            self.notebook_seed2_view.clear_seed_list()
+            self.notebook_seed2_view.set_skill2_button_enable(False)
+
+    def OnClickSeed2Clear(self, evt):
+        u""" reset seed2 search settings of combo box"""
+        self.notebook_seed2_view.set_skill_selected_idx(0)
+        self.notebook_seed2_view.clear_seed_list()
+        self.notebook_seed2_view.set_result_text(u"")
+        self.notebook_seed2_view.set_skill2_button_enable(False)
+
+    def OnClickSkill2SearchFromSeed2(self, evt):
+        u""" change page to skill2 search from seed2"""
+        seed2 = self.notebook_seed2_view.get_selected_seed2()
+        if seed2 is not None:
+            self.notebook_skill2_view.set_seed2_value(seed2)
+            self.frame_view.note_book.SetSelection(view.SKILL2_SEARCH_PAGE)
+            self.OnClickSkill2Search(evt)
+
+    u""" Skill1 search from Seed1's event """
+    def OnClickSkill1Search(self, evt):
+        u""" skill1 search from seed1"""
+        seed1 = self.notebook_skill1_view.get_seed1_value()
+        alchemy_type = self.notebook_skill1_view.get_tenun_radio_key()
+        if seed1 is not None:
+            try:
+                no, table_no, result_num = self.db_accessor.select_table_nos_from_seed1(seed1, alchemy_type)
+                self.notebook_skill1_view.set_text_result_by_seed1(seed1, table_no, no)
+                # list of (no, seed1)
+                near_num = view.NEAR_SEED1_NUMBERS
+                no_seed1_dict = self.db_accessor.select_near_seed1s_from_table_no(
+                        no, table_no, near_num, near_num, alchemy_type)
+
+                # no -> (seed1, result_num, amu_names, skill_names)
+                self.notebook_skill1_view.set_no2seed1s_dict(no_seed1_dict, no)
+                self.notebook_skill1_view.update_highlight(self._highlight_skills)
+            except KeyError, e:
+                self._show_message_dialog(message=u"指定されたSeed値は存在しません")
+        else:
+            self._show_message_dialog(message=u"Seed値には数字を入力してください")
+
+    def OnClickSkill1Clear(self, evt):
+        u""" clear skills from seed """
+        self.notebook_skill1_view.clear_skill1_grid()
+        self.notebook_skill1_view.clear_highlight()
+        self.notebook_skill1_view.clear_result_text()
+
+    u""" Skill2 search from Seed2's event """
+    def OnClickSkill2Search(self, evt):
+        u""" skill search from seed2"""
+        seed2 = self.notebook_skill2_view.get_seed2_value()
+        if seed2 is not None:
+            table_no, no, skill_dict, th1s, th2s = self.db_accessor.select_names_from_seed2(seed2)
+            try:
+                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]):
+                    skill_names = skill_dict[amu_name]
+                    self.notebook_skill2_view.set_skill2_by_col_key(amu_key, skill_names)
+                th_vals = [u"{0}".format(x) for x in th1s]
+                self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD1, th_vals)
+                th_vals = [u"{0}".format(x) for x in th2s]
+                self.notebook_skill2_view.set_skill2_by_col_key(view.KEY_THRESHOLD2, th_vals)
+                #inishie
+                skill_name, th1, th2 = self.db_accessor.select_inishie_skill2_from_seed2(seed2)
+                self.notebook_skill2_view.set_inishie(skill_name, th1, th2)
+                # explanation
+                self.notebook_skill2_view.set_result_text(
+                        u"SEED2: {2}, 通し番号: {1}, テーブルNo: {0}".format(table_no, no, seed2))
+            except KeyError, e:
+                self._show_message_dialog(message=u"指定されたSeed値は存在しません")
+            finally:
+                self._update_highlight()
+
+        else:
+            self._show_message_dialog(message=u"Seed値には数字を入力してください")
+
+    def OnClickSkill2Clear(self, evt):
+        u""" clear skills from seed """
+        self.notebook_skill2_view.clear_skill2_grid()
+        self.notebook_skill2_view.clear_skill2_highlight()
+        self.notebook_skill2_view.clear_result_text()
+        self.notebook_skill2_view.clear_inishie()
+
+    u""" Alchemy Simulator's event """
+    def OnClickSimulatorSearch(self, evt):
+        u""" alchemy simulation from seed1, seed2"""
+        seeds = self.notebook_simulator_view.get_seed_values()
+        alchemy_type = self.notebook_simulator_view.get_tenun_radio_key()
+        if seeds is not None:
+            seed1, seed2 = seeds
+            #try:
+            result = self.db_accessor.simulate_nazo(seed1, seed2)
+            self.notebook_simulator_view.set_values(view.KEY_NAZO, *result)
+            result = self.db_accessor.simulate_komyou(seed1, seed2)
+            self.notebook_simulator_view.set_values(view.KEY_KOMYOU, *result)
+            result = self.db_accessor.simulate_inishie(seed1, seed2)
+            self.notebook_simulator_view.set_values(view.KEY_INISHIE, *result)
+            result = self.db_accessor.simulate_tenun(seed1, seed2, alchemy_type)
+            self.notebook_simulator_view.set_tenun_values(result)
+            #except KeyError, e:
+            #   self._show_message_dialog(message=u"指定されたSeed値は存在しません")
+            #finally:
+            self.notebook_simulator_view.update_highlight(self._highlight_skills)
+        else:
+            self._show_message_dialog(message=u"Seed値には数字を入力してください")
+
+    def OnClickSimulatorClear(self, evt):
+        u""" clear simulator form """
+        self.notebook_simulator_view.clear_seed_values()
+        self.notebook_simulator_view.clear_grid()
+        self.notebook_simulator_view.clear_highlight()
+
+    u""" amulet search event """
+    def OnClickAmuletRadioAmulet(self, evt):
+        u""" switch skill lists by amulet id """
+        #btn_id = evt.GetId()
+        amulet_name = self.notebook_amulet_view.get_selected_amulet()
+        amu_id = self._amulet_name2id_dict[amulet_name]
+        self._update_notebook_amulet(amu_id)
+        self.notebook_amulet_view.set_result_text_ctrl_value(u"")
+        self.notebook_amulet_view.set_skill_button_enable(False)
+        self.notebook_amulet_view.clear_grid()
+
+    def OnClickAmuletRadioThresholdType(self, evt):
+        u""" switch seed lists by threshold_type """
+        self._update_notebook_amulet_seed2s()
+
+    def OnClickAmuletCombo(self, evt):
+        u""" switch skill minmax by amulet id and skill_id"""
+        self._update_notebook_amulet()
+
+    def OnClickAmuletSearch(self, evt):
+        u""" search seeds from amulet condition """
+        pass
+
+    def OnClickAmuletClear(self, evt):
+        u""" clear amulet conditions """
+        pass
+
+    def OnClickSkillSearchFromAmulet(self, evt):
+        u""" change page to skill search from amulet"""
+        seed = self.notebook_amulet_view.get_grid_selected_seed2()
+        if seed is not None:
+            self.notebook_skill2_view.set_seed2_value(seed)
+            self.frame_view.note_book.SetSelection(view.SKILL_SEARCH_PAGE)
+            self.OnClickSkillSearch(evt)
+
+    u""" settings' event """
+    def OnClickSettingOK(self, evt):
+        u""" get settings of setting tab """
+        (self._highlight_threshold1, 
+                self._highlight_threshold2) = self.frame_view.notebook_setting_view.get_threshold()
+        self._highlight_skills = set([x for x in self.frame_view.notebook_setting_view.get_checked_strings()
+                if x in self._skill_names])
+        self._update_highlight()
+
+    def OnClickSettingClear(self, evt):
+        u""" reset settings of setting tab """
+        self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
+        self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
+        self._highlight_skills = set()
+        self._update_notebook_setting()
+        self._update_highlight()
+
+    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()
+
+    def OnAboutBox(self, evt):
+        info = self.frame_view.GetAboutInfo()
+        wx.AboutBox(info)
+
+    def OnMemoBox(self, evt):
+        dlg_view = view.MemoDialogView(os.path.join(_get_base_dir(), u"view", view.XRC_MEMO_DIALOG))
+        dlg_view.Show()
+
+    def _write_settings(self):
+        with open(SETTING_FILE, mode="w") as f:
+            data = {SETTING_THRESHOLD1:self._highlight_threshold1, 
+                    SETTING_THRESHOLD2:self._highlight_threshold2, 
+                    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_threshold2 = data[SETTING_THRESHOLD2] 
+                    self._highlight_skills = data[SETTING_SKILLS]
+                except EOFError, e:
+                    self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
+                    self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
+                    self._highlight_skills = set()
+        else:
+            self._highlight_threshold1 = view.HIGHLIGHT_THRESHOLD1
+            self._highlight_threshold2 = view.HIGHLIGHT_THRESHOLD2
+            self._highlight_skills = set()
+
+if __name__ == "__main__":
+    app = AmuletToolController(False)
+    app.MainLoop()
+