flexgui 1.0.2__tar.gz → 1.1.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flexgui
3
- Version: 1.0.2
3
+ Version: 1.1.0
4
4
  Summary: Thư viện GUI Python kéo-thả-thiết-kế trên tkinter — FlexGUI Studio tích hợp (design mode + mini VS Code cho trigger)
5
5
  Keywords: gui,tkinter,designer,drag-and-drop,visual-designer,no-code,vietnamese,flexgui
6
6
  Author: FlexGUI Team
@@ -73,13 +73,21 @@ flexgui.setup() # hoặc: from flexgui import design; design.mode = True
73
73
  Chạy `python myapp.py` → **Studio tự mở giao diện trống** (hoặc nạp
74
74
  thiết kế đã lưu). Tính năng:
75
75
 
76
- - Kéo thả mượt (canvas item — không tàn ảnh), **snap lưới 10px** (bật/tắt)
76
+ - Kéo thả mượt (canvas item — không tàn ảnh), **snap lưới 10px** (bật/tắt),
77
+ **lưới chấm** kiểu Figma
77
78
  - **Resize 8 núm** quanh thành phần đang chọn
78
79
  - **Multi-select**: Ctrl/Shift + click, quét vùng chuột, kéo cả cụm
79
80
  - Đổi z-order (menu chuột phải), nhân bản `Ctrl+D`, undo/redo `Ctrl+Z/Y`
80
81
  - Bảng thuộc tính bên phải: chữ, tọa độ, kích thước **pixel WYSIWYG**,
81
82
  màu chữ/nền, cỡ chữ, **độ trong suốt 0–100%**, ẩn khi chạy, khoá...
82
- - **Xem trước F5** — chạy thử app thật + trigger thật ngay tại chỗ
83
+ - **Start F5** — Studio **hóa thành GUI demo thật** ngay trong cửa sổ
84
+ (trigger chạy thật); `■ Stop` hoặc `Esc` quay về thiết kế
85
+ - **Thư viện F1** — tra cứu API + ví dụ (checkbox toggle, nhập input,
86
+ decorator, lệnh app…) — chèn code thẳng vào trigger được
87
+ - **Chuột phải → "Thuộc tính khi bắt đầu"** — chỉnh trạng thái ban đầu
88
+ của widget khi GUI start, hiện ngay trên canvas để dễ hình dung
89
+ - **Ẩn tạm trong studio** (menu chuột phải) — giấu widget để thiết kế đè
90
+ lên vùng nhiều nút chồng nhau; bảng góc trái-dưới liệt kê để hiện lại
83
91
  - **Build .exe** bằng PyInstaller (nút ❖ — tự cài nếu thiếu)
84
92
  - **Xuất .py** — 1 file tự chạy nhúng toàn bộ thiết kế + trigger
85
93
 
@@ -116,7 +124,10 @@ def on_text_5_change(): # gõ chữ trong text[5]
116
124
  ```
117
125
 
118
126
  Quy ước tên hàm: `on_<loại>_<id>_<sự kiện>`
119
- (`click` / `toggle` / `enter` / `change`). Studio tự sinh sườn hàm khi lưu.
127
+ (`click` / `toggle` / `enter` / `change`). Muốn sườn hàm nhanh thì bấm
128
+ **`+ Sườn hàm`** trên drawer (1.1.0: việc LƯU không tự sinh hàm nữa — tránh
129
+ file ngày càng dài; hàm mồ côi của widget đã xóa và hàm trùng tên được
130
+ **tự dọn** mỗi lần lưu).
120
131
 
121
132
  Kiểu **decorator** mạnh hơn (cũng viết trong file trigger):
122
133
 
@@ -137,6 +148,18 @@ def dong_ho(): ...
137
148
  def khoi_dong(): ...
138
149
  ```
139
150
 
151
+ ### Mini VS Code trong Studio — giờ có phát hiện lỗi như VSCode
152
+
153
+ - **Bôi đen + Tab** → thụt lề **cả cụm dòng**; **bôi đen + Shift+Tab** →
154
+ bỏ thụt lề cả cụm (giữ nguyên vùng chọn để thao tác tiếp)
155
+ - **Tự kiểm tra lỗi** ~0.5s sau khi ngừng gõ:
156
+ - lỗi cú pháp Python → tô đỏ dòng + **bảng VẤN ĐỀ** (bấm vào dòng để
157
+ nhảy tới, như Problems của VS Code)
158
+ - cảnh báo **trigger mồ côi** — hàm `on_label_9_click` mà `label[9]`
159
+ không còn trong thiết kế
160
+ - cảnh báo **hàm trùng tên** — def lại 2 lần, bản sau đè bản trước
161
+ - Enter tự thụt lề theo dòng trên (an toàn bộ gõ tiếng Việt Unikey/Telex)
162
+
140
163
  ## 4. Chạy thật
