class DataHolder(object): def __init__(self, data_getter): self._data = [] self.data_getter = data_getter self.filter_manager = FilterRuleManager() def update_data(self): self._data = self.data_getter() def append(self, datum): self._data.append(datum) def remove(self, datum): self._data.remove(datum) def get_specific_datum(self, key, request): for datum in self._data: if datum[key] == request: return datum # Templete method def get_displaying_data(self, request): pass def get_matched_data(self, request): return self.filter_manager.filter(self._data, request) def set_comparison_rule(self, key, rule=sub_match_request): self.filter_manager.set_comparison_rule(key, rule) def clean_comparison_rules(self): self.filter_manager.clean_comparison_rules() def set_specific_condition(self, key, request, rule=match_request): self.filter_manager.set_specific_condition(key, request, rule) def clean_specific_condition(self): self.filter_manager.clean_specific_condition() def set_sub_object_filter(self, key, filter): self.filter_manager.set_sub_object_filter(key, filter) def clean_sub_object_filter(self): self.filter_manager.clean_sub_object_filter()
class FriendRecordFrame(MainFrameWithTable): def __init__(self, master): MainFrameWithTable.__init__(self, master) self.set_table_place(34, 29) self.table_view.cellwidth = 85 self.table_model = TableModelAdvance() self.table_model.set_columns(FriendRecord.TABLE_VIEW_COLUMNS, main_column='Names') self.table_view.setModel(self.table_model) self.filter_manager = FilterRuleManager() self.filter_manager.set_comparison_rule( 'used_names', rule=sub_match_request_or_japanese_character) self.filter_manager.set_comparison_rule('current_character') # 滑鼠中鍵事件註冊,設定為更新好友資訊,並選取該列 self.table_view.bind( "<Button-2>", lambda event: (self.table_view.handle_left_click(event), self.opening_info_update_window(event))) self._init_left_frame() self._init_upper_frame() self._init_context() def _init_left_frame(self): button = Button(self, text="送出並返回", width=2, height=8, wraplength=1, font=(MS_JH, 12), borderwidth=2) button.place(x=4, y=44) button["command"] = self.submitting button = Button(self, text="取消並返回", width=2, height=8, wraplength=1, font=(MS_JH, 12), borderwidth=2) button.place(x=4, y=224) button["command"] = self.switching_to_friend_info def _init_upper_frame(self): basic_y = 3 # 供使用者調整日期 basic_x = 60 Label(self, text='Date:', font=(MS_JH, 11)).place(x=basic_x, y=basic_y) self.date = StringVar(value='') Entry(self, width=11, textvariable=self.date, font=(MS_JH, 11)).place(x=basic_x + 41, y=basic_y + 2) # 選擇是否顯示已登記的好友 basic_x = 256 self.is_show_recorded_friends = BooleanVar() self.is_show_recorded_friends.trace("w", lambda *args: self.update_table()) check_button = Checkbutton(self, variable=self.is_show_recorded_friends) check_button.place(x=basic_x, y=basic_y) label = Label(self, text='顯示已登記', font=(MS_JH, 11)) label.place(x=basic_x + 18, y=basic_y) bind_check_box_and_label(check_button, label) # 角色部分名稱篩選 basic_x = 366 Label(self, text='篩選:', font=(MS_JH, 11)).place(x=basic_x, y=basic_y) self.queried_name = StringVar() entry = Entry(self, width=11, textvariable=self.queried_name, font=(MS_JH, 11)) entry.place(x=basic_x + 40, y=basic_y + 2) entry.bind('<Return>', lambda event: self.update_table()) basic_x = 550 self.friend_count_str = StringVar() Label(self, textvariable=self.friend_count_str, font=(MS_JH, 12)).place(x=basic_x + 17, y=basic_y) def _init_context(self): self.date.set(date.today()) # 此次記錄的日期 # 建立 FriendRecordObjects self.friend_records = FriendModel.select_new_friend_record_list() self.friend_count_str.set('Friends: %02d' % len(self.friend_records)) # 好友總數 self.update_table() def update_table(self): # 根據名稱要求篩選,同時篩選符合設定的已登記/未登記紀錄 self.filter_manager.set_specific_condition( 'status', RECORDED if self.is_show_recorded_friends.get() else UNRECORDED) self.table_model.set_rows([ record.get_table_view_info() for record in self.filter_manager.filter(self.friend_records, self.queried_name.get()) ]) self.table_model.setSortOrder(columnName='LastProfession') self.redisplay_table() self.table_view.hide_column('ID') self.table_view.hide_column('LastProfession') def submitting(self): # 先確認資料的正確性 if self.validate_before_submitting(): # 將已經登記的 record 逐一更新到 DB 內,有衝突時則通知並略過該筆記錄 for record in self.friend_records: try: if record.status == RECORDED: FriendModel.insert_friend_record_into_db( record, self.date.get(), commit_followed=False) except StandardError as e: tkMessageBox.showinfo( 'Fail to record', '{0}\'s {1}.\n Already been skipped.'.format( record.used_names.encode('utf-8'), e), parent=self) continue FriendModel.commit() # 更新 FriendInfo Table 中的資訊(RaisedIn3Weeks, LastCharacter 等) FriendModel.take_statistic_to_update_friend_info() self.switching_to_friend_info() # 檢查數量計算並要求確認,確認後時才真正送出 def validate_before_submitting(self): updated_record_number = sum(1 for record in self.friend_records if record.status == RECORDED) return tkMessageBox.askyesno( 'Recording these records?', '總計 {0} 筆記錄,\n是否確認送出?'.format(updated_record_number), parent=self) def switching_to_friend_info(self): self.master.change_main_frame(FriendInfoFrame(self.master)) # 編輯好友記錄 def do_double_clicking(self, event): the_friend_id = int( self.table_model.getCellRecord( self.table_view.get_row_clicked(event), 0)) for record in self.friend_records: if record.f_id == the_friend_id: FriendRecordWindow(self, record, self.update_table) break self.queried_name.set('') # 此時大部分篩選都是為了找此人來編輯刪除,故編輯後清空條件 # 更改好友資訊 def opening_info_update_window(self, event): friend_info = FriendModel.select_specific_friend_info( int( self.table_model.getCellRecord( self.table_view.get_row_clicked(event), 0))) open_updating_friend_info_window(self, friend_info, lambda: None)
class FriendRecordFrame(MainFrameWithTable): def __init__(self, master): MainFrameWithTable.__init__(self, master) self.set_table_place(34, 29) self.table_view.cellwidth = 85 self.table_model = TableModelAdvance() self.table_model.set_columns(FriendRecord.TABLE_VIEW_COLUMNS, main_column='Names') self.table_view.setModel(self.table_model) self.filter_manager = FilterRuleManager() self.filter_manager.set_comparison_rule('used_names', rule=sub_match_request_or_japanese_character) self.filter_manager.set_comparison_rule('current_character') # 滑鼠中鍵事件註冊,設定為更新好友資訊,並選取該列 self.table_view.bind("<Button-2>", lambda event: ( self.table_view.handle_left_click(event), self.opening_info_update_window(event))) self._init_left_frame() self._init_upper_frame() self._init_context() def _init_left_frame(self): button = Button(self, text="送出並返回", width=2, height=8, wraplength=1, font=(MS_JH, 12), borderwidth=2) button.place(x=4, y=44) button["command"] = self.submitting button = Button(self, text="取消並返回", width=2, height=8, wraplength=1, font=(MS_JH, 12), borderwidth=2) button.place(x=4, y=224) button["command"] = self.switching_to_friend_info def _init_upper_frame(self): basic_y = 3 # 供使用者調整日期 basic_x = 60 Label(self, text='Date:', font=(MS_JH, 11)).place(x=basic_x, y=basic_y) self.date = StringVar(value='') Entry(self, width=11, textvariable=self.date, font=(MS_JH, 11)).place(x=basic_x + 41, y=basic_y + 2) # 選擇是否顯示已登記的好友 basic_x = 256 self.is_show_recorded_friends = BooleanVar() self.is_show_recorded_friends.trace("w", lambda *args: self.update_table()) check_button = Checkbutton(self, variable=self.is_show_recorded_friends) check_button.place(x=basic_x, y=basic_y) label = Label(self, text='顯示已登記', font=(MS_JH, 11)) label.place(x=basic_x + 18, y=basic_y) bind_check_box_and_label(check_button, label) # 角色部分名稱篩選 basic_x = 366 Label(self, text='篩選:', font=(MS_JH, 11)).place(x=basic_x, y=basic_y) self.queried_name = StringVar() entry = Entry(self, width=11, textvariable=self.queried_name, font=(MS_JH, 11)) entry.place(x=basic_x + 40, y=basic_y + 2) entry.bind('<Return>', lambda event: self.update_table()) basic_x = 550 self.friend_count_str = StringVar() Label(self, textvariable=self.friend_count_str, font=(MS_JH, 12)).place(x=basic_x + 17, y=basic_y) def _init_context(self): self.date.set(date.today()) # 此次記錄的日期 # 建立 FriendRecordObjects self.friend_records = FriendModel.select_new_friend_record_list() self.friend_count_str.set('Friends: %02d' % len(self.friend_records)) # 好友總數 self.update_table() def update_table(self): # 根據名稱要求篩選,同時篩選符合設定的已登記/未登記紀錄 self.filter_manager.set_specific_condition( 'status', RECORDED if self.is_show_recorded_friends.get() else UNRECORDED) self.table_model.set_rows([record.get_table_view_info() for record in self.filter_manager.filter(self.friend_records, self.queried_name.get())]) self.table_model.setSortOrder(columnName='LastProfession') self.redisplay_table() self.table_view.hide_column('ID') self.table_view.hide_column('LastProfession') def submitting(self): # 先確認資料的正確性 if self.validate_before_submitting(): # 將已經登記的 record 逐一更新到 DB 內,有衝突時則通知並略過該筆記錄 for record in self.friend_records: try: if record.status == RECORDED: FriendModel.insert_friend_record_into_db(record, self.date.get(), commit_followed=False) except StandardError as e: tkMessageBox.showinfo('Fail to record', '{0}\'s {1}.\n Already been skipped.'.format( record.used_names.encode('utf-8'), e), parent=self) continue FriendModel.commit() # 更新 FriendInfo Table 中的資訊(RaisedIn3Weeks, LastCharacter 等) FriendModel.take_statistic_to_update_friend_info() self.switching_to_friend_info() # 檢查數量計算並要求確認,確認後時才真正送出 def validate_before_submitting(self): updated_record_number = sum(1 for record in self.friend_records if record.status == RECORDED) return tkMessageBox.askyesno('Recording these records?', '總計 {0} 筆記錄,\n是否確認送出?'.format( updated_record_number), parent=self) def switching_to_friend_info(self): self.master.change_main_frame(FriendInfoFrame(self.master)) # 編輯好友記錄 def do_double_clicking(self, event): the_friend_id = int(self.table_model.getCellRecord(self.table_view.get_row_clicked(event), 0)) for record in self.friend_records: if record.f_id == the_friend_id: FriendRecordWindow(self, record, self.update_table) break self.queried_name.set('') # 此時大部分篩選都是為了找此人來編輯刪除,故編輯後清空條件 # 更改好友資訊 def opening_info_update_window(self, event): friend_info = FriendModel.select_specific_friend_info( int(self.table_model.getCellRecord(self.table_view.get_row_clicked(event), 0))) open_updating_friend_info_window(self, friend_info, lambda: None)
class CharacterSelectionWindow(BasicWindow): def __init__(self, master, callback, character_selected, width=422, height=155, **kwargs): BasicWindow.__init__(self, master, width=width, height=height, **kwargs) self.title('Character selection') self.records = None self.update_records() self.filter_manager = FilterRuleManager() self.filter_manager.set_comparison_rule(0) self.filter_manager.set_comparison_rule(1) self._init_widgets() self._init_character_selected(character_selected) self.callback = callback def _init_widgets(self): self.profession_selector = ProfessionSelector(self, self.updating_request_profession) self.profession_selector.place(x=5, y=5) self.rank_selector = RankSelector(self, self.updating_request_rank) self.rank_selector.place(x=5, y=53) Label(self, text='所屬', width=5, font=("", 10)).place(x=221, y=5) self.belonged_selector = FilteredCombobox(self, width=7, font=("", 11), justify=CENTER) self.belonged_selector['values'] = BELONGEDS self.belonged_selector.place(x=213, y=22) self.belonged_selector.bind('<<ComboboxSelected>>', lambda x: self.updating_request_belonged(self.belonged_selector.get())) self.belonged_selector.bind('<Return>', lambda x: self.character_selector.focus_set()) Label(self, text='篩選', width=5, font=("", 11)).place(x=222, y=54) self.name_request = StringVar(value='') entry = Entry(self, width=8, textvariable=self.name_request, font=("", 12)) entry.place(x=214, y=73) entry.bind('<Return>', lambda x: self.updating_request_name()) entry.bind('<Escape>', lambda x: (self.name_request.set(''), self.updating_request_name())) Label(self, text='Character', width=10, font=("", 12)).place(x=304, y=26) self.character_selector = ttk.Combobox(self, state='readonly', width=10, font=("", 12), justify=CENTER) self.character_selector.place(x=305, y=48) self.character_selector.bind('<Return>', lambda x: self.submitting()) # 熱鍵,直接指過來 self.bind('<f>', lambda x: self.character_selector.focus_set()) y_position = 115 # 送交的按鈕 button = Button(self, text="選擇此角色", width=25, borderwidth=3) button.place(x=17, y=y_position) button["command"] = self.submitting # 新增角色的按鈕 button = Button(self, text="新增角色", width=9, borderwidth=3) button.place(x=225, y=y_position) button["command"] = lambda: open_adding_new_jp_character_window( self, lambda new_character: (self.update_records(), self.updating_character_selector())) # 取消並結束的按鈕 button = Button(self, text="放棄選擇", width=9, borderwidth=3) button.place(x=317, y=y_position) button["command"] = self.destroy def _init_character_selected(self, character_selected): if character_selected is None: pass elif isinstance(character_selected, Character): self.profession_selector.select(character_selected.profession) self.rank_selector.select(character_selected.rank) self.updating_character_selector() self.character_selector.set(character_selected.nickname) else: raise TypeError('In CharacterSelectionWindow, arg: \"character_selected\"') def updating_request_profession(self, profession): self.filter_manager.set_specific_condition(2, profession) self.updating_character_selector() self.character_selector.focus_set() def updating_request_rank(self, rank): self.filter_manager.set_specific_condition(3, rank, match_requested_rank) self.updating_character_selector() self.character_selector.focus_set() def updating_request_belonged(self, belonged): self.filter_manager.set_specific_condition(4, belonged) self.updating_character_selector() def updating_request_name(self): self.updating_character_selector() self.character_selector.focus_set() # 清除原本的選擇,並更新可選擇的角色 def updating_character_selector(self): self.character_selector.set('') character_matched = [] for character_infos in self.filter_manager.filter(self.records, self.name_request.get()): character_matched.append(character_infos[0]) self.character_selector['values'] = character_matched # 有選擇的情況下才回傳,否則彈出錯誤視窗 def submitting(self): if self.character_selector.get() != '': self.callback(CharacterModel.select_character_by_specific_column('Nickname', self.character_selector.get())) self.destroy() else: tkMessageBox.showwarning("Character haven't selected", '\"Character\" 未選\n', parent=self) def update_records(self): self.records = CharacterModel.select_character_info_for_character_selector()