<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      使用PySide6/PyQt6實(shí)現(xiàn)Python跨平臺通用列表頁面的基類設(shè)計

      我在隨筆《使用PySide6/PyQt6實(shí)現(xiàn)Python跨平臺GUI框架的開發(fā)》中介紹過PySide6/PyQt6 框架架構(gòu)的整體設(shè)計,本篇隨筆繼續(xù)深入探討框架的設(shè)計開發(fā)工作,主要針對通用列表頁面的基類設(shè)計進(jìn)行介紹,分析基類的各個模塊的功能,以及介紹如何抽象一些公用的邏輯,實(shí)現(xiàn)對子類頁面的簡化處理。

      1、通用列表界面的設(shè)計

      大多數(shù)情況下,界面的表現(xiàn)邏輯可以使用不同的規(guī)則進(jìn)行抽象,如自定義控件、列表界面、彈出對話框界面等,我們把它抽象出來進(jìn)行不同的處理。子類界面進(jìn)行一定程度的擴(kuò)張即可獲得更好的使用、更簡化的代碼。

       對于列表和對話框界面的封裝,能夠簡化對泛型模型數(shù)據(jù)的統(tǒng)一處理,因此可以簡化繼承子類的代碼,提供代碼維護(hù)開發(fā)和維護(hù)的效率。 

      其中用戶管理界面的列表界面如下所示。

      樹列表或者表格控件,右鍵可以彈出相關(guān)的右鍵菜單

      列表包含有有樹形列表、條件查詢框、通用條件(查詢、新增、編輯、刪除、導(dǎo)出)等、列表展示、分頁導(dǎo)航、右鍵菜單等內(nèi)容。這些都是在基類中進(jìn)行了統(tǒng)一的抽象處理,子類根據(jù)需要調(diào)整屬性或重寫相關(guān)函數(shù)即可實(shí)現(xiàn)個性化的界面定義。

      2、通用列表界面的分析處理

      如果我們需要設(shè)計通用列表界面窗體的基類,那么我們需要盡可能的減少子類的代碼,把常用的功能封裝在基類里面,以及特殊的內(nèi)容,可以通過封裝邏輯,下發(fā)具體實(shí)現(xiàn)給子類進(jìn)行重寫實(shí)現(xiàn)即可。

      前面我們介紹過,常用列表包含有有樹形列表、條件查詢框、通用條件(查詢、新增、編輯、刪除、導(dǎo)出)等、列表展示、分頁導(dǎo)航、右鍵菜單等內(nèi)容,另外還有詳細(xì)的需要接受一些子類的列表字段顯示和中文參考,以及表格處理的功能按鈕的權(quán)限控制等方面。

      由于我們需要子類傳入的相關(guān)DTO類型,因此我們定義泛型類型來傳入處理。

      基類定義如下所示。

      ModelType = TypeVar("ModelType")  # 定義泛型基類
      
      # 創(chuàng)建泛型基類 BaseListFrame ,并繼承 QMainWindow
      class BaseListFrame(QMainWindow, Generic[ModelType]):

      另外我們初始化函數(shù),需要接受子類的一些信息,用于對顯示內(nèi)容進(jìn)行精準(zhǔn)的控制處理,因此構(gòu)造函數(shù)__init__里面定義好相關(guān)的參數(shù),如下所示。

      # 創(chuàng)建泛型基類 BaseListFrame ,并繼承 QMainWindow
      class BaseListFrame(QMainWindow, Generic[ModelType]): 
         def __init__(
              self,
              parent,
              model: Optional[ModelType] = None,
              display_columns: str = display_columns,
              column_mapping: dict = column_mapping,
              items_per_page: int = items_per_page,
              EVT_FLAGS: EventFlags = EVT_FLAGS,
              show_menu_tips: bool = show_menu_tips,
              menu_tips: str = DEFAULT_MENU_TIPS,
              use_left_panel: bool = False,
              column_widths={"id": 50},
              plugins=None,
          ):
              """初始化窗體
      
              :param parent: 父窗口
              :param model: 實(shí)體類
              :param display_columns: 顯示的字段名稱,逗號分隔,如:id,name,customid,authorize,note
              :param column_mapping: 列名映射(字段名到顯示名的映射)dict格式:{"name": "顯示名稱"}
              :param items_per_page: 每頁顯示的行數(shù)
              :param EVT_FLAGS: 設(shè)置可以顯示的操作按鈕
              :param show_menu_tips: 是否顯示提示信息
              :param menu_tips: 設(shè)置菜單提示信息
              :param use_left_panel: 是否使用樹控件
              :param column_widths: Grid列的寬度設(shè)置
              """

      1)樹列表的控制和實(shí)現(xiàn)

      我們在init函數(shù)里面,主要通過_create_content()函數(shù)進(jìn)行創(chuàng)建界面元素。

          def _create_content(self):
              """創(chuàng)建主要內(nèi)容面板"""
      
              # 創(chuàng)建左側(cè)樹控件
              if self.use_left_panel:
                  self._merge_tree_panel()
      
              # "創(chuàng)建右側(cè)主要內(nèi)容面板
              content_panel = self._create_content_panel()
              self.setCentralWidget(content_panel)

      它負(fù)責(zé)判斷是否需要展示樹列表,如果打開顯示樹的開關(guān),就根據(jù)樹形列表的集合進(jìn)行構(gòu)建左側(cè)的樹列表顯示。

          def _merge_tree_panel(self):
              """合并左側(cè)樹控件"""
              tree_panels = self.create_tree_panels()
              if tree_panels is None or len(tree_panels.keys()) == 0:
                  return
      
              self.dock_widget = dock_widget = QDockWidget(self)
              dock_widget.setWindowTitle("")  # 左側(cè)樹控件
      
              # 創(chuàng)建 QTabWidget,并存儲self.tree_tab_widget
              self.tree_tab_widget = tree_tab_widget = QTabWidget()
              tree_tab_widget.setTabPosition(QTabWidget.TabPosition.South)
      
              # 添加樹控件到 QTabWidget
              for name, panel in tree_panels.items():
                  tree_tab_widget.addTab(panel, name)
      
              dock_widget.setWidget(tree_tab_widget)
      
              # 防止面板浮動
              dock_widget.setFloating(False)
              # 禁止關(guān)閉按鈕
              dock_widget.setFeatures(QDockWidget.DockWidgetFeature.NoDockWidgetFeatures)
      
              # 將 QDockWidget 添加到主窗口的左側(cè)
              self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock_widget)

      上面代碼就是在左側(cè)構(gòu)建一個 QDockWidget 的停靠區(qū)域,我們把所有樹列表的集合放到其中容器的 QTabWidget 里面即可。

      在抽象的父類里面,我們只需要給出一個默認(rèn)的 create_tree_panels 實(shí)現(xiàn)函數(shù)即可,如下所示。

          def create_tree_panels(self) -> dict[str, QWidget]:
              """子類重寫該方法,創(chuàng)建左側(cè)樹列表面板-可以多個樹列表"""
      
              tree_panels: dict[str, QWidget] = {}
              # 創(chuàng)建樹控件
              # tree_panels["Tab 1"] = QLabel(self)
              # tree_panels["Tab 2"] = QLabel(self)
              return tree_panels

      而 create_tree_panels 具體的實(shí)現(xiàn) 我們是留給子類進(jìn)行重寫的,因為我們不清楚具體的顯示,但是我們可以把它們邏輯上組合起來即可。

      如對于上面展示的用戶列表界面,這部分create_tree_panels  的代碼實(shí)現(xiàn)如下所示。

          def create_tree_panels(self) -> dict[str, QWidget]:
              """子類重寫該方法,創(chuàng)建左側(cè)樹列表面板-可以多個樹列表"""
              dict = {}
      
              self.tree_dept = ctrl.MyTreePanel(
                  self,
                  on_tree_selected_handler=self.OnDeptTreeSelected,
                  expand_all=True,
                  on_menu_handler=self.OnDeptTreeMenu,
              )
              self.tree_role = ctrl.MyTreePanel(
                  self,
                  on_tree_selected_handler=self.OnRoleTreeSelected,
                  expand_all=True,
                  on_menu_handler=self.OnRoleTreeMenu,
              )
              dict["按組織機(jī)構(gòu)查看"] = self.tree_dept
              dict["按角色查看"] = self.tree_role
      
              return dict

      其中ctrl.MyTreePanel的控件是我們自定義的一個樹列表控件,用于減少重復(fù)性的代碼,抽象一個樹列表的展示,有利于我們保持更好的控制,統(tǒng)一界面效果的處理。

      在子類的構(gòu)造函數(shù)處理上,我們只需要設(shè)置參數(shù) use_left_panel = True,并且實(shí)現(xiàn) create_tree_panels 函數(shù)即可。

      2)查詢條件控件內(nèi)容

       

      介紹完畢樹列表的處理,我們再次來到基類的界面構(gòu)建處理函數(shù)上。

          def _create_content(self):
              """創(chuàng)建主要內(nèi)容面板"""
      
              # 創(chuàng)建左側(cè)樹控件
              if self.use_left_panel:
                  self._merge_tree_panel()
      
              # "創(chuàng)建右側(cè)主要內(nèi)容面板
              content_panel = self._create_content_panel()
              self.setCentralWidget(content_panel)

      其中的_create_content_panel 是我們構(gòu)建主查詢面板內(nèi)容的,其中包括輸入條件展示、常見按鈕顯示、以及列表、分頁欄目等。

          def _create_content_panel(self) -> QWidget:
              """創(chuàng)建右側(cè)主要內(nèi)容面板"""
              panel = QWidget(self)
              # 創(chuàng)建一個垂直布局
              main_layout = QVBoxLayout()
      
              # 創(chuàng)建一個折疊的查詢條件框
              search_bar = self._create_search_bar(panel)
              main_layout.addWidget(search_bar)
      
              # 創(chuàng)建顯示數(shù)據(jù)的表格
              table_widget = self._create_grid(panel)
              main_layout.addWidget(table_widget, 1)  # 拉伸占用全部高度
      
              # 創(chuàng)建一個分頁控件
              self.pager_bar = ctrl.MyPager(panel, self.items_per_page, self.update_grid)
              main_layout.addWidget(self.pager_bar)
      
              # 設(shè)置布局
              panel.setLayout(main_layout)
              return panel

      上面標(biāo)注特殊的代碼,就是對不同模塊的邏輯進(jìn)行分離實(shí)現(xiàn),從而讓我們關(guān)注點(diǎn)集中一些。其中的create_search_bar里面,主要封裝了查詢條件框、常規(guī)按鈕、自定義按鈕等內(nèi)容。

          def _create_search_bar(self, parent: QWidget = None) -> QWidget:
              """創(chuàng)建折疊的查詢條件框,包含查詢條件輸入框和常規(guī)按鈕"""
              panel = QWidget(parent)
              # 創(chuàng)建一個垂直布局
              layout = QVBoxLayout()
              panel.setLayout(layout)
      
              # 添加查詢條件控件
              input_sizer = self.CreateConditionsWithSizer(panel)
              layout.addLayout(input_sizer, 0)
              layout.addSpacing(5)  # 增加間距
      
              # 添加常規(guī)按鈕
              btns_sizer = self._CreateCommonButtons(panel)
              # 自定義按鈕
              self.CreateCustomButtons(panel, btns_sizer)
              layout.addLayout(btns_sizer, 0)
      
              return panel

      我在基類窗體的抽象類里面,定義了默認(rèn)的布局規(guī)則,如下代碼所示。

          def CreateConditionsWithSizer(self, parent: QWidget = None) -> QGridLayout:
              """子類可重寫該方法,創(chuàng)建折疊面板中的查詢條件,包括布局 QGridLayout"""
              layout = QGridLayout()
              layout.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
              layout.setSpacing(5)  # 增加間距
      
              # 統(tǒng)一處理查詢條件控件的添加,使用默認(rèn)的布局方式
              cols = 4 * 2
              list = self.CreateConditions(parent)
      
              for i in range(len(list)):
                  control: QWidget = list[i]
                  control.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
                  layout.addWidget(control, i // cols, i % cols)
      
              return layout
      
          def CreateConditions(self, parent: QWidget = None) -> list[QWidget]:
              """子類可重寫該方法,創(chuàng)建折疊面板中的查詢條件輸入框控件,不包括布局,使用默認(rèn)的布局方式 QGridLayout"""
              list = [QWidget]
              # 示例代碼:
              lblName = QLabel("名稱:")
              self.txtName = ctrl.MyTextCtrl(parent, "請輸入名稱")
      
              list.append(lblName)
              list.append(self.txtName)
      
              return list

      如果我們不改變布局,那么我們主要實(shí)現(xiàn) CreateConditions 函數(shù)即可。這個函數(shù)也是比較簡單的,構(gòu)建所需的輸入幾個條件即可。

      如對于簡單的客戶信息界面,它的條件輸入框里面就幾個條件。

      我們根據(jù)上面的界面效果,可以看到客戶窗體子類實(shí)現(xiàn) CreateConditions 函數(shù)的代碼如下所示。

          def CreateConditions(self, parent: QWidget = None) -> list[QWidget]:
              """創(chuàng)建折疊面板中的查詢條件輸入框控件"""
              # 創(chuàng)建控件,不用管布局,交給CreateConditionsWithSizer控制邏輯
              # 默認(rèn)的QGridLayout 為4*2=8列,每列間隔5px
              self.txtName = ctrl.MyTextCtrl(parent)
              self.txtAge = ctrl.MyNumericRange(parent)
              self.txtCustomerType = ctrl.MyComboBox(parent)
      
              # ControlUtil 可以方便的創(chuàng)建文本標(biāo)簽和控件的組合,并返回所有的控件列表
              util = ControlUtil(parent)
              util.add_control("姓名:", self.txtName)
              util.add_control("年齡:", self.txtAge)
              util.add_control("客戶類型:", self.txtCustomerType)
      
              return util.get_controls()

      這樣,具體實(shí)現(xiàn)部分,對于WxPython和PySide6/PyQt6來說,代碼都是差不多的,因為我們用了自定義用戶控件類,并使用輔助函數(shù),讓它們和標(biāo)簽更好的粘合起來。

       對于自定義控件,我們對其封裝,使之能夠在開發(fā)使用習(xí)慣上更一致,下面是我們根據(jù)需要對常見的原生控件進(jìn)行一些自定義控件的封裝列表。

      對于常規(guī)的按鈕,我們根據(jù)權(quán)限集合進(jìn)行判斷是否顯示即可,自定義按鈕則留給子類進(jìn)一步實(shí)現(xiàn)。

              # 添加常規(guī)按鈕
              btns_sizer = self._CreateCommonButtons(panel)
              # 自定義按鈕
              self.CreateCustomButtons(panel, btns_sizer)

       

      對于常規(guī)的按鈕,代碼如下所示。

       而自定義按鈕的處理,我們留給子類實(shí)現(xiàn),父類給出一個默認(rèn)的函數(shù)即可。

          def CreateCustomButtons(self, parent: QWidget, btns_sizer: QHBoxLayout) -> None:
              """子類可重寫該方法,創(chuàng)建折疊面板中的自定義按鈕"""
              # 增加按鈕
              pass

      3)表格數(shù)據(jù)顯示

      我們回到前面介紹的代碼。

          def _create_content_panel(self) -> QWidget:
              """創(chuàng)建右側(cè)主要內(nèi)容面板"""
              panel = QWidget(self)
              # 創(chuàng)建一個垂直布局
              main_layout = QVBoxLayout()
      
              # 創(chuàng)建一個折疊的查詢條件框
              search_bar = self._create_search_bar(panel)
              main_layout.addWidget(search_bar)
      
              # 創(chuàng)建顯示數(shù)據(jù)的表格
              table_widget = self._create_grid(panel)
              main_layout.addWidget(table_widget, 1)  # 拉伸占用全部高度
      
              # 創(chuàng)建一個分頁控件
              self.pager_bar = ctrl.MyPager(panel, self.items_per_page, self.update_grid)
              main_layout.addWidget(self.pager_bar)
      
              # 設(shè)置布局
              panel.setLayout(main_layout)
              return panel

      其中 _create_grid 就是我們創(chuàng)建表格內(nèi)容的邏輯函數(shù)了,它負(fù)責(zé)創(chuàng)建一個QTableView 元素進(jìn)行展示,表格數(shù)據(jù)的綁定,通過只定義模型MyTableModel 來綁定界面顯示的。

          def _create_grid(self, parent: QWidget) -> QTableView:
              """創(chuàng)建顯示數(shù)據(jù)的表格"""
      
              self.total_count: int = 0
      
              self.table_model = ctrl.MyTableModel(
                  self.data,
                  self.display_columns,
                  self.column_mapping,
                  primary_key="id",
                  column_widths=self.column_widths,
                  replace_values_handler=self.replace_values,  # 替換內(nèi)容函數(shù)
                  forground_color_handler=self.paint_foreground,  # 前景色渲染函數(shù)
              )
              self.table_view = QTableView(parent)
              self.table_view.setModel(self.table_model)
      
              self._set_grid_options()
      
              # 綁定行選中事件
              self.table_view.selectionModel().selectionChanged.connect(self.on_row_selected)
      
              # 異步綁定雙擊行事件
              if self.has_edit or self.has_view:
                  self.table_view.doubleClicked.connect(self.on_row_double_clicked)

      表格頭部排序、右鍵菜單、表格特殊的選中和內(nèi)容轉(zhuǎn)義、背景色處理、導(dǎo)出Excel、導(dǎo)出PDF、打印預(yù)覽等,我能都可以通過對表格的一些屬性或者方法進(jìn)行跟蹤處理即可實(shí)現(xiàn)。這里由于篇幅原因,不在深入探討。

      4)分頁信息展示

      對于分頁內(nèi)容,表格顯示是不負(fù)責(zé)的,因此我們需要根據(jù)模型對象,構(gòu)建一個分頁控件來顯示,把它剝離基類列表的主界面,有利于減少我們的關(guān)注點(diǎn)分散,也有利于重用控件。

       前面的邏輯代碼中。

       

          def _create_content_panel(self) -> QWidget:
              """創(chuàng)建右側(cè)主要內(nèi)容面板"""
              panel = QWidget(self)
              # 創(chuàng)建一個垂直布局
              main_layout = QVBoxLayout()
      
              # 創(chuàng)建一個折疊的查詢條件框
              search_bar = self._create_search_bar(panel)
              main_layout.addWidget(search_bar)
      
              # 創(chuàng)建顯示數(shù)據(jù)的表格
              table_widget = self._create_grid(panel)
              main_layout.addWidget(table_widget, 1)  # 拉伸占用全部高度
      
              # 創(chuàng)建一個分頁控件
              self.pager_bar = ctrl.MyPager(panel, self.items_per_page, self.update_grid)
              main_layout.addWidget(self.pager_bar)
      
              # 設(shè)置布局
              panel.setLayout(main_layout)
              return panel

      分頁控件是獨(dú)立的一個用戶控件。

      class MyPager(QWidget):
          """列表的分頁控件"""
      
          def __init__(self, parent=None, items_per_page=10, on_update=None, total_count=0):
              """初始化
      
              :param parent: 父控件
              :param items_per_page: 每頁的行數(shù)
              :param on_update: 查詢數(shù)據(jù)的回調(diào)函數(shù),為異步函數(shù)
              :param total_count: 總記錄數(shù)
              """
      
              self.items_per_page = items_per_page
              self.total_count = total_count
              self.total_pages = (total_count + items_per_page - 1) // items_per_page
              self.current_page = 0
              self.on_update = on_update
      
              super().__init__(parent)

      通過有效的隔離,使得我們每次只需要關(guān)注特定部分的處理,而具體的邏輯由基類統(tǒng)一控制,特殊的具體實(shí)現(xiàn)交給子類重寫基類函數(shù)即可。

      完成了上面的處理后,我們發(fā)現(xiàn)業(yè)務(wù)模塊的子類需要實(shí)現(xiàn)的內(nèi)容比較少了,大多數(shù)交給抽象父類實(shí)現(xiàn)了。

      5)數(shù)據(jù)的初始化處理

      完成了界面元素的創(chuàng)建后,我們還需要再基類中統(tǒng)一一些數(shù)據(jù)初始化的函數(shù),如我們在構(gòu)造函數(shù)里面創(chuàng)建好內(nèi)容后,調(diào)用了init_ui的函數(shù)初始化界面元素。

      # 創(chuàng)建泛型基類 BaseListFrame ,并繼承 QMainWindow
      class BaseListFrame(QMainWindow, Generic[ModelType]):
          """列表窗口的基類定義"""def __init__(
              self,
              parent,
              model: Optional[ModelType] = None,
              display_columns: str = display_columns,
              column_mapping: dict = column_mapping,
              items_per_page: int = items_per_page,
              EVT_FLAGS: EventFlags = EVT_FLAGS,
              show_menu_tips: bool = show_menu_tips,
              menu_tips: str = DEFAULT_MENU_TIPS,
              use_left_panel: bool = False,
              column_widths={"id": 50},
              plugins=None,
          ):
              """初始化窗體
      
              :param parent: 父窗口
              :param model: 實(shí)體類
              :param display_columns: 顯示的字段名稱,逗號分隔,如:id,name,customid,authorize,note
              :param column_mapping: 列名映射(字段名到顯示名的映射)dict格式:{"name": "顯示名稱"}
              :param items_per_page: 每頁顯示的行數(shù)
              :param EVT_FLAGS: 設(shè)置可以顯示的操作按鈕
              :param show_menu_tips: 是否顯示提示信息
              :param menu_tips: 設(shè)置菜單提示信息
              :param use_left_panel: 是否使用樹控件
              :param column_widths: Grid列的寬度設(shè)置
              """
              super().__init__(parent)
              # 日志對象
              self.log = settings.log.get_logger()
      
              # 初始化屬性
              self.model = model
              self.display_columns = display_columns  # 顯示的字段名稱,逗號分隔,如:id,name
              self.column_mapping = column_mapping  # 列名映射
              self.items_per_page = items_per_page  # 每頁顯示的行數(shù)
              self.EVT_FLAGS = EVT_FLAGS  # 設(shè)置可以顯示的操作按鈕
              self.show_menu_tips = show_menu_tips  # 是否顯示提示信息
              self.menu_tips = menu_tips  # 設(shè)置菜單提示信息
              self.use_left_panel = use_left_panel  # 是否使用樹控件
              self.column_widths = column_widths  # Grid列的寬度設(shè)置
              self.plugins = plugins or {}  # 單元格的渲染列表,格式:{"列名稱": 插件實(shí)例}
              self.columns_permit = {}  # 字段權(quán)限
              self.total_count = 0  # 記錄總數(shù)
      # 創(chuàng)建主要內(nèi)容面板
              self._create_content()# 調(diào)度異步任務(wù), 使用@asyncSlot()裝飾器后,你可以像同步函數(shù)一樣調(diào)用異步方法
              self.init_ui()
      
          @asyncSlot()
          async def init_ui(self):
              """初始化界面"""
              # 使用 @asyncSlot 裝飾器后,你可以像同步函數(shù)一樣調(diào)用異步方法,Qt 會自動管理異步任務(wù)的調(diào)度和執(zhí)行,
              # 不需要顯式使用 await 或者 asyncio.create_task 來啟動異步任務(wù)。
              # 如果你在子類中重寫了 init_ui,你仍然需要在子類中顯式地添加 @asyncSlot() 裝飾器。
              # 在子類中,Python 會將其視為新的方法定義,因此你必須在子類中的方法上再次應(yīng)用 @asyncSlot() 裝飾器來確保它仍然被處理為異步槽。
      
              await self.init_dict_items()
              await self.init_treedata()
              await self.update_grid()
      
          async def init_dict_items(self):
              """初始化字典數(shù)據(jù)-子類可重寫"""
              # await self.txtCustomerType.bind_dictType("客戶類型")
              pass
      
          async def init_treedata(self):
              """初始化樹控件數(shù)據(jù)-子類可重寫"""
              pass
      
          async def update_grid(self) -> None:
              """更新表格的內(nèi)容"""
      
              # 查詢數(shù)據(jù)
              await self.OnQuery()
      
              # 獲取當(dāng)前用戶有權(quán)限查看的列
              self.columns_permit = await self.get_columns_permit()
              # 更新表格數(shù)據(jù)
              self.table_model.UpdateData(self.data, self.columns_permit)
              # 更新頁碼信息
              self._update_pager()

      而各個子類負(fù)責(zé)各自模塊內(nèi)容的初始化即可。

      3、子類列表界面代碼分析

      由于父類已經(jīng)抽象了很多相關(guān)的元素創(chuàng)建、數(shù)據(jù)初始化的邏輯函數(shù),因此子類根據(jù)需要重寫函數(shù)實(shí)現(xiàn)即可。

      如對于簡單的業(yè)務(wù)表,客戶信息表,它的子類只需要實(shí)現(xiàn)下面幾個函數(shù)即可。

      CreateConditions函數(shù)負(fù)責(zé)查詢條件的構(gòu)建,前面介紹過。
          def CreateConditions(self, parent: QWidget = None) -> list[QWidget]:
              """創(chuàng)建折疊面板中的查詢條件輸入框控件"""
              # 創(chuàng)建控件,不用管布局,交給CreateConditionsWithSizer控制邏輯
              # 默認(rèn)的QGridLayout 為4*2=8列,每列間隔5px
      
              self.txtName = ctrl.MyTextCtrl(parent)
              self.txtAge = ctrl.MyNumericRange(parent)
              self.txtCustomerType = ctrl.MyComboBox(parent)
      
              # ControlUtil 可以方便的創(chuàng)建文本標(biāo)簽和控件的組合,并返回所有的控件列表
              util = ControlUtil(parent)
              util.add_control("姓名:", self.txtName)
              util.add_control("年齡:", self.txtAge)
              util.add_control("客戶類型:", self.txtCustomerType)
      
              return util.get_controls()

      OnQuery函數(shù)負(fù)責(zé)提取輸入條件,并提交服務(wù)端獲取數(shù)據(jù)返回。

          async def OnQuery(self):
              """子類實(shí)現(xiàn)-發(fā)送查詢請求, 需設(shè)置self.data,self.total_count"""
      
              # 獲取默認(rèn)查詢參數(shù),包括skipCount,maxResultCount,sorting
              params = self.GetDefaultParams()
              # 新的數(shù)據(jù),可以從控件獲取,也可以是動態(tài)生成的
              search_params = { "name": self.txtName.GetValue()}
              ****#其他條件# 將 search_params 合并到 params 中
              params.update(search_params)
      
              # 發(fā)送查詢請求
              data = await api.GetList(params)
              if data.success:
                  result = data.result
                  self.data = result.items
                  self.total_count = result.totalCount

      而OnAdd用于打開新增對話框。

          def OnAdd(self) -> None:
              """子類重寫-打開新增對話框"""
              dlg = FrmCustomerEdit(self, columns_permit=self.columns_permit)
              if dlg.exec() == QDialog.DialogCode.Accepted:
                  # 新增成功,刷新表格
                  asyncio.run(self.update_grid())
              dlg.deleteLater()

      而 OnEditById 用于編輯對話框的打開

          def OnEditById(self, entity_id: Any | str):
              """子類重寫-根據(jù)主鍵值打開編輯對話框"""
              # 使用列表窗體獲得的字段權(quán)限
              dlg = FrmCustomerEdit(self, entity_id, columns_permit=self.columns_permit)
              # 獲取對話框結(jié)果
              if dlg.exec() == QDialog.DialogCode.Accepted:
                  # 編輯成功,刷新表格
                  asyncio.run(self.update_grid())
              dlg.deleteLater()

      而刪除對話框的處理,如下函數(shù)所示。

          async def OnDeleteByIdList(self, id_list: List[Any | str]):
              """子類重寫-根據(jù)主鍵值刪除記錄"""
      
              # 發(fā)送刪除請求
              result = await api.DeleteByIds(id_list)
              # print(result)
              if result.success:
                  # 刪除成功,刷新表格
                  await self.update_grid()
              else:
                  error = result.errorInfo.message if result.errorInfo else "未知錯誤"
                  MessageUtil.show_error("刪除失敗:%s" % error)

      以上就是我們對于基類列表界面的抽象,和具體子類的一些個性化函數(shù)重寫的處理,以便實(shí)現(xiàn)更好的邏輯抽象并保證具體個性化頁面內(nèi)容的處理。

      對于不同的頁面,我們可以公用同一個列表界面的基類,可以簡化子類的很多操作,并能夠統(tǒng)一整體的界面效果,提供更多通用的功能入口,是一種比較好的設(shè)計模式。

      posted on 2025-03-12 12:47  伍華聰  閱讀(1066)  評論(2)    收藏  舉報

      導(dǎo)航

      主站蜘蛛池模板: 韩国精品久久久久久无码| 不卡乱辈伦在线看中文字幕| 精品国产午夜福利在线观看 | 欧美牲交a欧美牲交aⅴ免费真| 蜜桃久久精品成人无码av | 一区二区三区无码免费看| 亚洲av成人三区国产精品| 国产免费一区二区不卡| 日本精品一区二区不卡| 久久成人国产精品免费软件| 久久人人爽人人爽人人av| jizzjizz少妇亚洲水多| 中文激情一区二区三区四区| 三人成全免费观看电视剧高清| 日韩熟女乱综合一区二区| 欧美巨大极度另类| 成人无码午夜在线观看| 国产无遮挡裸体免费久久| 精品国产欧美一区二区三区在线| 一区二区三区四区国产综合| 亚洲 日本 欧洲 欧美 视频| 亚洲成人av免费一区| 国内熟女中文字幕第一页| 午夜天堂av天堂久久久| 亚洲精品国产男人的天堂| 日韩人妻无码精品久久| 大肉大捧一进一出好爽视频mba| 国产免费无遮挡吃奶视频| 亚洲人成网线在线播放VA| 国产亚洲真人做受在线观看| 乱色熟女综合一区二区三区| 一区二区三区四区亚洲自拍| 国产真实乱对白精彩久久老熟妇女| 亚洲国产午夜理论片不卡| 亚洲日韩精品无码av海量| 大地资源免费视频观看| 福利网午夜视频一区二区| 义马市| 国产一区二区av天堂热| 四虎永久在线精品无码视频| 亚洲欧美日韩综合一区在线|