141
164
 
142
165
  Đổi 1 dòng trong `myapp.py`:
@@ -234,6 +257,16 @@ flexgui/
234
257
 
235
258
  ## Lịch sử phiên bản
236
259
 
260
+ - **1.1.0** — **Start/Stop thay cho "Xem trước"**: F5 biến Studio thành
261
+ **GUI demo thật ngay trong cửa sổ** (trigger chạy thật, đúng cỡ app);
262
+ `■ Stop` / `Esc` đưa về thiết kế. **Thư viện F1** (API + ví dụ chèn được).
263
+ **Chuột phải → Thuộc tính khi bắt đầu** (hiện ngay trên canvas). **Ẩn tạm
264
+ trong studio** + bảng góc hiện lại. Mini VS Code: **bôi đen + Tab /
265
+ Shift+Tab thụt cả cụm dòng** + **tự phát hiện lỗi** (cú pháp, trigger mồ
266
+ côi, hàm trùng) với bảng VẤN ĐỀ. **Fix bug lưu tự thêm hàm**: Lưu không
267
+ còn sinh sườn `def on_label_<id>_click` — chỉ bấm `+ Sườn hàm` mới sinh;
268
+ hàm mồ côi & trùng tên tự dọn khi lưu; xóa widget xóa luôn trigger của nó.
269
+ Đồ họa: nút Start xanh, lưới chấm Figma, thanh "đang chạy thử" nổi.
237
270
  - **1.0.2** — **Sửa lỗi "Xem trước" (F5) mở lại Studio**: khi file người
238
271
  dùng còn `flexgui.setup()` (hoặc `design.mode = True`, hoặc chạy với cờ
239
272
  `--design`), bấm "Xem trước" trước đây làm cửa sổ preview tự hủy rồi mở
@@ -44,13 +44,21 @@ flexgui.setup() # hoặc: from flexgui import design; design.mode = True
44
44
  Chạy `python myapp.py` → **Studio tự mở giao diện trống** (hoặc nạp
45
45
  thiết kế đã lưu). Tính năng:
46
46
 
47
- - Kéo thả mượt (canvas item — không tàn ảnh), **snap lưới 10px** (bật/tắt)
47
+ - Kéo thả mượt (canvas item — không tàn ảnh), **snap lưới 10px** (bật/tắt),
48
+ **lưới chấm** kiểu Figma
48
49
  - **Resize 8 núm** quanh thành phần đang chọn
49
50
  - **Multi-select**: Ctrl/Shift + click, quét vùng chuột, kéo cả cụm
50
51
  - Đổi z-order (menu chuột phải), nhân bản `Ctrl+D`, undo/redo `Ctrl+Z/Y`
51
52
  - Bảng thuộc tính bên phải: chữ, tọa độ, kích thước **pixel WYSIWYG**,
52
53
  màu chữ/nền, cỡ chữ, **độ trong suốt 0–100%**, ẩn khi chạy, khoá...
53
- - **Xem trước F5** — chạy thử app thật + trigger thật ngay tại chỗ
54
+ - **Start F5** — Studio **hóa thành GUI demo thật** ngay trong cửa sổ
55
+ (trigger chạy thật); `■ Stop` hoặc `Esc` quay về thiết kế
56
+ - **Thư viện F1** — tra cứu API + ví dụ (checkbox toggle, nhập input,
57
+ decorator, lệnh app…) — chèn code thẳng vào trigger được
58
+ - **Chuột phải → "Thuộc tính khi bắt đầu"** — chỉnh trạng thái ban đầu
59
+ của widget khi GUI start, hiện ngay trên canvas để dễ hình dung
60
+ - **Ẩn tạm trong studio** (menu chuột phải) — giấu widget để thiết kế đè
61
+ lên vùng nhiều nút chồng nhau; bảng góc trái-dưới liệt kê để hiện lại
54
62
  - **Build .exe** bằng PyInstaller (nút ❖ — tự cài nếu thiếu)
55
63
  - **Xuất .py** — 1 file tự chạy nhúng toàn bộ thiết kế + trigger
56
64
 
