ikkVisualKit 0.0.0__tar.gz → 0.0.1__tar.gz

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.
Files changed (46) hide show
  1. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/PKG-INFO +1 -1
  2. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/CTkUiBase.py +110 -73
  3. ikkvisualkit-0.0.1/ikkVisualKit/Dialog/DialogGridSelecter.py +222 -0
  4. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/__init__.py +3 -1
  5. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/__init__.py +13 -1
  6. ikkvisualkit-0.0.1/ikkVisualKit/image/alert.dio.png +0 -0
  7. ikkvisualkit-0.0.1/ikkVisualKit/image/disconnect.dio.png +0 -0
  8. ikkvisualkit-0.0.1/ikkVisualKit/image/folder.dio.png +0 -0
  9. ikkvisualkit-0.0.1/ikkVisualKit/image/start.dio.png +0 -0
  10. ikkvisualkit-0.0.1/ikkVisualKit/image/swap.dio.png +0 -0
  11. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit.egg-info/PKG-INFO +1 -1
  12. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit.egg-info/SOURCES.txt +5 -0
  13. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/pyproject.toml +1 -1
  14. ikkvisualkit-0.0.0/ikkVisualKit/image/start.dio.png +0 -0
  15. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/LICENSE +0 -0
  16. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/README.md +0 -0
  17. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/CdialogBase.py +0 -0
  18. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/CdialogOk.py +0 -0
  19. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/CdialogYesNo.py +0 -0
  20. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/DialogBase.py +0 -0
  21. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/DialogOk.py +0 -0
  22. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/DialogSelectReaderPort.py +0 -0
  23. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Dialog/DialogYesNo.py +0 -0
  24. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/TkUiBase.py +0 -0
  25. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Tray/ResidentApp.py +0 -0
  26. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Tray/TrayMenu.py +0 -0
  27. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Tray/__init__.py +0 -0
  28. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Window/WindowBase.py +0 -0
  29. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Window/WindowBaseModan.py +0 -0
  30. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/Window/__init__.py +0 -0
  31. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/clear.dio.png +0 -0
  32. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/goto.dio.png +0 -0
  33. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/help.dio.png +0 -0
  34. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/link.dio.png +0 -0
  35. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/lock.dio.png +0 -0
  36. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/log.dio.png +0 -0
  37. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/open.dio.png +0 -0
  38. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/recipe.dio.png +0 -0
  39. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/save.dio.png +0 -0
  40. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/search.dio.png +0 -0
  41. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/setting.dio.png +0 -0
  42. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit/image/unlock.dio.png +0 -0
  43. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit.egg-info/dependency_links.txt +0 -0
  44. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit.egg-info/requires.txt +0 -0
  45. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/ikkVisualKit.egg-info/top_level.txt +0 -0
  46. {ikkvisualkit-0.0.0 → ikkvisualkit-0.0.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ikkVisualKit
3
- Version: 0.0.0
3
+ Version: 0.0.1
4
4
  Summary: Insight.k.k. VisualKit for easy application development
5
5
  Author-email: "Insight.k.k. Team" <maintainer@insightkk.net>
6
6
  License-Expression: MIT
@@ -1,4 +1,3 @@
1
- import customtkinter as ctk
2
1
  import os
3
2
  import tkinter as tk
4
3
  from pathlib import Path
@@ -7,22 +6,20 @@ from importlib.resources import files
7
6
  from io import BytesIO
8
7
 
9
8
  from PIL import Image
10
-
11
-
9
+ import customtkinter as ctk
10
+ #==============================================================================
12
11
  class CTkButtonWithTooltip(ctk.CTkButton):
13
12
  """ツールチップ対応のカスタムCTkButtonクラス"""
14
13
  def __init__(self, *args, **kwargs):
15
14
  super().__init__(*args, **kwargs)
16
15
  self._ui_base: Optional['CTkUiBase'] = None
17
16
  self._command: Optional[Callable[[], Any]] = None
18
-
19
17
  @property
20
18
  def tooltip_text(self) -> Optional[str]:
21
19
  """ツールチップテキストを取得"""
22
20
  if self._ui_base:
23
21
  return self._ui_base._button_tooltips.get(self)
24
22
  return None
25
-
26
23
  @tooltip_text.setter
27
24
  def tooltip_text(self, value: Optional[str]):
28
25
  """ツールチップテキストを設定"""
@@ -31,12 +28,10 @@ class CTkButtonWithTooltip(ctk.CTkButton):
31
28
  self._ui_base._button_tooltips[self] = value
32
29
  else:
33
30
  self._ui_base._button_tooltips.pop(self, None)
34
-
35
31
  @property
36
32
  def command(self) -> Optional[Callable[[], Any]]:
37
33
  """クリック時のコマンドを取得"""
38
34
  return self._command
39
-
40
35
  @command.setter
41
36
  def command(self, func: Optional[Callable[[], Any]]):
42
37
  """クリック時のコマンドを設定"""
@@ -45,22 +40,24 @@ class CTkButtonWithTooltip(ctk.CTkButton):
45
40
  self.configure(command=func, state=tk.NORMAL)
46
41
  else:
47
42
  self.configure(state=tk.DISABLED)
48
-
49
-
43
+ #==============================================================================
50
44
  class TextInputPair:
51
45
  """ラベルとテキストボックスのペア"""
52
46
  def __init__(self, label: ctk.CTkLabel, entry: ctk.CTkEntry):
53
- self.label = label
54
- self.entry = entry
47
+ self.label = label #ラベル
48
+ self.entry = entry #読み取り専用テキストボックス
55
49
  self._ui_base: Optional['CTkUiBase'] = None
56
-
50
+ self._command: Optional[Callable[[], Any]] = None
51
+ self._attention: bool = False # attention フラグ
52
+ self._blink_task_id: Optional[str] = None # 点滅タスクのID
53
+ self._original_border_width: int = self.entry.cget("border_width") # 元の枠線幅
54
+ self._original_border_color: str = self.entry.cget("border_color") # 元の枠線色
57
55
  @property
58
56
  def tooltip_text(self) -> Optional[str]:
59
57
  """ツールチップテキストを取得"""
60
58
  if self._ui_base:
61
59
  return self._ui_base._entry_tooltips.get(self.entry)
62
60
  return None
63
-
64
61
  @tooltip_text.setter
65
62
  def tooltip_text(self, value: Optional[str]):
66
63
  """ツールチップテキストを設定"""
@@ -69,56 +66,94 @@ class TextInputPair:
69
66
  self._ui_base._entry_tooltips[self.entry] = value
70
67
  else:
71
68
  self._ui_base._entry_tooltips.pop(self.entry, None)
72
-
73
69
  @property
74
- def display_text(self) -> str:
70
+ def command(self) -> Optional[Callable[[], Any]]:
71
+ """クリック時のコマンドを取得"""
72
+ return self._command
73
+ @command.setter
74
+ def command(self, func: Optional[Callable[[], Any]]):
75
+ """クリック時のコマンドを設定(テキストボックス・ラベル両方のクリックに対応)"""
76
+ self._command = func
77
+ if func is not None:
78
+ # テキストボックスのクリックイベントをバインド
79
+ self.entry.bind("<Button-1>", lambda e: func())
80
+ # ラベルのクリックイベントをバインド
81
+ self.label.bind("<Button-1>", lambda e: func())
82
+ else:
83
+ # クリックイベントの削除
84
+ self.entry.unbind("<Button-1>")
85
+ self.label.unbind("<Button-1>")
86
+ @property
87
+ def attention(self) -> bool:
88
+ """attention プロパティを取得"""
89
+ return self._attention
90
+ @attention.setter
91
+ def attention(self, value: bool):
92
+ """attention プロパティを設定(True=枠線点滅、False=通常状態)"""
93
+ self._attention = value
94
+ if value:
95
+ self._start_border_blink()
96
+ else:
97
+ self._stop_border_blink()
98
+ @property
99
+ def value(self) -> str:
75
100
  """表示テキストを取得"""
76
101
  return self.entry.get()
77
-
78
- @display_text.setter
79
- def display_text(self, text: str):
102
+ @value.setter
103
+ def value(self, text: str):
80
104
  """表示テキストを設定(読み取り専用テキストボックスに値を設定)"""
81
105
  # 読み取り専用を一時的に解除して値を設定
82
106
  self.entry.configure(state="normal")
83
107
  self.entry.delete(0, "end")
84
108
  self.entry.insert(0, text)
85
109
  self.entry.configure(state="disabled")
86
-
87
- # 背景色を黄色く2回点滅
88
- self._blink_entry()
89
-
110
+ self._blink_entry()# 背景色を黄色く2回点滅
90
111
  def _blink_entry(self):
91
112
  """エントリーの背景色を黄色く2回点滅"""
92
113
  if not self._ui_base or not self._ui_base._tkRoot:
93
114
  return
94
-
95
115
  blink_state = [0] # 点滅状態を保持(0=黄→1=元→2=黄→3=元→終了)
96
116
  original_bg = self.entry.cget("fg_color") # 元の背景色を保存
97
-
98
117
  def toggle_color():
99
118
  if blink_state[0] % 2 == 0:
100
119
  self.entry.configure(fg_color="#FFFF00") # 黄色
101
120
  else:
102
121
  self.entry.configure(fg_color=original_bg) # 元の色
103
-
104
122
  blink_state[0] += 1
105
123
  if blink_state[0] < 4: # 4回の遷移 = 2回点滅
106
124
  self._ui_base._tkRoot.after(300, toggle_color)
107
-
108
125
  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
-
126
+ def _start_border_blink(self):
127
+ """エントリーの枠線を赤く太くして点滅"""
128
+ if not self._ui_base or not self._ui_base._tkRoot:
129
+ return
130
+ # 前回の点滅をキャンセル
131
+ if self._blink_task_id:
132
+ self._ui_base._tkRoot.after_cancel(self._blink_task_id)
133
+ blink_state = [True] # 点滅状態(True=赤、False=元)
134
+ def toggle_border():
135
+ if blink_state[0]:
136
+ self.entry.configure(border_width=4, border_color="#FF0000") # 赤、太い
137
+ else:
138
+ self.entry.configure(border_width=self._original_border_width,
139
+ border_color=self._original_border_color) # 元に戻す
140
+ blink_state[0] = not blink_state[0]
141
+ # 点続けるために next task をスケジュール
142
+ if self._attention:
143
+ self._blink_task_id = self._ui_base._tkRoot.after(500, toggle_border)
144
+ toggle_border()
145
+ def _stop_border_blink(self):
146
+ """エントリーの枠線の点滅を停止して元に戻す"""
147
+ if self._blink_task_id:
148
+ if self._ui_base and self._ui_base._tkRoot:
149
+ self._ui_base._tkRoot.after_cancel(self._blink_task_id)
150
+ self._blink_task_id = None
151
+ # 枠線を元の状態に戻す
152
+ if self._ui_base and self._ui_base._tkRoot:
153
+ self.entry.configure(border_width=self._original_border_width,
154
+ border_color=self._original_border_color)
121
155
 
156
+ #==============================================================================
122
157
  class LogViewBox:
123
158
  """ログ表示用テキストボックス"""
124
159
  def __init__(self, text_widget: ctk.CTkTextbox, x: int, y: int, width: int, initial_lines: int = 2):
@@ -128,73 +163,66 @@ class LogViewBox:
128
163
  self.width = width
129
164
  self.lines = initial_lines
130
165
  self.parent = None
131
-
132
166
  # ログレベルごとの色定義
133
167
  self._log_level_colors = {
134
168
  "ERROR": "#ff6b6b", # 赤
135
169
  "WARN": "#ffa500", # オレンジ
136
- "INFO": "#4dabf7", # 青
170
+ "INFO": "#0000ff", # 青
137
171
  "DEBUG": "#909090" # グレー
138
172
  }
139
-
140
173
  # タグ設定
141
174
  for level, color in self._log_level_colors.items():
142
175
  text_widget.tag_config(level, foreground=color)
143
-
144
176
  def add_log(self, message: str, level: str = "INFO"):
145
177
  """ログを追加(最下行に追加、自動スクロール)"""
146
178
  if level not in self._log_level_colors:
147
179
  level = "INFO"
148
-
149
180
  # テキストボックスを編集可能に
150
181
  self.text_widget.configure(state="normal")
151
182
 
152
- # ログエントリを作成
153
- log_entry = f"[{level}] {message}\n"
183
+ # テキストボックスが空でなければ改行を追加
184
+ current_text = self.text_widget.get("1.0", "end-1c")
185
+ if current_text:
186
+ self.text_widget.insert("end", "\n")
154
187
 
188
+ # ログエントリを作成(改行なし)
189
+ log_entry = f"[{level}] {message}"
155
190
  # 末尾に追加
156
- start_index = self.text_widget.index("end-1c")
191
+ start_index = self.text_widget.index("end")
157
192
  self.text_widget.insert("end", log_entry)
158
-
159
193
  # ログレベルに応じて色付け
160
- end_index = self.text_widget.index("end-1c")
194
+ end_index = self.text_widget.index("end")
161
195
  self.text_widget.tag_add(level, start_index, end_index)
162
-
163
196
  # 最下行にスクロール
164
197
  self.text_widget.see("end")
165
-
166
198
  # 読み取り専用に戻す
167
199
  self.text_widget.configure(state="disabled")
168
-
169
200
  def expand(self, lines: int):
170
201
  """表示行数を拡張"""
171
202
  self.lines = lines
172
203
  line_height = 20 # 1行あたりのピクセル高さ(概算)
173
204
  new_height = lines * line_height + 10
174
- self.text_widget.configure(height=new_height)
175
-
205
+ self.text_widget.configure(height=80)
176
206
  def collapse(self):
177
207
  """デフォルト表示に戻す"""
178
208
  self.expand(self.lines)
179
-
180
209
  def clear(self):
181
210
  """ログをクリア"""
182
211
  self.text_widget.configure(state="normal")
183
212
  self.text_widget.delete("1.0", "end")
184
213
  self.text_widget.configure(state="disabled")
185
-
186
214
  @property
187
215
  def tooltip_text(self) -> Optional[str]:
188
216
  """ツールチップ(互換性のため)"""
189
217
  return None
190
-
191
218
  @tooltip_text.setter
192
219
  def tooltip_text(self, value: Optional[str]):
193
220
  """ツールチップ設定"""
194
221
  if self.parent and value:
195
222
  self.parent._entry_tooltips[self.text_widget] = value
196
-
197
-
223
+ #==============================================================================
224
+ #
225
+ #==============================================================================
198
226
  class CTkUiBase:
199
227
  """
200
228
  Tkは必ずメインスレッドで動かす。
@@ -306,17 +334,17 @@ class CTkUiBase:
306
334
  if size_rank == 1:
307
335
  btn_width = 96
308
336
  btn_height = 64
309
- btn_font_size = 12
337
+ btn_font_size = 16
310
338
  btn_compound = "bottom" # アイコンの下に文字
311
339
  fg_color = "#ADD8E6" # ライトブルー
312
- hover_color = "#87CEEB" # スカイブルー
340
+ hover_color = "#FF911B"
313
341
  elif size_rank == 2:
314
342
  btn_width = 128
315
343
  btn_height = 64
316
- btn_font_size = 12
344
+ btn_font_size = 16
317
345
  btn_compound = "bottom" # アイコンの下に文字
318
346
  fg_color = "#00FF00" # Lime
319
- hover_color = "#32CD32" # ライムグリーン
347
+ hover_color = "#FF911B"
320
348
  else: # size_rank == 0
321
349
  btn_width = 48
322
350
  btn_height = 32
@@ -333,8 +361,9 @@ class CTkUiBase:
333
361
  "state": tk.NORMAL if command is not None else tk.DISABLED,
334
362
  "fg_color": fg_color,
335
363
  "hover_color": hover_color,
336
- "text_color": "black",
337
- "font": ctk.CTkFont(size=btn_font_size, weight="normal"),
364
+ "text_color": "#1a1a1a", # より濃い黒
365
+ "text_color_disabled": "#1a1a1a", # DISABLED時も黒
366
+ "font": ctk.CTkFont("Meiryo",size=btn_font_size, weight="normal"),
338
367
  "border_width": 2,
339
368
  "border_color": "#004382",
340
369
  "corner_radius": 4,
@@ -353,17 +382,18 @@ class CTkUiBase:
353
382
 
354
383
  # デフォルトの色を保存
355
384
  default_fg_color = button_kwargs["fg_color"]
385
+ default_text_color = button_kwargs["text_color"]
356
386
  hover_fg_color = button_kwargs["hover_color"]
357
387
 
358
388
  #マウスホバー処理
359
389
  def on_enter(event):
360
- btn.configure(fg_color=hover_fg_color, text_color="black")
390
+ btn.configure(fg_color=hover_fg_color, text_color=default_text_color)
361
391
  tooltip_text = self._button_tooltips.get(btn)
362
392
  if tooltip_text:
363
393
  self._show_tooltip(btn, tooltip_text)
364
394
  #マウスホバー解除処理
365
395
  def on_leave(event):
366
- btn.configure(fg_color=default_fg_color, text_color="black")
396
+ btn.configure(fg_color=default_fg_color, text_color=default_text_color)
367
397
  self._hide_tooltip()
368
398
  #ホバーイベントのバインド
369
399
  btn.bind("<Enter>", on_enter)
@@ -438,28 +468,35 @@ class CTkUiBase:
438
468
  def make_LogTerminal(self,
439
469
  x: int, y: int, # 左上座標
440
470
  width: int = 300, # 幅
441
- initial_lines: int = 2, # 初期表示行数
471
+ initial_lines: int = 4, # 初期表示行数
442
472
  ) -> Optional[LogViewBox]:
443
473
  """ログ表示用テキストボックスを生成"""
444
474
  if not self._tkRoot:
445
475
  return None
446
- # CTkTextbox生成
476
+ # フォント定義
477
+ font = ctk.CTkFont("Meiryo", size=12)
478
+
479
+ # 実際の行高を測定
480
+ # Meiryo size=16 の場合、約 18-20px が一般的
481
+ line_height = 20 # Meiryo 16pt の実測値
482
+ padding = 2 # 上下マージン
483
+
447
484
  textbox = ctk.CTkTextbox(
448
485
  self._tkRoot,
449
486
  width=width,
450
- height=initial_lines * 20 + 10,
451
- font=ctk.CTkFont("Courier", size=10),
487
+ height=initial_lines * line_height + padding, # 例: 3行 = 66px
488
+ font=font,
452
489
  border_width=1,
453
490
  border_color="#000000",
454
491
  corner_radius=4,
492
+ padx=2,
493
+ pady=2,
455
494
  )
456
495
  textbox.place(x=x, y=y)
457
- textbox.configure(state="disabled") # 読み取り専用
458
496
 
459
497
  # ログビューボックス生成
460
- log_view = LogViewBox(textbox, x, y, width, initial_lines)
498
+ log_view = LogViewBox(textbox, x, y, width, 80)
461
499
  log_view.parent = self
462
-
463
500
  return log_view
464
501
  #========================================================
465
502
  #UIに使う画像の登録
@@ -0,0 +1,222 @@
1
+ # DialogGridSelecter.py
2
+ import customtkinter as tk
3
+ from .CdialogBase import CdialogBase
4
+
5
+ class DialogGridSelecter(CdialogBase):
6
+ """
7
+ 2次元配列に応じたボタングリッドを表示するダイアログ
8
+ ボタンサイズ: 64x48
9
+ ボタン間隙: 16
10
+ ウィンドウ端余白: 48
11
+ """
12
+
13
+ def __init__(self, parent,
14
+ title="Grid Selecter",
15
+ grid_data=None,
16
+ ):
17
+ """
18
+ Parameters:
19
+ -----------
20
+ parent : tk.CTk | tk.CTkToplevel | object with _tkRoot attribute
21
+ 親ウィンドウ(tk.CTk, tk.CTkToplevel, または _tkRoot を持つオブジェクト)
22
+ title : str
23
+ ダイアログのタイトル
24
+ grid_data : list[list[str|None]]
25
+ 2次元配列(ボタンのテキスト、Noneの部分はボタンなし)
26
+ """
27
+ # parentが_tkRoot属性を持つ場合(WindowBaseModan等)はそれを取得
28
+ if hasattr(parent, '_tkRoot'):
29
+ parent = parent._tkRoot
30
+
31
+ self._grid_data = grid_data if grid_data is not None else []
32
+ self._btn_min_width = 64
33
+ self._btn_height = 48
34
+ self._btn_spacing = 16
35
+ self._window_padding = 48
36
+ self._selected_text = None
37
+ self._btn_width = 64 # 全ボタン共通の幅
38
+
39
+ # グリッドのサイズを計算
40
+ size = self._calculate_window_size()
41
+
42
+ super().__init__(parent, title=title, size=size)
43
+
44
+ def _calculate_window_size(self):
45
+ """ウィンドウサイズを計算(全テキスト幅の最大値を基準)"""
46
+ if not self._grid_data:
47
+ return (400, 200)
48
+
49
+ # 行数と列数を取得
50
+ num_rows = len(self._grid_data)
51
+ num_cols = max(len(row) for row in self._grid_data) if self._grid_data else 1
52
+
53
+ # フォント情報を取得してテキスト幅を計算
54
+ font = tk.CTkFont("Meiryo", 24, weight="bold") # フォントサイズは適宜調整
55
+
56
+ # 全テキストの最大幅を計算
57
+ max_text_width = self._btn_min_width
58
+ for row in self._grid_data:
59
+ for text in row:
60
+ if text is not None:
61
+ # テキストの実際の幅を計算
62
+ text_width = font.measure(text)
63
+ # ボタンの内部パディング(左右各5px)を含める
64
+ actual_width = text_width + 10
65
+ max_text_width = max(max_text_width, actual_width)
66
+
67
+ # 全ボタンを同じ幅で使用
68
+ self._btn_width = max_text_width
69
+
70
+ # グリッド全体の幅と高さ
71
+ grid_width = (self._btn_width * num_cols) + (self._btn_spacing * (num_cols - 1))
72
+ grid_height = (self._btn_height + self._btn_spacing) * num_rows - self._btn_spacing
73
+
74
+ # ウィンドウのサイズ(余白を含む)
75
+ window_width = grid_width + (self._window_padding * 2)
76
+ window_height = grid_height + (self._window_padding * 2)
77
+
78
+ return (window_width, window_height)
79
+
80
+ def _build(self, root):
81
+ """グリッドレイアウトを構築"""
82
+ # ライトテーマの設定
83
+ root.configure(fg_color="#f0f0f0")
84
+
85
+ # 背景を作成(クリック可能にするため)
86
+ bg_frame = tk.CTkFrame(root, fg_color="#f0f0f0")
87
+ bg_frame.pack(fill="both", expand=True)
88
+ bg_frame.bind("<Button-1>", self._on_background_click)
89
+
90
+ # グリッドの全体サイズを計算
91
+ num_rows = len(self._grid_data)
92
+ num_cols = max(len(row) for row in self._grid_data) if self._grid_data else 1
93
+ grid_width = (self._btn_width * num_cols) + (self._btn_spacing * (num_cols - 1))
94
+ grid_height = (self._btn_height + self._btn_spacing) * num_rows - self._btn_spacing
95
+
96
+ # ボタンフレーム(サイズを明示的に設定)
97
+ btn_frame = tk.CTkFrame(bg_frame, fg_color="#f0f0f0", width=grid_width, height=grid_height)
98
+ btn_frame.place(x=self._window_padding, y=self._window_padding)
99
+
100
+ # グリッドにボタンを配置
101
+ for row_idx, row in enumerate(self._grid_data):
102
+ for col_idx, text in enumerate(row):
103
+ # X座標を計算(全ボタン共通の幅とボタン間隙を考慮)
104
+ x = (self._btn_width + self._btn_spacing) * col_idx
105
+ y = row_idx * (self._btn_height + self._btn_spacing)
106
+
107
+ if text is not None:
108
+ # ボタンを作成(全ボタン共通の幅を使用)
109
+ btn = tk.CTkButton(
110
+ btn_frame,
111
+ text=text,
112
+ width=self._btn_width,
113
+ height=self._btn_height,
114
+ font=("Meiryo", 16),
115
+ border_width=2,
116
+ corner_radius=4,
117
+ fg_color="#ffffff",
118
+ text_color="#000000",
119
+ border_color="#cccccc",
120
+ hover_color="#e000e0",
121
+ command=lambda t=text: self._on_button_click(t)
122
+ )
123
+ btn.place(x=x, y=y)
124
+
125
+ # Escapeキーで閉じる
126
+ root.bind("<Escape>", lambda e: self._on_window_close())
127
+
128
+ def _on_button_click(self, text):
129
+ """ボタンをクリックした時の処理"""
130
+ self._selected_text = text
131
+ self._tkRoot.destroy()
132
+
133
+ def _on_background_click(self, event):
134
+ """背景をクリックした時の処理"""
135
+ self._selected_text = None
136
+ self._tkRoot.destroy()
137
+
138
+ def _on_window_close(self):
139
+ """ウィンドウを閉じた時の処理"""
140
+ self._selected_text = None
141
+ self._tkRoot.destroy()
142
+
143
+ def show(self):
144
+ """ダイアログを表示して結果を返す"""
145
+ self._selected_text = None
146
+ self._position_near_mouse()
147
+
148
+ self._tkRoot.deiconify()
149
+ self._tkRoot.lift()
150
+ self._tkRoot.grab_set()
151
+ self._tkRoot.focus_set()
152
+ self._parent.wait_window(self._tkRoot)
153
+
154
+ return self._selected_text
155
+
156
+ def _position_near_mouse(self):
157
+ """マウス位置をダイアログの中心に配置"""
158
+ self._tkRoot.update_idletasks()
159
+
160
+ # マウスの現在位置を取得
161
+ mouse_x = self._tkRoot.winfo_pointerx()
162
+ mouse_y = self._tkRoot.winfo_pointery()
163
+
164
+ # ダイアログのサイズを取得
165
+ dlg_width = self._size[0]
166
+ dlg_height = self._size[1]
167
+
168
+ # 画面のサイズを取得
169
+ screen_width = self._tkRoot.winfo_screenwidth()
170
+ screen_height = self._tkRoot.winfo_screenheight()
171
+
172
+ # マウス位置がダイアログの中心になるように配置
173
+ x = mouse_x - (dlg_width // 2)
174
+ y = mouse_y - (dlg_height // 2)
175
+
176
+ # ダイアログが画面をはみ出さないようにクリップ
177
+ # 左側がはみ出す場合
178
+ if x < 0:
179
+ x = 10
180
+
181
+ # 右側がはみ出す場合
182
+ if x + dlg_width > screen_width:
183
+ x = screen_width - dlg_width - 10
184
+
185
+ # 上側がはみ出す場合
186
+ if y < 0:
187
+ y = 10
188
+
189
+ # 下側がはみ出す場合
190
+ if y + dlg_height > screen_height:
191
+ y = screen_height - dlg_height - 10
192
+
193
+ # ダイアログのサイズと位置を設定
194
+ self._tkRoot.geometry(f"{dlg_width}x{dlg_height}+{x}+{y}")
195
+
196
+ #####################################################################
197
+ # 使用例(単体テスト)
198
+ #####################################################################
199
+ if __name__ == "__main__":
200
+ #TKのルート
201
+ root = tk.CTk()
202
+
203
+ def test_grid_selecter():
204
+ # 2次元配列でボタンを定義
205
+ grid_data = [
206
+ ["入力", "確認", "完了"],
207
+ ["キャンセル", "実行", "リセット"],
208
+ [None, "削除", None],
209
+ ]
210
+
211
+ # rootまたは_tkRootを持つオブジェクトの両方に対応
212
+ dlg = DialogGridSelecter(root, # root直接渡しでもOK
213
+ title="グリッド選択ダイアログ",
214
+ grid_data=grid_data
215
+ )
216
+ result = dlg.show()
217
+ print(f"選択された: {result}")
218
+
219
+ root.geometry("600x400")
220
+ root.title("Grid Selector Dialog テスト")
221
+ tk.CTkButton(root, text="グリッド選択ダイアログを表示", command=test_grid_selecter).pack(pady=20)
222
+ root.mainloop()
@@ -5,6 +5,7 @@ from .DialogSelectReaderPort import DialogSelectReaderPort
5
5
  from .CdialogBase import CdialogBase, CdialogResult, CdialogIcon
6
6
  from .CdialogOk import CdialogOk
7
7
  from .CdialogYesNo import CdialogYesNo
8
+ from .DialogGridSelecter import DialogGridSelecter
8
9
 
9
10
  __all__ = [
10
11
  "DialogBase",
@@ -17,5 +18,6 @@ __all__ = [
17
18
  "CdialogResult",
18
19
  "CdialogIcon",
19
20
  "CdialogOk",
20
- "CdialogYesNo"
21
+ "CdialogYesNo",
22
+ "DialogGridSelecter",
21
23
  ]
@@ -2,7 +2,10 @@ from .TkUiBase import TkUiBase
2
2
  from .CTkUiBase import CTkUiBase
3
3
  from .Window import WindowBase
4
4
  from .Window import WindowBaseModan
5
- from .Dialog import DialogBase, DialogResult, DialogIcon, DialogOk, DialogYesNo, DialogSelectReaderPort
5
+ from .Dialog import (
6
+ DialogBase, DialogResult, DialogIcon, DialogOk, DialogYesNo, DialogSelectReaderPort,
7
+ CdialogBase, CdialogResult, CdialogIcon, CdialogOk, CdialogYesNo, DialogGridSelecter
8
+ )
6
9
  from .Tray import TrayMenu, acquire_single_instance_guard, SingleInstanceGuard
7
10
 
8
11
  __all__ = [
@@ -10,12 +13,21 @@ __all__ = [
10
13
  "CTkUiBase",
11
14
  "WindowBase",
12
15
  "WindowBaseModan",
16
+ # tkinter版ダイアログ
13
17
  "DialogBase",
14
18
  "DialogResult",
15
19
  "DialogIcon",
16
20
  "DialogOk",
17
21
  "DialogYesNo",
18
22
  "DialogSelectReaderPort",
23
+ # customtkinter版ダイアログ
24
+ "CdialogBase",
25
+ "CdialogResult",
26
+ "CdialogIcon",
27
+ "CdialogOk",
28
+ "CdialogYesNo",
29
+ "DialogGridSelecter",
30
+ # トレイ機能
19
31
  "TrayMenu",
20
32
  "acquire_single_instance_guard",
21
33
  "SingleInstanceGuard",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ikkVisualKit
3
- Version: 0.0.0
3
+ Version: 0.0.1
4
4
  Summary: Insight.k.k. VisualKit for easy application development
5
5
  Author-email: "Insight.k.k. Team" <maintainer@insightkk.net>
6
6
  License-Expression: MIT
@@ -13,6 +13,7 @@ ikkVisualKit/Dialog/CdialogBase.py
13
13
  ikkVisualKit/Dialog/CdialogOk.py
14
14
  ikkVisualKit/Dialog/CdialogYesNo.py
15
15
  ikkVisualKit/Dialog/DialogBase.py
16
+ ikkVisualKit/Dialog/DialogGridSelecter.py
16
17
  ikkVisualKit/Dialog/DialogOk.py
17
18
  ikkVisualKit/Dialog/DialogSelectReaderPort.py
18
19
  ikkVisualKit/Dialog/DialogYesNo.py
@@ -23,7 +24,10 @@ ikkVisualKit/Tray/__init__.py
23
24
  ikkVisualKit/Window/WindowBase.py
24
25
  ikkVisualKit/Window/WindowBaseModan.py
25
26
  ikkVisualKit/Window/__init__.py
27
+ ikkVisualKit/image/alert.dio.png
26
28
  ikkVisualKit/image/clear.dio.png
29
+ ikkVisualKit/image/disconnect.dio.png
30
+ ikkVisualKit/image/folder.dio.png
27
31
  ikkVisualKit/image/goto.dio.png
28
32
  ikkVisualKit/image/help.dio.png
29
33
  ikkVisualKit/image/link.dio.png
@@ -35,4 +39,5 @@ ikkVisualKit/image/save.dio.png
35
39
  ikkVisualKit/image/search.dio.png
36
40
  ikkVisualKit/image/setting.dio.png
37
41
  ikkVisualKit/image/start.dio.png
42
+ ikkVisualKit/image/swap.dio.png
38
43
  ikkVisualKit/image/unlock.dio.png
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ikkVisualKit"
7
- version = "0.0.0"
7
+ version = "0.0.1"
8
8
  description = "Insight.k.k. VisualKit for easy application development"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes