家計簿アプリを作成する⑤【自作コントロール(1)】

Flet

自作コントロール

各画面で使用する共通コントロール

My_Controlクラスに定義

import flet as ft
from datetime import datetime
from db.master_adapter import Master_Adapter
from db.models import Master
from common.const import Const
from config.config import Config


class My_Control:
    """
    自作コントロール
    """

一覧表

引数で受け取った列名と行情報をもとに、一覧表を作成する
・行をクリックしたら背景が灰色になる
・詳ボタンがクリックされたら詳細ボタン遷移先に設定した遷移先に行番号を渡す(家計簿アプリでは、家計簿詳細画面への遷移にのみ使用)

メソッド「set_data_list」を列名と行情報を引数として呼び出すことで表を作成する

    class HAB_LIST:
        """
        一覧表
        """

        def __init__(self, arg_page):
            self.page = arg_page
            self.control = None
            """
            保存用作成コントロール
            """
            self.column_name = []
            """
            保存用列名
            """
            self.data_list = []
            """
            保存用データ
            """
            self.page_go_setting = None
            """
            詳細ボタン遷移先
            """
            self.selected_row_value = None
            """
            保存用行番号
            """

        def change_bgcolor(self, arg_parent_container: ft.Container, arg_row_no):
            """
            背景色変更
            引数
                arg_parent_container: 親コンテナリスト
                arg_row_no: 選択行番号
            """
            # 全行の背景を白にする
            for item in arg_parent_container:
                item.bgcolor = ft.Colors.WHITE
            # 選択行の背景を灰色にする
            arg_parent_container[arg_row_no + 1].bgcolor = ft.Colors.GREY

        def on_child_click(self, e, arg_parent_container: ft.Container):
            """
            子コントロールがクリックされた時のイベント
            引数
                e: コントロールイベント
                arg_parent_container: 親コンテナリスト
            """
            # 選択行番号取得
            row_no = e.control.data
            # 背景色変更
            self.change_bgcolor(arg_parent_container, row_no)
            # 選択行番号を保持する
            self.selected_row_value = row_no
            self.page.update()

        def page_go(self, e, arg_parent_container: ft.Container):
            """
            [詳]ボタンがクリックされた時のイベント
            引数
                e: コントロールイベント
                parent_container: 親コンテナリスト
            """
            # 画面を非活性にする
            self.page.views[len(self.page.views) - 1].controls[1].visible = True
            self.page.update()
            # 選択行番号取得
            row_no = e.control.data
            # 背景色変更
            self.change_bgcolor(arg_parent_container, row_no)
            n = len(self.page.views) - 1
            # 行番号のdataをビューのdataに代入する
            self.page.views[n].data = arg_parent_container[row_no + 1].data
            # 画面を活性にする
            self.page.views[len(self.page.views) - 1].controls[1].visible = False
            # 遷移先を表示する
            self.page.go(f"/{self.page_go_setting}")

        def set_data_list(
            self, arg_column_name, arg_data_list, details_button_setting=True
        ):
            """
            一覧表作成
            引数
                arg_column_name: 列名リスト
                arg_data_list: データリスト
            """
            self.control = None
            # 列名がある場合、保存用列名に代入
            if arg_column_name is not None:
                self.column_name = arg_column_name
            # データがある場合、保存用データに代入
            if arg_data_list is not None:
                self.data_list = arg_data_list
            # サイズを設定
            row_button_width = 40
            row_button_height = 40
            column_width = 200
            row_width = 199.95
            row_height = 40
            # 列名行リスト
            column_name_list = []
            # 列番号
            column_no = 0
            # 詳細ボタン表示の場合
            if details_button_setting:
                # 詳細ボタン列作成
                column_details_button = ft.Container(
                    width=row_button_width,
                    height=row_button_height,
                    bgcolor=ft.Colors.with_opacity(0.0, ft.Colors.PRIMARY_CONTAINER),
                    border=ft.border.all(1.0, ft.Colors.BLACK),
                    data=column_no,
                )
                # 列名行リストに詳細ボタン列を追加する
                column_name_list.append(column_details_button)
                # 列番号更新
                column_no = column_no + 1
            # 保存用列名の分繰り返す
            for name in self.column_name:
                # 列名作成
                column = ft.Container(
                    content=ft.Text(
                        value=name,
                        weight=ft.FontWeight.BOLD,
                    ),
                    width=column_width,
                    height=row_height,
                    padding=ft.padding.only(left=5),
                    alignment=ft.alignment.center_left,
                    bgcolor=ft.Colors.with_opacity(0.0, ft.Colors.PRIMARY_CONTAINER),
                    border=ft.border.all(1.0, ft.Colors.BLACK),
                    data=column_no,
                )
                # 列名行リストに作成した列名を追加する
                column_name_list.append(column)
                # 列番号更新
                column_no = column_no + 1
            # 列名行作成
            columu_name_row = ft.Row(controls=column_name_list, spacing=0)
            # コンテナリスト
            cont_list = []
            # 列名行をコンテナに追加する
            cont_column = ft.Container(
                content=columu_name_row,
                bgcolor=ft.Colors.WHITE,
            )
            # コンテナの横幅を設定する
            if details_button_setting:
                cont_column.width = row_button_width + column_width * len(
                    self.column_name
                )
            else:
                cont_column.width = column_width * len(self.column_name)
            cont_list.append(cont_column)
            # 行番号
            row_no = 0
            # 保存用データの分繰り返す
            for data in self.data_list:
                # 行データ用コンテナ作成
                cont_row = ft.Container(bgcolor=ft.Colors.WHITE, data=data[0])
                # SEQ番号削除
                del data[0]
                # コンテナの横幅を設定する
                if details_button_setting:
                    cont_row.width = row_button_width + row_width * len(
                        self.column_name
                    )
                else:
                    cont_row.width = row_width * len(self.column_name)
                # 各データ用コンテナリスト
                detail_cont_list = []
                # 詳細ボタン表示の場合
                if details_button_setting:
                    # 詳細ボタン作成
                    row_cont_details_button = ft.Container(
                        ft.Container(
                            content=ft.TextButton(
                                content=ft.Text(
                                    "詳",
                                    size=12,
                                    color=ft.Colors.BLACK,
                                    text_align=ft.TextAlign.CENTER,
                                ),
                                data=row_no,
                                on_click=lambda e: My_Control.HAB_LIST.page_go(
                                    self,
                                    e,
                                    cont_list,
                                ),
                            ),
                            alignment=ft.alignment.center,
                            bgcolor=ft.Colors.GREY,
                            border=ft.border.all(1.0),
                            border_radius=10,
                        ),
                        width=row_button_width,
                        height=row_button_height,
                        bgcolor=ft.Colors.with_opacity(
                            0.0, ft.Colors.PRIMARY_CONTAINER
                        ),
                        border=ft.border.all(0.5, ft.Colors.BLACK),
                        data=row_no,
                    )
                    # 各データ用コンテナリストに詳細ボタンを追加する
                    detail_cont_list.append(row_cont_details_button)
                # データの分繰り返す
                for data_detail in data:
                    # 表示するデータ作成
                    row_cont = ft.Container(
                        content=ft.Text(data_detail),
                        width=row_width,
                        height=row_height,
                        padding=ft.padding.only(left=5),
                        alignment=ft.alignment.center_left,
                        border=ft.border.all(0.5, ft.Colors.BLACK),
                        bgcolor=ft.Colors.with_opacity(
                            0.0, ft.Colors.PRIMARY_CONTAINER
                        ),
                        data=row_no,
                        on_click=lambda e: My_Control.HAB_LIST.on_child_click(
                            self,
                            e,
                            cont_list,
                        ),
                    )
                    # 各データ用コンテナリストに表示するデータを追加する
                    detail_cont_list.append(row_cont)
                # 表示するデータを1行に成形する
                detail_row = ft.Row(controls=detail_cont_list, spacing=0)
                # 成形した行が行データ用コンテナに表示されるよう設定する
                cont_row.content = detail_row
                # 行データ用コンテナをコンテナリストに追加する
                cont_list.append(cont_row)
                # 行番号更新
                row_no = row_no + 1
            # スクロールを表示するために、列コントロールにコンテナリストが表示されるよう設定
            # コンテナに列コントロールが表示されるよう設定し、保存用作成コントロールに代入する
            self.control = ft.Container(
                content=ft.Column(
                    controls=cont_list,
                    spacing=0,
                    scroll=ft.ScrollMode.ALWAYS,
                ),
                height=450,
                border=ft.border.all(2.0, ft.Colors.BLACK),
            )

