ikkVisualKit 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,555 @@
1
+ import customtkinter as ctk
2
+ import os
3
+ import tkinter as tk
4
+ from pathlib import Path
5
+ from typing import Optional, Callable, Any
6
+ from importlib.resources import files
7
+ from io import BytesIO
8
+
9
+ from PIL import Image
10
+
11
+
12
+ class CTkButtonWithTooltip(ctk.CTkButton):
13
+ """ツールチップ対応のカスタムCTkButtonクラス"""
14
+ def __init__(self, *args, **kwargs):
15
+ super().__init__(*args, **kwargs)
16
+ self._ui_base: Optional['CTkUiBase'] = None
17
+ self._command: Optional[Callable[[], Any]] = None
18
+
19
+ @property
20
+ def tooltip_text(self) -> Optional[str]:
21
+ """ツールチップテキストを取得"""
22
+ if self._ui_base:
23
+ return self._ui_base._button_tooltips.get(self)
24
+ return None
25
+
26
+ @tooltip_text.setter
27
+ def tooltip_text(self, value: Optional[str]):
28
+ """ツールチップテキストを設定"""
29
+ if self._ui_base:
30
+ if value:
31
+ self._ui_base._button_tooltips[self] = value
32
+ else:
33
+ self._ui_base._button_tooltips.pop(self, None)
34
+
35
+ @property
36
+ def command(self) -> Optional[Callable[[], Any]]:
37
+ """クリック時のコマンドを取得"""
38
+ return self._command
39
+
40
+ @command.setter
41
+ def command(self, func: Optional[Callable[[], Any]]):
42
+ """クリック時のコマンドを設定"""
43
+ self._command = func
44
+ if func is not None:
45
+ self.configure(command=func, state=tk.NORMAL)
46
+ else:
47
+ self.configure(state=tk.DISABLED)
48
+
49
+
50
+ class TextInputPair:
51
+ """ラベルとテキストボックスのペア"""
52
+ def __init__(self, label: ctk.CTkLabel, entry: ctk.CTkEntry):
53
+ self.label = label
54
+ self.entry = entry
55
+ self._ui_base: Optional['CTkUiBase'] = None
56
+
57
+ @property
58
+ def tooltip_text(self) -> Optional[str]:
59
+ """ツールチップテキストを取得"""
60
+ if self._ui_base:
61
+ return self._ui_base._entry_tooltips.get(self.entry)
62
+ return None
63
+
64
+ @tooltip_text.setter
65
+ def tooltip_text(self, value: Optional[str]):
66
+ """ツールチップテキストを設定"""
67
+ if self._ui_base:
68
+ if value:
69
+ self._ui_base._entry_tooltips[self.entry] = value
70
+ else:
71
+ self._ui_base._entry_tooltips.pop(self.entry, None)
72
+
73
+ @property
74
+ def display_text(self) -> str:
75
+ """表示テキストを取得"""
76
+ return self.entry.get()
77
+
78
+ @display_text.setter
79
+ def display_text(self, text: str):
80
+ """表示テキストを設定(読み取り専用テキストボックスに値を設定)"""
81
+ # 読み取り専用を一時的に解除して値を設定
82
+ self.entry.configure(state="normal")
83
+ self.entry.delete(0, "end")
84
+ self.entry.insert(0, text)
85
+ self.entry.configure(state="disabled")
86
+
87
+ # 背景色を黄色く2回点滅
88
+ self._blink_entry()
89
+
90
+ def _blink_entry(self):
91
+ """エントリーの背景色を黄色く2回点滅"""
92
+ if not self._ui_base or not self._ui_base._tkRoot:
93
+ return
94
+
95
+ blink_state = [0] # 点滅状態を保持(0=黄→1=元→2=黄→3=元→終了)
96
+ original_bg = self.entry.cget("fg_color") # 元の背景色を保存
97
+
98
+ def toggle_color():
99
+ if blink_state[0] % 2 == 0:
100
+ self.entry.configure(fg_color="#FFFF00") # 黄色
101
+ else:
102
+ self.entry.configure(fg_color=original_bg) # 元の色
103
+
104
+ blink_state[0] += 1
105
+ if blink_state[0] < 4: # 4回の遷移 = 2回点滅
106
+ self._ui_base._tkRoot.after(300, toggle_color)
107
+
108
+ toggle_color()
109
+
110
+ # 後方互換性のための value プロパティ
111
+ @property
112
+ def value(self) -> str:
113
+ """テキストボックスの値を取得(deprecated: display_text を使用してください)"""
114
+ return self.display_text
115
+
116
+ @value.setter
117
+ def value(self, text: str):
118
+ """テキストボックスの値を設定(deprecated: display_text を使用してください)"""
119
+ self.display_text = text
120
+
121
+
122
+ class LogViewBox:
123
+ """ログ表示用テキストボックス"""
124
+ def __init__(self, text_widget: ctk.CTkTextbox, x: int, y: int, width: int, initial_lines: int = 2):
125
+ self.text_widget = text_widget
126
+ self.x = x
127
+ self.y = y
128
+ self.width = width
129
+ self.lines = initial_lines
130
+ self.parent = None
131
+
132
+ # ログレベルごとの色定義
133
+ self._log_level_colors = {
134
+ "ERROR": "#ff6b6b", # 赤
135
+ "WARN": "#ffa500", # オレンジ
136
+ "INFO": "#4dabf7", # 青
137
+ "DEBUG": "#909090" # グレー
138
+ }
139
+
140
+ # タグ設定
141
+ for level, color in self._log_level_colors.items():
142
+ text_widget.tag_config(level, foreground=color)
143
+
144
+ def add_log(self, message: str, level: str = "INFO"):
145
+ """ログを追加(最下行に追加、自動スクロール)"""
146
+ if level not in self._log_level_colors:
147
+ level = "INFO"
148
+
149
+ # テキストボックスを編集可能に
150
+ self.text_widget.configure(state="normal")
151
+
152
+ # ログエントリを作成
153
+ log_entry = f"[{level}] {message}\n"
154
+
155
+ # 末尾に追加
156
+ start_index = self.text_widget.index("end-1c")
157
+ self.text_widget.insert("end", log_entry)
158
+
159
+ # ログレベルに応じて色付け
160
+ end_index = self.text_widget.index("end-1c")
161
+ self.text_widget.tag_add(level, start_index, end_index)
162
+
163
+ # 最下行にスクロール
164
+ self.text_widget.see("end")
165
+
166
+ # 読み取り専用に戻す
167
+ self.text_widget.configure(state="disabled")
168
+
169
+ def expand(self, lines: int):
170
+ """表示行数を拡張"""
171
+ self.lines = lines
172
+ line_height = 20 # 1行あたりのピクセル高さ(概算)
173
+ new_height = lines * line_height + 10
174
+ self.text_widget.configure(height=new_height)
175
+
176
+ def collapse(self):
177
+ """デフォルト表示に戻す"""
178
+ self.expand(self.lines)
179
+
180
+ def clear(self):
181
+ """ログをクリア"""
182
+ self.text_widget.configure(state="normal")
183
+ self.text_widget.delete("1.0", "end")
184
+ self.text_widget.configure(state="disabled")
185
+
186
+ @property
187
+ def tooltip_text(self) -> Optional[str]:
188
+ """ツールチップ(互換性のため)"""
189
+ return None
190
+
191
+ @tooltip_text.setter
192
+ def tooltip_text(self, value: Optional[str]):
193
+ """ツールチップ設定"""
194
+ if self.parent and value:
195
+ self.parent._entry_tooltips[self.text_widget] = value
196
+
197
+
198
+ class CTkUiBase:
199
+ """
200
+ Tkは必ずメインスレッドで動かす。
201
+ サブクラスは _build(self, root) をオーバーライドする。
202
+ """
203
+ # パッケージリソースのイメージディレクトリ
204
+ _image_dir = files('ikkVisualKit').joinpath('image')
205
+
206
+ # ==========================================
207
+ def __init__(self
208
+ ,title: str = "Title"
209
+ ,size: tuple[int, int] = (600, 300)
210
+ ,icon_path: Optional[str] = None
211
+ ):
212
+ '''コンストラクタ
213
+ title: ウィンドウタイトル
214
+ size: ウィンドウサイズ (幅, 高さ)
215
+ icon_path: アイコン画像のパス(必要に応じて指定、Noneの場合はデフォルトアイコン)
216
+ '''
217
+ # テーマ設定
218
+ ctk.set_appearance_mode("Light")
219
+ ctk.set_default_color_theme("green")
220
+ self._title = title
221
+ self._size = size
222
+ self._icon_path = icon_path
223
+ self._tkRoot: Optional[ctk.CTk] = ctk.CTk()
224
+ self._tooltip: Optional[tk.Toplevel] = None
225
+ self._button_tooltips: dict[ctk.CTkButton, str] = {}
226
+ self._entry_tooltips: dict[ctk.CTkEntry, str] = {}
227
+ #画像をキーで管理する辞書
228
+ self._images: dict[str, ctk.CTkImage] = {}
229
+
230
+ # 汎用画像を起動時に自動登録
231
+ self._register_common_images()
232
+ self._tkRoot.title(self._title)
233
+ self._tkRoot.geometry(f"{self._size[0]}x{self._size[1]}")
234
+ self._tkRoot.protocol("WM_DELETE_WINDOW", self._handle_close)
235
+
236
+ # UI構築
237
+ self._build(self._tkRoot)
238
+ # ==========================================
239
+ # サブクラス用
240
+ # ==========================================
241
+ def _build(self, root: ctk.CTk):
242
+ """UI構築(サブクラスでオーバーライド)"""
243
+ # ウィンドウアイコン(.ico)
244
+ iconPath = self._icon_path
245
+ if iconPath and os.path.exists(iconPath):
246
+ root.iconbitmap(iconPath)
247
+ # ==========================================
248
+ # 他スレッドから安全にUI処理を投げる
249
+ # ==========================================
250
+ def invoke_ui(self, func: Callable, *args, **kwargs):
251
+ if not self._tkRoot:
252
+ return
253
+ self._tkRoot.after(0, lambda: func(*args, **kwargs))
254
+ # ==========================================
255
+ # 表示制御
256
+ # ==========================================
257
+ def show_window(self):
258
+ if not self._tkRoot:
259
+ return
260
+ self._tkRoot.deiconify()
261
+ self._tkRoot.lift()
262
+
263
+ def hide_window(self):
264
+ if not self._tkRoot:
265
+ return
266
+ self._tkRoot.withdraw()
267
+ # ==========================================
268
+ # 終了制御
269
+ # ==========================================
270
+ def close(self):
271
+ if self._tkRoot:
272
+ self._tkRoot.destroy()
273
+ self._tkRoot = None
274
+ #--------------------------------------------
275
+ def _handle_close(self):
276
+ self._on_close()
277
+ #--------------------------------------------
278
+ def _on_close(self):
279
+ """×ボタン動作(必要なら継承先で上書き)"""
280
+ self.close()
281
+ # ==========================================
282
+ # mainloop開始(必ずメインスレッドで呼ぶ)
283
+ # ==========================================
284
+ def run(self):
285
+ if self._tkRoot:
286
+ self._tkRoot.mainloop()
287
+ #==========================================
288
+ # ボタン作成
289
+ def make_ActionButton(self,
290
+ x: int,y: int, #位置
291
+ text: Optional[str]="", #表示する文字
292
+ command: Optional[Callable[[], Any]] = None, #割り当てるメソッド
293
+ image_key: Optional[str] = None,
294
+ compound: str = "left",
295
+ size_rank: int = 0, # 0=48x32, 1=96x64(ライトブルー), 2=128x64(Lime)
296
+ ) -> Optional[ctk.CTkButton]:
297
+ """共通ボタン生成
298
+ size_rank=0: 通常サイズ(幅48、高さ32)グレー
299
+ size_rank=1: 中サイズ(幅96、高さ64)ライトブルー
300
+ size_rank=2: 大サイズ(幅128、高さ64)Lime
301
+ """
302
+ if not self._tkRoot:
303
+ return None
304
+
305
+ # size_rankに応じてパラメータを変更
306
+ if size_rank == 1:
307
+ btn_width = 96
308
+ btn_height = 64
309
+ btn_font_size = 12
310
+ btn_compound = "bottom" # アイコンの下に文字
311
+ fg_color = "#ADD8E6" # ライトブルー
312
+ hover_color = "#87CEEB" # スカイブルー
313
+ elif size_rank == 2:
314
+ btn_width = 128
315
+ btn_height = 64
316
+ btn_font_size = 12
317
+ btn_compound = "bottom" # アイコンの下に文字
318
+ fg_color = "#00FF00" # Lime
319
+ hover_color = "#32CD32" # ライムグリーン
320
+ else: # size_rank == 0
321
+ btn_width = 48
322
+ btn_height = 32
323
+ btn_font_size = 12
324
+ btn_compound = compound
325
+ fg_color = "#c3c3c3" # グレー
326
+ hover_color = "#DB8D0F" # オレンジ
327
+
328
+ #ボタンの基本スタイル
329
+ button_kwargs: dict[str, Any] = {
330
+ "text": text,
331
+ "width": btn_width,
332
+ "height": btn_height,
333
+ "state": tk.NORMAL if command is not None else tk.DISABLED,
334
+ "fg_color": fg_color,
335
+ "hover_color": hover_color,
336
+ "text_color": "black",
337
+ "font": ctk.CTkFont(size=btn_font_size, weight="normal"),
338
+ "border_width": 2,
339
+ "border_color": "#004382",
340
+ "corner_radius": 4,
341
+ }
342
+
343
+ if command is not None:
344
+ button_kwargs["command"] = command
345
+ #画像があれば追加
346
+ image = self.get_image(image_key) if image_key else None
347
+ if image is not None:
348
+ button_kwargs["image"] = image
349
+ button_kwargs["compound"] = btn_compound
350
+ #ボタン生成
351
+ btn = CTkButtonWithTooltip(self._tkRoot, **button_kwargs)
352
+ btn._ui_base = self # UIBase参照を設定
353
+
354
+ # デフォルトの色を保存
355
+ default_fg_color = button_kwargs["fg_color"]
356
+ hover_fg_color = button_kwargs["hover_color"]
357
+
358
+ #マウスホバー処理
359
+ def on_enter(event):
360
+ btn.configure(fg_color=hover_fg_color, text_color="black")
361
+ tooltip_text = self._button_tooltips.get(btn)
362
+ if tooltip_text:
363
+ self._show_tooltip(btn, tooltip_text)
364
+ #マウスホバー解除処理
365
+ def on_leave(event):
366
+ btn.configure(fg_color=default_fg_color, text_color="black")
367
+ self._hide_tooltip()
368
+ #ホバーイベントのバインド
369
+ btn.bind("<Enter>", on_enter)
370
+ btn.bind("<Leave>", on_leave)
371
+ btn.place(x=x, y=y)
372
+ return btn
373
+ #--------------------------------------------------------
374
+ def set_button_tooltip(self, button: ctk.CTkButton, text: Optional[str]):
375
+ """生成済みボタンへツールチップを後付け設定する。"""
376
+ if text:
377
+ self._button_tooltips[button] = text
378
+ return
379
+
380
+ self._button_tooltips.pop(button, None)
381
+ self._hide_tooltip()
382
+ #========================================================
383
+ # テキスト入力フィールド作成
384
+ def make_labelTextBox(self,
385
+ x: int, y: int, # ラベルの左上座標
386
+ label_text: str, # ラベルのテキスト
387
+ width: int = 200, # 幅
388
+ entry_text: str = "", # テキストボックスの初期テキスト
389
+ ) -> Optional[TextInputPair]:
390
+ """ラベルとテキストボックスのペアを生成"""
391
+ if not self._tkRoot:
392
+ return None
393
+ # ラベル生成
394
+ _labnel_height = 32
395
+ label = ctk.CTkLabel(
396
+ self._tkRoot,
397
+ text=label_text,
398
+ font=ctk.CTkFont("Meiryo",size=16,weight="normal"),
399
+ width=width,
400
+ height=_labnel_height,
401
+ anchor="e", # ウィジェット内での位置を右側に(east)
402
+ justify="right", # 複数行のテキストを右揃えに
403
+ )
404
+ label.place(x=x, y=y)
405
+ # テキストボックス生成
406
+ entry = ctk.CTkEntry(
407
+ self._tkRoot,
408
+ font=ctk.CTkFont(size=30),
409
+ width=width,
410
+ height=48,
411
+ justify="center",
412
+ border_width=1,
413
+ border_color="#124463",
414
+ corner_radius=1, #角丸
415
+ )
416
+ entry.place(x=x, y=y + _labnel_height)
417
+ entry.insert(0, entry_text)
418
+ # 読み取り専用に設定
419
+ entry.configure(state="disabled")
420
+ # ホバーイベント設定
421
+ def on_enter(event):
422
+ tooltip_text = self._entry_tooltips.get(entry)
423
+ if tooltip_text:
424
+ self._show_tooltip(entry, tooltip_text)
425
+ #マウスホバー解除処理
426
+ def on_leave(event):
427
+ self._hide_tooltip()
428
+ #ホバーイベントのバインド
429
+ entry.bind("<Enter>", on_enter)
430
+ entry.bind("<Leave>", on_leave)
431
+ # ペアオブジェクト生成
432
+ pair = TextInputPair(label, entry)
433
+ pair._ui_base = self
434
+
435
+ return pair
436
+ #========================================================
437
+ # ログ表示ボックス作成
438
+ def make_LogTerminal(self,
439
+ x: int, y: int, # 左上座標
440
+ width: int = 300, # 幅
441
+ initial_lines: int = 2, # 初期表示行数
442
+ ) -> Optional[LogViewBox]:
443
+ """ログ表示用テキストボックスを生成"""
444
+ if not self._tkRoot:
445
+ return None
446
+ # CTkTextbox生成
447
+ textbox = ctk.CTkTextbox(
448
+ self._tkRoot,
449
+ width=width,
450
+ height=initial_lines * 20 + 10,
451
+ font=ctk.CTkFont("Courier", size=10),
452
+ border_width=1,
453
+ border_color="#000000",
454
+ corner_radius=4,
455
+ )
456
+ textbox.place(x=x, y=y)
457
+ textbox.configure(state="disabled") # 読み取り専用
458
+
459
+ # ログビューボックス生成
460
+ log_view = LogViewBox(textbox, x, y, width, initial_lines)
461
+ log_view.parent = self
462
+
463
+ return log_view
464
+ #========================================================
465
+ #UIに使う画像の登録
466
+ def register_image(self, key: str, filename: str, size: tuple[int, int] = (32, 24)) -> bool:
467
+ """imageフォルダ配下の画像をキーで登録する(主用途: png)
468
+ size: 画像サイズ(幅, 高さ)デフォルト(32, 24)でボタン内にフィッティング
469
+ """
470
+ try:
471
+ image_file = self._image_dir.joinpath(filename)
472
+ if not image_file.is_file():
473
+ return False
474
+ pil_image = Image.open(BytesIO(image_file.read_bytes()))
475
+ self._images[key] = ctk.CTkImage(light_image=pil_image, dark_image=pil_image, size=size)
476
+ return True
477
+ except Exception:
478
+ return False
479
+
480
+ def get_image(self, key: str) -> Optional[ctk.CTkImage]:
481
+ return self._images.get(key)
482
+
483
+ def _register_common_images(self):
484
+ """baseクラスで汎用利用するpngを事前登録する。"""
485
+ try:
486
+ for image_path in self._image_dir.iterdir():
487
+ if image_path.name.endswith('.png'):
488
+ # 拡張子なしファイル名をキーとして登録(例: setting.dio.png -> setting.dio)
489
+ self.register_image(image_path.name[:-4], image_path.name)
490
+ except Exception:
491
+ # イメージディレクトリが存在しない場合は何もしない
492
+ pass
493
+
494
+ # アイコンリスト(icoファイルのアイコンはTkinterで直接扱えないため、ここではpngを想定)
495
+ @staticmethod
496
+ def icon_list() -> list[str]:
497
+ """imageフォルダ内のpngファイル名のリストを返す"""
498
+ try:
499
+ image_dir = files('ikkVisualKit').joinpath('image')
500
+ return [p.name[:-4] for p in image_dir.iterdir() if p.name.endswith('.png')]
501
+ except Exception:
502
+ return []
503
+ #-------------------------------------------------------------------
504
+ #ツールチップ表示
505
+ #-------------------------------------------------------------------
506
+ def _show_tooltip(self, widget, text):
507
+ self._hide_tooltip()
508
+ if not self._tkRoot:
509
+ return
510
+ #ウィジェットの位置を取得してツールチップを表示
511
+ x = widget.winfo_rootx() + (widget.winfo_width() /2)+4
512
+ y = widget.winfo_rooty() - (widget.winfo_height() /2)-4
513
+ #
514
+ tooltip = tk.Toplevel(self._tkRoot)
515
+ tooltip.wm_overrideredirect(True)
516
+ tooltip.wm_geometry(f"+{int(x)}+{int(y)}")
517
+ tooltip.configure(bg="#CCCCCC")
518
+ tooltip.attributes("-alpha", 0.7)#半透明
519
+ #ツールチップとして表示させるラベルの設定
520
+ label = tk.Label(
521
+ tooltip,
522
+ text=text,
523
+ justify="left",
524
+ bg="#CCCCCC",
525
+ fg="black",
526
+ relief="solid",
527
+ borderwidth=1,
528
+ padx=8,
529
+ pady=4,
530
+ font=("Meiryo", 10),
531
+ )
532
+ label.pack()
533
+ self._tooltip = tooltip
534
+ #ツールチップ非表示
535
+ def _hide_tooltip(self):
536
+ if self._tooltip is not None:
537
+ self._tooltip.destroy()
538
+ self._tooltip = None
539
+
540
+ #####################################################################
541
+ #
542
+ #####################################################################
543
+ if __name__ == "__main__":
544
+ window = CTkUiBase(title="テスト", size=(400, 200,), icon_path="../../icon.ico")
545
+ window.show_window()
546
+
547
+ # 2秒後に閉じる
548
+ def _close_later():
549
+ if window._tkRoot:
550
+ window._tkRoot.after(2000, window.close)
551
+
552
+ window.invoke_ui(_close_later)
553
+
554
+ window.run()
555
+ print("ウィンドウを閉じました")
@@ -0,0 +1,96 @@
1
+ # DialogBase.py
2
+ from enum import Enum
3
+ import customtkinter as tk
4
+
5
+
6
+ class CdialogResult(Enum):
7
+ NONE = 0
8
+ OK = 1
9
+ CANCEL = 2
10
+ CLOSED = 3
11
+ YES = 4
12
+ NO = 5
13
+
14
+
15
+ class CdialogIcon(Enum):
16
+ INFO = "info"
17
+ WARNING = "warning"
18
+ ERROR = "error"
19
+
20
+ class CdialogBase:
21
+ """
22
+ Toplevel ベースのモーダルダイアログ基底クラス
23
+ """
24
+
25
+ def __init__(self, parent: tk.CTk, title="Dialog", size=(400, 200)):
26
+ self._parent = parent
27
+ self._result = CdialogResult.NONE
28
+ self._size = size
29
+
30
+ self._tkRoot = tk.CTkToplevel(parent)
31
+ self._tkRoot.title(title)
32
+ self._tkRoot.geometry(f"{size[0]}x{size[1]}")
33
+ self._tkRoot.protocol("WM_DELETE_WINDOW", self._on_window_close)
34
+
35
+ self._tkRoot.transient(parent)
36
+ self._tkRoot.resizable(False, False)
37
+ self._tkRoot.withdraw()
38
+
39
+ self._build(self._tkRoot)
40
+
41
+ # ---------------------------------
42
+ def _build(self, root: tk.CTkToplevel):
43
+ pass
44
+
45
+ # ---------------------------------
46
+ def _on_window_close(self):
47
+ self._result = CdialogResult.CLOSED
48
+ self._tkRoot.destroy()
49
+
50
+ # ---------------------------------
51
+ def _action_handle(self, result: CdialogResult):
52
+ self._result = result
53
+ self._tkRoot.destroy()
54
+
55
+ # ---------------------------------
56
+ def show(self) -> CdialogResult:
57
+ self._result = CdialogResult.NONE
58
+
59
+ self._center_to_parent()
60
+
61
+ self._tkRoot.deiconify()
62
+ self._tkRoot.lift()
63
+ self._tkRoot.grab_set()
64
+ self._tkRoot.focus_set()
65
+ self._parent.wait_window(self._tkRoot)
66
+
67
+ return self._result
68
+
69
+ # ---------------------------------
70
+ def _center_to_parent(self):
71
+ self._tkRoot.update_idletasks()
72
+ #画面サイズを取得
73
+ screen_width = self._tkRoot.winfo_screenwidth()
74
+ screen_height = self._tkRoot.winfo_screenheight()
75
+ #ダイアログのサイズ
76
+ w = self._size[0]
77
+ h = self._size[1]
78
+ #画面の中央にダイアログを配置
79
+ x = (screen_width - w) // 2
80
+ y = (screen_height - h) // 2
81
+ #ダイアログのサイズと位置を設定
82
+ self._tkRoot.geometry(f"{w}x{h}+{x}+{y}")
83
+
84
+ #####################################################################
85
+ # 使用例(単体テスト)
86
+ #####################################################################
87
+ if __name__ == "__main__":
88
+ root = tk.CTk()
89
+ root.withdraw()
90
+
91
+
92
+ dlg = CdialogBase(root, title="Dialog Example", size=(400, 200))
93
+ result = dlg.show()
94
+ print(f"Dialog result: {result}")
95
+
96
+ root.destroy()