@@ -87,7 +95,10 @@ def on_text_5_change(): # gõ chữ trong text[5]
87
95
  ```
88
96
 
89
97
  Quy ước tên hàm: `on_<loại>_<id>_<sự kiện>`
90
- (`click` / `toggle` / `enter` / `change`). Studio tự sinh sườn hàm khi lưu.
98
+ (`click` / `toggle` / `enter` / `change`). Muốn sườn hàm nhanh thì bấm
99
+ **`+ Sườn hàm`** trên drawer (1.1.0: việc LƯU không tự sinh hàm nữa — tránh
100
+ file ngày càng dài; hàm mồ côi của widget đã xóa và hàm trùng tên được
101
+ **tự dọn** mỗi lần lưu).
91
102
 
92
103
  Kiểu **decorator** mạnh hơn (cũng viết trong file trigger):
93
104
 
@@ -108,6 +119,18 @@ def dong_ho(): ...
108
119
  def khoi_dong(): ...
109
120
  ```
110
121
 
122
+ ### Mini VS Code trong Studio — giờ có phát hiện lỗi như VSCode
123
+
124
+ - **Bôi đen + Tab** → thụt lề **cả cụm dòng**; **bôi đen + Shift+Tab** →
125
+ bỏ thụt lề cả cụm (giữ nguyên vùng chọn để thao tác tiếp)
126
+ - **Tự kiểm tra lỗi** ~0.5s sau khi ngừng gõ:
127
+ - lỗi cú pháp Python → tô đỏ dòng + **bảng VẤN ĐỀ** (bấm vào dòng để
128
+ nhảy tới, như Problems của VS Code)
129
+ - cảnh báo **trigger mồ côi** — hàm `on_label_9_click` mà `label[9]`
130
+ không còn trong thiết kế
131
+ - cảnh báo **hàm trùng tên** — def lại 2 lần, bản sau đè bản trước
132
+ - Enter tự thụt lề theo dòng trên (an toàn bộ gõ tiếng Việt Unikey/Telex)
133
+
111
134
  ## 4. Chạy thật
112
135
 
113
136
  Đổi 1 dòng trong `myapp.py`:
@@ -205,6 +228,16 @@ flexgui/
205
228
 
206
229
  ## Lịch sử phiên bản
207
230
 
231
+ - **1.1.0** — **Start/Stop thay cho "Xem trước"**: F5 biến Studio thành
232
+ **GUI demo thật ngay trong cửa sổ** (trigger chạy thật, đúng cỡ app);
233
+ `■ Stop` / `Esc` đưa về thiết kế. **Thư viện F1** (API + ví dụ chèn được).
234
+ **Chuột phải → Thuộc tính khi bắt đầu** (hiện ngay trên canvas). **Ẩn tạm
235
+ trong studio** + bảng góc hiện lại. Mini VS Code: **bôi đen + Tab /
236
+ Shift+Tab thụt cả cụm dòng** + **tự phát hiện lỗi** (cú pháp, trigger mồ
237
+ côi, hàm trùng) với bảng VẤN ĐỀ. **Fix bug lưu tự thêm hàm**: Lưu không
238
+ còn sinh sườn `def on_label_<id>_click` — chỉ bấm `+ Sườn hàm` mới sinh;
239
+ hàm mồ côi & trùng tên tự dọn khi lưu; xóa widget xóa luôn trigger của nó.
240
+ Đồ họa: nút Start xanh, lưới chấm Figma, thanh "đang chạy thử" nổi.
208
241
  - **1.0.2** — **Sửa lỗi "Xem trước" (F5) mở lại Studio**: khi file người
209
242
  dùng còn `flexgui.setup()` (hoặc `design.mode = True`, hoặc chạy với cờ
210
243
  `--design`), bấm "Xem trước" trước đây làm cửa sổ preview tự hủy rồi mở
@@ -53,7 +53,7 @@ from .widgets import Label, Button, Input, Checkbox, Text
53
53
  from .app import App
54
54
  from .runtime import run, setup, run_embedded
55
55
 
56
- __version__ = "1.0.2"
56
+ __version__ = "1.1.0"
57
57
  __author__ = "FlexGUI Team"
58
58
 
