""" Compiz対応ショートカットキー画面 Mate+Compiz環境で安定動作するUI """ import flet as ft import signal import sys import logging from datetime import datetime class CompizShortcutsApp: """Compiz対応ショートカットキーアプリケーション""" def __init__(self, page: ft.Page): self.page = page # ログ設定 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('app.log'), logging.StreamHandler() ] ) # シグナルハンドラ設定 def signal_handler(signum, frame): print(f"\nシグナル {signum} を受信しました") print("✅ 正常終了処理完了") logging.info("アプリケーション正常終了") sys.exit(0) self.signal_handler = signal_handler # インスタンス変数として保存 signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) # ウィンドウ設定 - スマホ固定サイズ page.title = "販売アシスト・ショートカットランチャー" page.window.width = 420 # 400px → 420pxに拡大 page.window.height = 900 # 800px → 900pxに拡大して下切れ対策 page.window.resizable = False # 固定サイズ page.window_center = True # 中央配置 page.theme_mode = ft.ThemeMode.LIGHT # デバッグ表示 print(f"ウィンドウサイズ設定: {page.window.width} x {page.window.height}") print(f"リサイズ可能: {page.window.resizable}") print(f"中央配置: {page.window_center}") # ウィンドウクローズイベント page.on_window_close = lambda _: signal_handler(0, None) # メインコンテナ self.main_container = ft.Column([], expand=True, spacing=20) # ショートカットキー設定 self.setup_shortcuts() # UI構築 self.build_ui() logging.info("Compiz対応ショートカットキーアプリ起動完了") print("🚀 Compiz対応ショートカットキーアプリ起動完了") def setup_shortcuts(self): """ショートカットキー設定""" self.key_pressed = {} # キー押下状態を管理 self.last_key_time = {} # 最後のキー押下時間を管理 def on_keyboard(e: ft.KeyboardEvent): import time current_time = time.time() # キーリピート防止(0.2秒以内の同じキーを無視) if e.key in self.last_key_time and current_time - self.last_key_time[e.key] < 0.2: return self.last_key_time[e.key] = current_time # 数字キーで機能呼び出し if e.key == "1": self.show_function("ダッシュボード", "統計情報の表示") elif e.key == "2": self.show_function("売上管理", "売上データの入力・管理") elif e.key == "3": self.show_function("顧客管理", "顧客マスタの編集") elif e.key == "4": self.show_function("商品管理", "商品マスタの編集") elif e.key == "5": self.show_function("伝票入力", "伝票データの入力") elif e.key == "6": self.show_function("テキストエディタ", "下書き・メモの作成") elif e.key == "7": self.show_function("GPS機能", "GPS情報の取得・管理") elif e.key == "8": self.show_function("PDF出力", "帳票のPDF出力") elif e.key == "9": self.show_function("設定", "アプリケーション設定") elif e.key == "0": self.show_function("終了", "アプリケーションの終了") # ESCキーで終了 elif e.key == "Escape": self.show_function("終了", "アプリケーションの終了") # SPACEで実行 elif e.key == " ": self.execute_current_function() # ENTERで実行 elif e.key == "Enter": self.execute_current_function() self.page.on_keyboard_event = on_keyboard logging.info("ショートカットキー設定完了") def build_ui(self): """UIを構築""" # タイトル self.title = ft.Text( "Compiz対応ショートカットキー", size=36, weight=ft.FontWeight.BOLD, color=ft.Colors.BLUE_900, text_align=ft.TextAlign.CENTER ) # 説明テキスト self.description = ft.Text( "Mate+Compiz環境で安定動作するショートカットキー対応画面", size=18, color=ft.Colors.GREY_600, text_align=ft.TextAlign.CENTER ) # 現在の機能表示 self.current_function = ft.Container( content=ft.Text( "機能: 未選択", size=24, weight=ft.FontWeight.BOLD, color=ft.Colors.WHITE, text_align=ft.TextAlign.CENTER ), padding=20, bgcolor=ft.Colors.ORANGE, border_radius=15, margin=ft.Margin.symmetric(vertical=10) ) # ショートカットキーガイド self.shortcuts_guide = ft.Container( content=ft.Column([ ft.Text("ショートカットキー一覧", size=20, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER), ft.Divider(height=2, thickness=2), self.create_shortcut_row("1", "ダッシュボード", "統計情報の表示", ft.Colors.BLUE), self.create_shortcut_row("2", "売上管理", "売上データの入力・管理", ft.Colors.GREEN), self.create_shortcut_row("3", "顧客管理", "顧客マスタの編集", ft.Colors.ORANGE), self.create_shortcut_row("4", "商品管理", "階層構造商品マスター編集", ft.Colors.PURPLE), self.create_shortcut_row("5", "伝票入力", "伝票データの入力", ft.Colors.RED), self.create_shortcut_row("6", "テキストエディタ", "下書き・メモの作成", ft.Colors.TEAL), self.create_shortcut_row("7", "GPS機能", "GPS情報の取得・管理", ft.Colors.CYAN), self.create_shortcut_row("8", "PDF出力", "帳票のPDF出力", ft.Colors.BROWN), self.create_shortcut_row("9", "設定", "アプリケーション設定", ft.Colors.GREY), self.create_shortcut_row("0", "終了", "アプリケーションの終了", ft.Colors.RED), ft.Divider(height=2, thickness=2), ft.Container( content=ft.Column([ ft.Text("操作方法:", size=16, weight=ft.FontWeight.BOLD), ft.Text("• 数字キー: 機能を選択", size=14), ft.Text("• SPACE/ENTER: 選択した機能を実行", size=14), ft.Text("• ESC: アプリケーション終了", size=14), ft.Text("• マウスクリックも可能", size=14), ], spacing=5), padding=15, bgcolor=ft.Colors.BLUE_50, border_radius=10 ) ], spacing=10), padding=20, bgcolor=ft.Colors.WHITE, border_radius=15, shadow=ft.BoxShadow( spread_radius=1, blur_radius=5, color=ft.Colors.GREY_300, offset=ft.Offset(0, 2) ), margin=ft.Margin.only(bottom=20) ) # 実行ボタン(マウス用) self.execute_btn = ft.Container( content=ft.Button( "実行", on_click=self.execute_current_function, style=ft.ButtonStyle( bgcolor=ft.Colors.GREEN, color=ft.Colors.WHITE, elevation=5, shape=ft.RoundedRectangleBorder(radius=10), padding=ft.Padding.symmetric(horizontal=30, vertical=15) ) ), alignment=ft.alignment.Alignment(0, 0), margin=ft.Margin.symmetric(vertical=10) ) # 環境情報 self.env_info = ft.Container( content=ft.Column([ ft.Text("環境情報", size=16, weight=ft.FontWeight.BOLD), ft.Text(f"OS: Linux Mint + Compiz"), ft.Text(f"デスクトップ環境: Mate"), ft.Text(f"対応: Compiz特殊操作に最適化"), ft.Text(f"作成日時: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"), ], spacing=5), padding=15, bgcolor=ft.Colors.BLUE_50, border_radius=10, shadow=ft.BoxShadow( spread_radius=1, blur_radius=3, color=ft.Colors.GREY_200, offset=ft.Offset(0, 1) ) ) # メインコンテナに追加 self.main_container.controls = [ ft.Container( content=self.title, margin=ft.Margin.only(bottom=10) ), ft.Container( content=self.description, margin=ft.Margin.only(bottom=20) ), ft.Divider(height=1, thickness=1), self.current_function, ft.Row([ self.execute_btn, self.env_info ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN), ft.Divider(height=1, thickness=1), ft.Container( content=self.shortcuts_guide, expand=True ) ] # ページに追加 self.page.add( ft.Container( content=self.main_container, padding=20, bgcolor=ft.Colors.GREY_100, width=380 # 360px → 380pxに拡大 ) ) def create_shortcut_row(self, key: str, title: str, description: str, color: ft.Colors) -> ft.Container: """ショートカットキー行を作成""" return ft.Container( content=ft.Row([ ft.Container( content=ft.Text(key, size=22, weight=ft.FontWeight.BOLD, color=ft.Colors.WHITE), width=60, height=60, bgcolor=color, alignment=ft.alignment.Alignment(0, 0), border_radius=12, shadow=ft.BoxShadow( spread_radius=1, blur_radius=3, color=ft.Colors.with_opacity(0.3, color), offset=ft.Offset(0, 2) ) ), ft.Container( content=ft.Column([ ft.Text(title, size=16, weight=ft.FontWeight.BOLD, color=ft.Colors.BLUE_900), ft.Text(description, size=12, color=ft.Colors.GREY_600) ], spacing=3), width=280, # 280px → 300pxに調整 padding=ft.Padding.symmetric(horizontal=10, vertical=8), bgcolor=ft.Colors.GREY_50, border_radius=10, margin=ft.Margin.only(left=8) ) ], spacing=0, width=340), # Rowに幅制限を追加 margin=ft.Margin.only(bottom=10), on_click=lambda _: self.show_function(title, description) ) def show_function(self, title: str, description: str): """機能情報を表示""" self.current_function.content.value = f"機能: {title}" self.current_function.content.color = ft.Colors.WHITE self.current_function.bgcolor = ft.Colors.BLUE_900 self.page.update() logging.info(f"機能選択: {title}") def execute_current_function(self, e=None): """現在の機能を実行""" current_text = self.current_function.content.value if "ダッシュボード" in current_text: self.show_message("ダッシュボード機能を起動します", ft.Colors.GREEN) # 実際のアプリ起動 self.launch_app("app_compiz_fixed.py") logging.info("ダッシュボード機能実行") elif "売上管理" in current_text: self.show_message("売上管理機能を起動します", ft.Colors.GREEN) self.launch_app("app_simple_working.py") logging.info("売上管理機能実行") elif "顧客管理" in current_text: self.show_message("顧客管理機能を起動します", ft.Colors.GREEN) self.launch_app("app_master_management.py") logging.info("顧客管理機能実行") elif "商品管理" in current_text: self.show_message("商品管理機能を起動します", ft.Colors.GREEN) self.launch_app("app_hierarchical_product_master.py") logging.info("商品管理機能実行") elif "伝票入力" in current_text: self.show_message("伝票入力機能を起動します", ft.Colors.GREEN) self.launch_app("app_slip_framework_demo.py") logging.info("伝票入力機能実行") elif "テキストエディタ" in current_text: self.show_message("テキストエディタ機能を起動します", ft.Colors.GREEN) self.launch_app("app_text_editor.py") logging.info("テキストエディタ機能実行") elif "GPS機能" in current_text: self.show_message("GPS機能を起動します", ft.Colors.GREEN) self.launch_app("app_theme_master.py") logging.info("GPS機能実行") elif "PDF出力" in current_text: self.show_message("PDF出力機能を起動します", ft.Colors.GREEN) self.launch_app("app_framework_demo.py") logging.info("PDF出力機能実行") elif "設定" in current_text: self.show_message("設定機能を起動します", ft.Colors.GREEN) self.launch_app("app_robust.py") logging.info("設定機能実行") elif "終了" in current_text: self.show_message("アプリケーションを終了します", ft.Colors.RED) self.signal_handler(0, None) def launch_app(self, app_name: str): """アプリを起動""" import subprocess import os try: # 現在のディレクトリでアプリを起動 script_dir = os.path.dirname(os.path.abspath(__file__)) app_path = os.path.join(script_dir, app_name) # バックグラウンドでアプリ起動 subprocess.Popen([ "flet", "run", app_path ], cwd=script_dir) except Exception as e: logging.error(f"アプリ起動エラー: {e}") self.show_message(f"アプリ起動に失敗しました: {e}", ft.Colors.RED) def show_message(self, message: str, color: ft.Colors): """メッセージを表示""" self.page.snack_bar = ft.SnackBar( content=ft.Text(message), bgcolor=color ) self.page.snack_bar.open = True self.page.update() def main(page: ft.Page): """メイン関数""" try: app = CompizShortcutsApp(page) except Exception as e: logging.error(f"アプリケーション起動エラー: {e}") if __name__ == "__main__": ft.run(main)