Python GUIであるTcl/Tkのウィジェットを用いてエンジニアらしいGUIアプリケーションの実装(クラス化)を展開しようと思います。因みに現在バージョンはPython 3.13、Tcl/Tk 8.6.13(確認方法>py -m tkinter)である。下に展開したソースは、Python GUIサンプルアプリとして第3弾目(PyQt6→PySide6→Tkinter向け)です。改善&機能追加した実装になってます。機能ごとにモジュール化(クラス化)した実装です。
使用した部品は<PySide6 GUIサンプルアプリ>に合わせて、メインウィンドウ(Frame)、タブコントロール(Notebook)、グループボックス(LabelFrame)、1行のエディトボックス(Entry)、複数行のエディトボックス(Text)、プッシュボタン(Button)、コンボボックス(Combobox)、ディレクトリ選択ダイアログ(QFileDialog.getExistingDirectory)、Windows風INIファイル(configparser.ConfigParser)、ロギング機能(logging, logger, handlers, formatters)です。
Tkinter GUIサンプルアプリ起動直後の画面と[設定]タブ画面


---main.py---
# coding: utf-8
# Tcl/Tk 8.6.13(確認方法>py -m tkinter)
#import sys
import tkinter as tk
import tkinter.ttk as ttk
# 独自モジュール
import config_ini
import debug1_tab, setting_tab
class MainWindow(tk.Frame):
def __init__(self, main_win):
main_win.title("Tkinter タブ クラス化")
main_win.geometry("500x540")
self.config_ini = config_ini.ConfigIni()
self.config_ini.read_config_ini()
# タブウィジェット生成
self.tab_widget = ttk.Notebook(main_win)
# [Debug1]タブGUI付加
self.debug1_tab = debug1_tab.Debug1Tab(self.tab_widget, self.config_ini)
self.tab_widget.add(self.debug1_tab, text="Debug1")
# [設定]タブGUI付加
self.setting_tab = setting_tab.SettingTab(self.tab_widget, self.config_ini)
self.tab_widget.add(self.setting_tab, text="設定")
# タブウィジェット配置
self.tab_widget.pack(expand=True, fill='both', padx=4, pady=4)
# ステータスバー表示は2通り
frame = ttk.Frame(main_win, width=500)
frame.pack(anchor=tk.W, side=tk.BOTTOM)
self.status_bar = ttk.Label(frame, text="ここに情報を表示します", anchor=tk.W)
self.status_bar.pack()
# ステータスバーは、(上4行)Frame内にLabelを貼るか、(下2行)Frameを作らずLabelを貼るか。
#self.status_bar = ttk.Label(main_win, text="ここに情報を表示します", anchor=tk.W)
#self.status_bar.pack(anchor=tk.W, side=tk.BOTTOM)
if __name__ == "__main__":
root = tk.Tk()
main_win = MainWindow(root)
root.mainloop()
main_win.config_ini.uptate_config_ini()
---debug1_tab.py---
# coding: utf-8
# Tcl/Tk 8.6.13
import os
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog
import logging
class Debug1Tab(tk.Frame):
def __init__(self, parent, config_ini):
super().__init__(parent)
self.config_ini = config_ini
self.logger = logging.getLogger(__name__)
#self.logger.debug("Debログ") # モジュール毎ロガー確認
#self.logger.info("Infoログ") #
#self.logger.warning("Waringログ") #
label = ttk.Label(self, text="Debug-1 GUI")
label.pack()
groupbox = tk.LabelFrame(self, text="格納先フォルダ指定")
groupbox.pack(padx=2, ipady=2)
button = ttk.Button(groupbox)
button.configure(text="フォルダ指定", command=self.folder_button_click)
button.pack(anchor=tk.NW, side=tk.LEFT)
self.folder_line_edit = ttk.Entry(groupbox, width=66)
self.folder_line_edit.insert(tk.END, self.config_ini.save_folder)
self.folder_line_edit.pack(anchor=tk.NW, side=tk.LEFT, pady=2)
groupbox = tk.LabelFrame(self, text="アクション")
#groupbox = tk.LabelFrame(self, text="アクション", width=400, height=94)
#groupbox.propagate(False)
groupbox.pack(anchor=tk.NW, side=tk.TOP, padx=2, ipady=2)
frame = ttk.Frame(groupbox, width=80)
frame.pack()
button = ttk.Button(frame)
button.configure(text="アクション1", command=self.action1_button_click)
button.pack(side=tk.LEFT)
self.combobox = ttk.Combobox(frame, values=["アイテム0", "アイテム1"]) #item=0,1
self.combobox.current(0)
self.combobox.pack(side=tk.LEFT)
button = ttk.Button(groupbox)
button.configure(text="アクション2", command=self.action2_button_click)
button.pack(anchor=tk.W, side=tk.TOP)
button = ttk.Button(groupbox)
button.configure(text="アクション3", command=self.action3_button_click)
button.pack(anchor=tk.W, side=tk.TOP)
groupbox = tk.LabelFrame(self, text="実行情報")
groupbox.pack(anchor=tk.NW, side=tk.TOP, padx=2, ipady=2)
button = ttk.Button(groupbox)
button.configure(text="表示クリア", command=self.clear_button_click)
button.pack(anchor=tk.N)
self.text_edit = tk.Text(groupbox, width=80, height=20)
self.text_edit.pack(anchor=tk.W, padx=2, pady=2)
def folder_button_click(self):
folder = self.folder_line_edit.get()
if folder == "":
folder = "c:/"
folder = filedialog.askdirectory(initialdir = folder)
self.folder_line_edit.insert(tk.END, folder)
self.config_ini.save_folder = folder
def action1_button_click(self):
self.item_index = self.combobox.current()
self.text_edit.insert(tk.END, "action1_button clicked item={}\n".format(self.item_index))
def action2_button_click(self):
self.text_edit.insert(tk.END, "action2_button clicked\n")
def action3_button_click(self):
self.text_edit.insert(tk.END, "action3_button clicked\n")
def clear_button_click(self):
self.text_edit.delete("1.0", "end")
---setting_tab.py---
# coding: utf-8
# Tcl/Tk 8.6.13
import os
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog
import logging
class SettingTab(tk.Frame):
def __init__(self, parent, config_ini):
super().__init__(parent)
self.config_ini = config_ini
self.logger = logging.getLogger(__name__)
#self.logger.debug("Debログ") # ロギング確認
#self.logger.info("Infoログ") #
#self.logger.warning("Waringログ") #
label = ttk.Label(self, text="Settings GUI")
label.pack()
groupbox = tk.LabelFrame(self, text="格納先フォルダ指定")
groupbox.pack(padx=2, ipady=2)
folder_button = ttk.Button(groupbox)
folder_button.configure(text="フォルダ指定", command=self.folder_button_click)
folder_button.pack(anchor=tk.NW, side=tk.LEFT)
self.folder_line_edit = ttk.Entry(groupbox, width=100)
self.folder_line_edit.insert(tk.END, self.config_ini.save_folder)
self.folder_line_edit.pack(anchor=tk.NW, side=tk.LEFT, pady=2)
groupbox = tk.LabelFrame(self, text="プログラム情報")
groupbox.pack(padx=2, ipady=2)
self.text_edit = tk.Text(groupbox, width=80, height=20)
self.text_edit.pack(padx=2, pady=2)
self.text_edit.insert(tk.END, "Version : {}\n".format(self.config_ini.latest_ver))
self.text_edit.insert(tk.END, "Date : {}\n".format(self.config_ini.latest_date))
self.text_edit.insert(tk.END, "{}\n".format(self.config_ini.latest_overview))
self.text_edit.config(state="disabled") #リードオンリー
def folder_button_click(self):
folder = self.folder_line_edit.get()
if folder == "":
folder = "c:/"
folder = filedialog.askdirectory(initialdir = folder)
self.folder_line_edit.insert(tk.END, folder)
self.config_ini.save_folder = folder
---config_ini.py---
# coding: utf-8
# yamlパッケージ pyyaml-6.0.3 (pip install pyyaml)
# config.ini関連
import configparser
import os
# logging関連
import logging
import logging.config
import yaml
# config.ini関連
CONFIG_INI_FILE = "config.ini"
DEFAULT_SECTION = "DEFAULT"
SETTING_SECTION = "SETTING"
SAVE_FOLDER = "save_folder"
LATEST_SECTION = "LATEST"
RELEASE_VER = "version"
RELEASE_DATE = "date"
RELEASE_OVERVIEW = "overview"
# logging関連
LOG_DIR = "./log"
CONFIG_FILE = "config.yaml"
class ConfigIni():
def __init__(self):
# .iniファイルが存在しない場合、キーの設定値に文字列をセットしてプログラム実行を続行する
# 尚、キーの設定値にNoneをセットして(プログラム)処理にてExcetsion発生させ中断することも可能
self.default_folder = ""
self.save_folder = ""
self.latest_ver = ""
self.latest_date = ""
self.latest_overview = ""
try:
self.config_ini = configparser.ConfigParser()
if not os.path.exists(CONFIG_INI_FILE):
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), CONFIG_INI_FILE)
# config.yamlの内容をloggingに登録する
if not os.path.exists(LOG_DIR):
os.mkdir(LOG_DIR)
with open(CONFIG_FILE, "r") as fp:
config = yaml.safe_load(fp)
logging.config.dictConfig(config)
except Exception as err:
print(err)
def read_config_ini(self):
retval = False
try:
#self.config_ini.read(CONFIG_INI_FILE, encoding='utf-8')
# Python 3.2以降のencoding指定(推奨)ぽい…AIによると
with open(CONFIG_INI_FILE, 'r', encoding='utf-8') as fp:
self.config_ini.read_file(fp)
section = self.config_ini[DEFAULT_SECTION]
self.default_folder = section.get(SAVE_FOLDER)
section = self.config_ini[SETTING_SECTION]
self.save_folder = section.get(SAVE_FOLDER)
section = self.config_ini[LATEST_SECTION]
self.latest_ver = section.get(RELEASE_VER)
self.latest_date = section.get(RELEASE_DATE)
self.latest_overview = section.get(RELEASE_OVERVIEW)
retval = True
except Exception as err:
print(err)
return retval
def uptate_config_ini(self):
retval = False
try:
self.config_ini.set(DEFAULT_SECTION, SAVE_FOLDER, self.default_folder)
self.config_ini.set(SETTING_SECTION, SAVE_FOLDER, self.save_folder)
# Python 3.2以降のencoding指定(推奨)方法
with open(CONFIG_INI_FILE, 'w', encoding='utf-8') as fp:
self.config_ini.write(fp)
retval = True
except Exception as err:
print(err)
return retval
---config.ini--- [DEFAULT] save_folder = C:/ [SETTING] save_folder = C:/Python [LATEST] version = V1.0.0 date = 25/10/20 overview = ここにリリース概要を記載する
---config.yaml---
version: 1
formatters:
defaultFormat:
format: '%(asctime)s %(name)s:%(lineno)s %(funcName)s %(levelname)s %(message)s'
handlers:
consoleHandler:
class: logging.StreamHandler
formatter: defaultFormat
level: DEBUG
fileHandler:
class: logging.FileHandler
formatter: defaultFormat
level: INFO
filename: ./log/error.log
mode: a
loggers:
debug1_tab:
handlers:
- consoleHandler
- fileHandler
level: DEBUG
propagate: no
setting_tab:
handlers:
- consoleHandler
level: WARNING
propagate: no
root:
handlers:
- consoleHandler
level: INFO
ロガーの出力結果は、当サイト「Python ロギング(Logger)」をご確認ください。
1 Responses on “Python GUI Tkinter”