59
59
  __all__ = [
@@ -41,24 +41,33 @@ class App:
41
41
  goc : None = cửa sổ chính (tk.Tk)
42
42
  tk widget = cửa sổ con (Toplevel) — dùng khi
43
43
  Studio mở bản XEM TRƯỚC, không chặn Studio.
44
+ trong_khung : tk.Frame — chạy GẮN TRONG khung có sẵn (chế độ
45
+ START của Studio: cửa sổ Studio hóa thành app
46
+ demo thật, nút Stop đưa về thiết kế lại).
44
47
  """
45
48
 
46
49
  def __init__(self, title="Ứng dụng FlexGUI", width=860, height=560,
47
- bg=None, goc=None):
50
+ bg=None, goc=None, trong_khung=None):
48
51
  self.title = title
49
52
  self.width = int(width)
50
53
  self.height = int(height)
51
54
  self._nen = bg or MAU_APP["nen_mac_dinh"]
52
- self.xem_truoc = goc is not None # True = bản xem trước từ Studio
55
+ self.xem_truoc = (goc is not None) or (trong_khung is not None)
56
+ self.trong_khung = trong_khung
53
57
  self.dang_thiet_ke = False
58
+ self.hoi_dong = None # callback khi bản demo bị đóng
54
59
 
55
- if goc is None:
60
+ if trong_khung is not None:
61
+ # START trong Studio: root = khung nhúng (không phải cửa sổ)
62
+ self.root = trong_khung
63
+ elif goc is None:
56
64
  self.root = tk.Tk()
57
65
  else:
58
66
  self.root = tk.Toplevel(goc)
59
67
  self.root.title(title)
60
- self.root.geometry(f"{self.width}x{self.height}")
61
- self.root.minsize(320, 200)
68
+ if trong_khung is None:
69
+ self.root.geometry(f"{self.width}x{self.height}")
70
+ self.root.minsize(320, 200)
62
71
  self.root.configure(bg=self._nen)
63
72
  self.khung = tk.Frame(self.root, bg=self._nen)
64
73
  self.khung.pack(fill="both", expand=True)
@@ -200,8 +209,10 @@ class App:
200
209
  self.dong_co.bat_dau()
201
210
 
202
211
  if self.xem_truoc:
203
- # Bản xem trước từ Studio: đóng = dọn dẹp + destroy Toplevel
204
- self.root.protocol("WM_DELETE_WINDOW", self._dong_xem_truoc)
212
+ # Bản demo/xem trước: đóng = dọn dẹp + destroy (Toplevel)
213
+ # hoặc trả quyền cho Studio (chế độ trong_khung)
214
+ if isinstance(self.root, (tk.Tk, tk.Toplevel)):
215
+ self.root.protocol("WM_DELETE_WINDOW", self._dong_xem_truoc)
205
216
  else:
206
217
  self.root.protocol("WM_DELETE_WINDOW", self._dong_app)
207
218
 
@@ -221,11 +232,21 @@ class App:
221
232
  pass
222
233
 
223
234
  def _dong_xem_truoc(self):
224
- """Đóng bản XEM TRƯỚC (Studio vẫn sống)."""
235
+ """Đóng bản XEM TRƯỚC / demo START (Studio vẫn sống)."""
225
236
  self.dong_co.dung()
226
237
  try:
227
- self.root.destroy()
238
+ if isinstance(self.root, (tk.Tk, tk.Toplevel)):
239
+ self.root.destroy()
240
+ elif self.root.winfo_exists():
241
+ self.root.destroy() # khung nhúng trong Studio
228
242
  except Exception:
229
243
  pass
230
244
  if _reg._app_hien_tai() is self:
231
245
  _reg._dat_app(None)
246
+ hoi = getattr(self, "hoi_dong", None)
247
+ if hoi is not None:
248
+ self.hoi_dong = None # tránh gọi 2 lần
249
+ try:
250
+ hoi()
251
+ except Exception:
252
+ pass
@@ -28,9 +28,14 @@ def xuat_file_dung(duong_dan, thiet_ke, nguon_trigger):
28
28
  nguon_trigger : chuỗi code trigger (file .triggers.py)
29
29
  Trả về đường dẫn đã ghi.
30
30
  """
31
+ import copy
31
32
  import pprint
32
33
 
33
34
  ten = os.path.splitext(os.path.basename(duong_dan))[0]
35
+ # bỏ cờ studio-only (an_tam) — file chạy LUÔN hiện đầy đủ widget
36
+ thiet_ke = copy.deepcopy(thiet_ke)
37
+ for d in thiet_ke.get("components", []):
38
+ d.pop("an_tam", None)
34
39
  body = (
35
40
  f"# {'=' * 74}\n"
36
41
  f"# {ten} — sinh bởi FlexGUI Studio\n"