想要設計一個物件分類的管理,如下:
MVC架構設計順序: view -> model -> controller
ObjectNameView:
1. 首先,定義5個signals,每種signal有不同輸入引數,依照實際需求決定輸入引數的資料型態
分別如下
由Qt desinger預先設計好介面main_view,
並將一部分主介面中的元件依照需求傳入
好處是: ObjectNameView類別相當於一個中間層
如此可以解偶'事件'與'人機介面', 未來使用者可以依照需求傳入不同元件;
換句話說,上層(人機介面)變更時 但底層(事件)運作邏輯不受影響(不需要改寫)
2. 連接(connect)人機介面互動的通訊(signal-slot)
例如
add按鈕clicked signal,連接 _handle_add slot
modify按鈕clicked signal,連接 _handle_modify slot
delete按鈕clicked signal,連接 _handle_delete slot
object_name_list_widget清單itemClicked signal,連接 _handle_selection slot
save按鈕clicked signal,連接 匿名函式 slot
_handle_add:
1. 獲取view上輸入的物件名稱(name),並將name發送(emit)
2. emit相當於把資料(name)丟出,讓後續的controller或model使用
"""Emit signal to add a new object name."""
def _handle_add(self):
name = self.object_name_lineEdt.text().strip()
if name:
# 獲取view上輸入的物件名稱
self.add_requested.emit(name)
else:
QMessageBox.warning(self, "物件名稱錯誤", "物件名稱不可以為空值!")
_handle_modify:
1. 獲取哪一筆需修改(current_row)和對應新的物件名稱(new_name),並將兩者資料發送(emit)
2. emit相當於把資料(current_row, new_name)丟出,讓後續的controller或model使用
"""Emit signal to modify the selected object name."""
def _handle_modify(self):
current_row = self.object_name_list_widget.currentRow()
if current_row != -1:
new_name = self.object_name_lineEdt.text().strip()
if new_name:
# 通知哪一筆需修改和對應新的物件名稱
self.modify_requested.emit(current_row, new_name)
else:
QMessageBox.warning(self, "物件名稱錯誤", "物件名稱不可以為空值!")
_handle_delete:
1. 獲取哪一筆需刪除(current_row),並將current_row發送(emit)
2. emit相當於把資料(current_row)丟出,讓後續的controller或model使用
"""Emit signal to delete the selected object name."""
def _handle_delete(self):
current_row = self.object_name_list_widget.currentRow()
if current_row != -1:
self.delete_requested.emit(current_row)
else:
QMessageBox.warning(self, "刪除物件錯誤", "請選擇欲刪除的物件名稱!")
_handle_selection:
1. 獲取哪一筆資料正被選取(current_row),並將current_row發送(emit)
2. emit相當於把資料(current_row)丟出,讓後續的controller或model使用
"""Emit signal to notify the selected object's index"""
def _handle_selection(self):
current_row = self.object_name_list_widget.currentRow()
# 通知被選取的物件索引
self.item_selected.emit(current_row)
Public member function:update_object_list和set_line_edit_text
"""Update the list of object_name_list_widget"""
def update_object_list(self, object_names):
# 更新view本身,所以不需要通知外部(無emit訊號)
self.object_name_list_widget.clear()
self.object_name_list_widget.addItems(object_names)
"""Set the text in the input line edit."""
def set_line_edit_text(self, text):
# 更新view本身,所以不需要通知外部(無emit訊號)
self.object_name_lineEdt.setText(text)
ObjectNameModel:
初始化階段
class ObjectNameModel:
"""object_names儲存所有物件名稱"""
def __init__(self, file_name ="classname.txt"):
self.file_name = file_name
self.object_names = []
add_object_name輸入引數的資料格式,和view中add_reqested(str)一樣
"""Add an object name to the list."""
def add_object_name(self, name):
self.object_names.append(name)
modify_object_name輸入引數的資料格式,和view中modify_reqested(int, str)一樣
"""Modify an object name at a specific index."""
def modify_object_name(self, index, new_name):
if 0<= index < len(self.object_names):
self.object_names[index] = new_name
delete_object_name輸入引數的資料格式,和view中delete_reqested(int)一樣
"""Delete an object name at a specific index."""
def delete_object_name(self, index):
if 0<= index <len(self.object_names):
self.object_names.pop(index)
save_object_name輸入引數的資料格式,和view中save_reqested()一樣
"""Save object names to the file."""
def save_object_names(self):
try:
with open(self.file_name, "w") as file:
file.write("\n".join(self.object_names))
except:
print("save_object_names failed.")
load_object_name: 從檔案讀取物件名稱
"""Load object names from the file."""
def load_object_names(self):
try:
with open(self.file_name, "r") as file:
self.object_names = file.read().splitlines()
except FileNotFoundError:
self.object_names = []
ObjectNameController:
初始化階段
1. 傳入model和view
2. 連結view signals至controller's slots
所以slots輸入引數必須符合view signals的資料格式
3. view 人機介面動作通知view本身的slot,
接著該slot會將signal emit,
由於步驟2有預先連結,
因此controller slot會接收到view signal並處理
class ObjectNameController:
def __init__(self, model:ObjectNameModel, view:ObjectNameView):
self.model = model
self.view = view
# Connect "view signals" to "controller slots"
# 1. 相當於在controller的slots中去呼叫view和model的API
# 2. "controller slots"都是公開成員函數(API),由main_controller去呼叫之
self.view.add_requested.connect(self.add_object_name)
self.view.modify_requested.connect(self.modify_object_name)
self.view.delete_requested.connect(self.delete_object_name)
self.view.save_requested.connect(self.save_object_names)
self.view.item_selected.connect(self.display_selected_object_name)
# Load initial data
self.model.load_object_names()
self.view.update_object_list(self.model.object_names)
4. controller收到view signal通知,進行model/view整合
4.1 以add_object_name slot為例,該輸入引數符合view add_requested訊號格式 Signal(str)
4.2 model呼叫add_object_name,
相當於將name加入 model.object_names
4.3 接著,view呼叫update_object_list更新清單,
其輸入來源model.object_names
def add_object_name(self, name):
self.model.add_object_name(name)
self.view.update_object_list(self.model.object_names)
self.view.object_name_lineEdt.clear()
5. controller收到view signal通知,進行model/view整合
5.1 以modify_object_name slot為例,該輸入引數符合view modify_requested訊號格式 Signal(int, str)
5.2 model呼叫modify_object_name,
相當於將index和new_name傳入model
並修改model.object_names對應索引(index),
該筆資料寫入新名稱(new_name)
5.3 接著,view呼叫update_object_list更新清單,
其輸入來源model.object_names
def modify_object_name(self, index, new_name):
self.model.modify_object_name(index, new_name)
self.view.update_object_list(self.model.object_names)
其他controller的slots如下,就不在贅述
def delete_object_name(self, index):
self.model.delete_object_name(index)
self.view.update_object_list(self.model.object_names)
def save_object_names(self):
self.model.save_object_names()
QMessageBox.information(self.view, "Success", "Object names saved successfully!")
def display_selected_object_name(self, index):
if 0<= index < len(self.model.object_names):
self.view.set_line_edit_text(self.model.object_names[index])
留言列表