ビュー

背景色を水色に設定したビュー

このビューに画面コントロールを設定していく

    class MyView(ft.View):
        """
        背景色を設定したビュー
        """

        def __init__(
            self,
            route=None,
            controls=None,
            appbar=None,
            bottom_appbar=None,
            floating_action_button=None,
            floating_action_button_location=None,
            navigation_bar=None,
            drawer=None,
            end_drawer=None,
            vertical_alignment=None,
            horizontal_alignment=None,
            spacing=None,
            padding=None,
            bgcolor=ft.Colors.LIGHT_BLUE_50,
            decoration=None,
            foreground_decoration=None,
            can_pop=None,
            on_confirm_pop=None,
            scroll=None,
            auto_scroll=None,
            fullscreen_dialog=None,
            on_scroll_interval=None,
            on_scroll=None,
            adaptive=None,
        ):
            super().__init__(
                route,
                controls,
                appbar,
                bottom_appbar,
                floating_action_button,
                floating_action_button_location,
                navigation_bar,
                drawer,
                end_drawer,
                vertical_alignment,
                horizontal_alignment,
                spacing,
                padding,
                bgcolor,
                decoration,
                foreground_decoration,
                can_pop,
                on_confirm_pop,
                scroll,
                auto_scroll,
                fullscreen_dialog,
                on_scroll_interval,
                on_scroll,
                adaptive,
            )

オーバーレイ

画面サイズのオーバーレイ
初期設定は非表示

    class MyOverlay:
        """
        自作オーバーレイ
        """

        def __init__(self, arg_page: ft.Page):
            self.overlay = ft.Stack(
                controls=[
                    ft.Container(
                        width=arg_page.window.width,
                        height=arg_page.window.height,
                        alignment=ft.alignment.center,
                    )
                ],
                visible=False,
            )

ヘッダ部エリア

画面名と戻るボタンを作成する
イニシャライズ時に引数に画面名を渡すことで中央に表示される

    class Header_Area:
        """
        ヘッダ部エリア
        """

        def __init__(
            self,
            arg_display_name,
        ):
            """
            引数
                arg_display_name: 画面名
            """
            self.control = None
            # 画面ラベル作成
            self.display_label = ft.Container(
                content=ft.Text(
                    arg_display_name,
                    size=30,
                    weight=ft.FontWeight.BOLD,
                ),
                alignment=ft.alignment.center,
            )
            # 戻るボタン作成
            # ボタンクリックで1つ前の画面に戻る
            self.back_button = ft.FilledButton(
                content=ft.Text("戻る", size=18),
                width=100,
                height=40,
            )
            # ヘッダ部エリア作成
            self.control = ft.Row(
                controls=[
                    ft.Container(width=100),
                    self.display_label,
                    self.back_button,
                ],
                alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
            )

メッセージボックス

モーダル、文字サイズ、アクションを設定したメッセージボックス
イニシャライズ時に引数に「メッセージID」、「メッセージ内容」を渡すことで作成できる

    class Msgbox(ft.AlertDialog):
        """
        アクションを設定した自作メッセージボックス
        """

        def __init__(self, arg_title, arg_content):
            """
            メッセージボックス作成
            引数
                arg_title:メッセージID
                arg_content:メッセージ内容
            """
            super().__init__(
                modal=True,
                title=ft.Text(
                    f"【{arg_title}】",
                    size=18,
                    weight=ft.FontWeight.BOLD,
                ),
                content=ft.Text(f"{arg_content}", size=18),
                actions_alignment=ft.MainAxisAlignment.END,
            )
            # メッセージIDの末尾が'W'の場合
            if arg_title[-1] == Const.Log_Kinds.WARNING:
                # ウィンドウが閉じるように設定
                self.actions = [
                    ft.TextButton("はい", on_click=lambda e: self.page.window.close())
                ]
            else:
                # メッセージが閉じるように設定
                self.actions = [
                    ft.TextButton("はい", on_click=lambda e: self.page.close(self))
                ]

全体コード(GitHub)

household_account_book/common/my_control.py at main · SakumaTakayuki/household_account_book
Contribute to SakumaTakayuki/household_account_book development by creating an account on GitHub.