mvcfw 0.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.
- mvcfw-0.1.0/PKG-INFO +9 -0
- mvcfw-0.1.0/README.md +274 -0
- mvcfw-0.1.0/fw/__init__.py +0 -0
- mvcfw-0.1.0/fw/contrib/__init__.py +0 -0
- mvcfw-0.1.0/fw/contrib/ui.py +106 -0
- mvcfw-0.1.0/fwfiles/ORM/base.py +29 -0
- mvcfw-0.1.0/fwfiles/ORM/commands.py +12 -0
- mvcfw-0.1.0/fwfiles/ORM/db.py +13 -0
- mvcfw-0.1.0/fwfiles/ORM/feilds.py +40 -0
- mvcfw-0.1.0/fwfiles/ORM/generalcommands.py +121 -0
- mvcfw-0.1.0/fwfiles/ORM/migration.py +234 -0
- mvcfw-0.1.0/fwfiles/UI/decorators.py +19 -0
- mvcfw-0.1.0/fwfiles/cli.py +29 -0
- mvcfw-0.1.0/fwfiles/projectcontext.py +21 -0
- mvcfw-0.1.0/fwfiles/protocols.py +9 -0
- mvcfw-0.1.0/fwfiles/routingjunction.py +89 -0
- mvcfw-0.1.0/fwfiles/scaffolding.py +293 -0
- mvcfw-0.1.0/mvcfw.egg-info/PKG-INFO +9 -0
- mvcfw-0.1.0/mvcfw.egg-info/SOURCES.txt +23 -0
- mvcfw-0.1.0/mvcfw.egg-info/dependency_links.txt +1 -0
- mvcfw-0.1.0/mvcfw.egg-info/entry_points.txt +2 -0
- mvcfw-0.1.0/mvcfw.egg-info/requires.txt +3 -0
- mvcfw-0.1.0/mvcfw.egg-info/top_level.txt +2 -0
- mvcfw-0.1.0/pyproject.toml +23 -0
- mvcfw-0.1.0/setup.cfg +4 -0
mvcfw-0.1.0/PKG-INFO
ADDED
mvcfw-0.1.0/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# MVCFW
|
|
2
|
+
|
|
3
|
+
MVCFW is a Python framework for building desktop applications using the **Model-View-Controller (MVC)** architecture.
|
|
4
|
+
|
|
5
|
+
**Version:** 0.1.0
|
|
6
|
+
**Created and maintained by Ritesh Kashyap**
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
* MVC architecture
|
|
11
|
+
* Hierarchical routing
|
|
12
|
+
* Master pages / layouts
|
|
13
|
+
* SQLAlchemy-based ORM
|
|
14
|
+
* Database migrations
|
|
15
|
+
* Reusable UI components
|
|
16
|
+
* Project and application scaffolding
|
|
17
|
+
* Development server with auto-reload
|
|
18
|
+
* Command-line interface
|
|
19
|
+
|
|
20
|
+
## Requirements
|
|
21
|
+
|
|
22
|
+
* Python 3.10+
|
|
23
|
+
* CustomTkinter
|
|
24
|
+
* SQLAlchemy
|
|
25
|
+
* Watchdog
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Install MVCFW with pip:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install mvcfw
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For development installation from source:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Create a Project
|
|
42
|
+
|
|
43
|
+
Create a new MVCFW project:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
mvcfw startproject MyProject
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Move into the project:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd MyProject
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Run the application:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python run.py
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Create an Application
|
|
62
|
+
|
|
63
|
+
Inside your project:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python manager.py startapp students
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This generates:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
students/
|
|
73
|
+
├── models.py
|
|
74
|
+
├── controller.py
|
|
75
|
+
├── router.py
|
|
76
|
+
├── __init__.py
|
|
77
|
+
└── views/
|
|
78
|
+
├── index.py
|
|
79
|
+
└── __init__.py
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The application is automatically added to `INSTALLED_APPS`.
|
|
83
|
+
|
|
84
|
+
## Routing
|
|
85
|
+
|
|
86
|
+
MVCFW uses hierarchical routing.
|
|
87
|
+
|
|
88
|
+
Project-level routing connects an application router:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
ROUTING={
|
|
92
|
+
"Main":app_router.ROUTING
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Routes can then be accessed using:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
routingjunction.goto(["Main","index"])
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Master Pages
|
|
103
|
+
|
|
104
|
+
Views can use a master page/layout:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from fwfiles.UI.decorators import use_masterpage
|
|
108
|
+
|
|
109
|
+
@use_masterpage("app.views.layout.Layout_default")
|
|
110
|
+
class Index(ctk.CTkFrame):
|
|
111
|
+
...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The master page provides a reusable layout while the view renders its content inside the layout body.
|
|
115
|
+
|
|
116
|
+
## ORM
|
|
117
|
+
|
|
118
|
+
MVCFW provides a simple SQLAlchemy-based ORM.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from fwfiles.ORM.base import Model
|
|
124
|
+
from fwfiles.ORM import feilds
|
|
125
|
+
|
|
126
|
+
class Student(Model):
|
|
127
|
+
__tablename__="students"
|
|
128
|
+
|
|
129
|
+
id=feilds.IntegerField(primary_key=True,increment=True)
|
|
130
|
+
name=feilds.CharField(max_length=255)
|
|
131
|
+
email=feilds.CharField(max_length=255)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Basic operations include:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
Student.all()
|
|
138
|
+
Student.filter(name="Ritesh")
|
|
139
|
+
|
|
140
|
+
student.save()
|
|
141
|
+
student.delete()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Database Migrations
|
|
145
|
+
|
|
146
|
+
Create migrations:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
python manager.py makemigrations
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Apply migrations:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
python manager.py migrate
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Reset the database:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
python manager.py reset_db
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
View installed applications:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
python manager.py show_apps
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Development Server
|
|
171
|
+
|
|
172
|
+
Run the development server:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
python manager.py runserver
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
MVCFW uses `watchdog` for detecting file changes during development.
|
|
179
|
+
|
|
180
|
+
## Reference Application
|
|
181
|
+
|
|
182
|
+
Generate the MVCFW reference application:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
mvcfw start_ref_app
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The reference application demonstrates:
|
|
189
|
+
|
|
190
|
+
* MVC
|
|
191
|
+
* Routing
|
|
192
|
+
* Master pages
|
|
193
|
+
* ORM
|
|
194
|
+
* Database migrations
|
|
195
|
+
* Reusable UI components
|
|
196
|
+
|
|
197
|
+
## CLI
|
|
198
|
+
|
|
199
|
+
Check the framework version:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
mvcfw --version
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
You can also use:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
mvcfw version
|
|
209
|
+
mvcfw -v
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Create a project:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
mvcfw startproject <project_name>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Create the reference application:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
mvcfw start_ref_app
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Project Commands
|
|
225
|
+
|
|
226
|
+
Inside a project:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
python manager.py startapp <app_name>
|
|
230
|
+
python manager.py makemigrations
|
|
231
|
+
python manager.py migrate
|
|
232
|
+
python manager.py reset_db
|
|
233
|
+
python manager.py show_apps
|
|
234
|
+
python manager.py runserver
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Project Structure
|
|
238
|
+
|
|
239
|
+
A project created with `startproject` has the following structure:
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
MyProject/
|
|
243
|
+
├── app/
|
|
244
|
+
│ ├── models.py
|
|
245
|
+
│ ├── controller.py
|
|
246
|
+
│ ├── router.py
|
|
247
|
+
│ └── views/
|
|
248
|
+
├── migrations/
|
|
249
|
+
├── MyProject/
|
|
250
|
+
│ ├── config.py
|
|
251
|
+
│ └── router.py
|
|
252
|
+
├── manager.py
|
|
253
|
+
└── run.py
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Architecture
|
|
257
|
+
|
|
258
|
+
MVCFW follows the Model-View-Controller architecture:
|
|
259
|
+
|
|
260
|
+
```text
|
|
261
|
+
Model
|
|
262
|
+
↓
|
|
263
|
+
Controller
|
|
264
|
+
↓
|
|
265
|
+
View
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Routing connects routes to controllers, while master pages provide reusable UI layouts.
|
|
269
|
+
|
|
270
|
+
## Author
|
|
271
|
+
|
|
272
|
+
**Ritesh Kashyap**
|
|
273
|
+
|
|
274
|
+
MVCFW is an open-source project focused on making desktop application development with Python and MVC architecture simpler.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from tkinter import ttk
|
|
2
|
+
import customtkinter as ctk
|
|
3
|
+
from fwfiles import routingjunction
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Card(ctk.CTkFrame):
|
|
8
|
+
def __init__(
|
|
9
|
+
self, master, title="Card Title", content="Card Content...", button_text=None, button_command=None, **kwargs):
|
|
10
|
+
super().__init__(master, corner_radius=10, **kwargs)
|
|
11
|
+
|
|
12
|
+
self.title_label = ctk.CTkLabel(self, text=title,font=("Helvetica", 16, "bold"))
|
|
13
|
+
self.title_label.pack(pady=(10, 0), padx=10, anchor="w")
|
|
14
|
+
|
|
15
|
+
self.content_label = ctk.CTkLabel(self, text=content)
|
|
16
|
+
self.content_label.pack(pady=(0, 5), padx=10, anchor="w")
|
|
17
|
+
|
|
18
|
+
self.button = None
|
|
19
|
+
|
|
20
|
+
if button_text:
|
|
21
|
+
self.button = ctk.CTkButton( self, text=button_text, command=button_command)
|
|
22
|
+
self.button.pack(pady=10, padx=10, anchor="e", fill="both")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class InfoCard(ctk.CTkFrame):
|
|
29
|
+
def __init__(self, master, title="Card Title", content="Card Content...", **kwargs):
|
|
30
|
+
super().__init__(master, corner_radius=10, **kwargs)
|
|
31
|
+
|
|
32
|
+
self.title_label = ctk.CTkLabel(self, text=title, font=("Helvetica", 16, "bold"))
|
|
33
|
+
self.title_label.pack(pady=(10, 0), padx=10, anchor="w")
|
|
34
|
+
|
|
35
|
+
self.content_label = ctk.CTkLabel(self, text=content)
|
|
36
|
+
self.content_label.pack(pady=(0, 5), padx=10, anchor="w")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Table(ctk.CTkFrame):
|
|
42
|
+
def __init__(self, master, headings=None, data=None, height=12, **kwargs):
|
|
43
|
+
super().__init__(master, corner_radius=10, **kwargs)
|
|
44
|
+
|
|
45
|
+
self.headings = headings or []
|
|
46
|
+
self.data = data or []
|
|
47
|
+
|
|
48
|
+
style = ttk.Style()
|
|
49
|
+
style.configure("Treeview", font=("Roboto Narrow", 10))
|
|
50
|
+
style.configure("Treeview.Heading", font=("Roboto", 10, "bold"))
|
|
51
|
+
|
|
52
|
+
self.table = ttk.Treeview(self, columns=self.headings, show="headings", height=height)
|
|
53
|
+
|
|
54
|
+
for col in self.headings:
|
|
55
|
+
self.table.heading(col, text=col)
|
|
56
|
+
self.table.column(col, anchor="w")
|
|
57
|
+
|
|
58
|
+
self.table.pack(padx=10, pady=10, anchor="w", fill="both", expand=True)
|
|
59
|
+
|
|
60
|
+
self.refresh(self.data)
|
|
61
|
+
|
|
62
|
+
def refresh(self, data):
|
|
63
|
+
self.clear()
|
|
64
|
+
|
|
65
|
+
for row in data:
|
|
66
|
+
self.table.insert("", "end", values=row)
|
|
67
|
+
|
|
68
|
+
def clear(self):
|
|
69
|
+
for item in self.table.get_children():
|
|
70
|
+
self.table.delete(item)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class LeftSidebar(ctk.CTkFrame):
|
|
74
|
+
def __init__(self, master, items=None, width=200, fg_color=None, **kwargs):
|
|
75
|
+
super().__init__(master, width=width, fg_color=fg_color, corner_radius=0, **kwargs)
|
|
76
|
+
self.items = items or []
|
|
77
|
+
self.create_items()
|
|
78
|
+
|
|
79
|
+
def create_items(self):
|
|
80
|
+
for item in self.items:
|
|
81
|
+
text = item.get("text", "")
|
|
82
|
+
command = item.get("command")
|
|
83
|
+
|
|
84
|
+
button = ctk.CTkButton(self, fg_color="#ebebeb", text_color="#292828", text=text, command=command)
|
|
85
|
+
|
|
86
|
+
button.pack(padx=5, pady=5, fill="x")
|
|
87
|
+
|
|
88
|
+
class CardGrid(ctk.CTkFrame):
|
|
89
|
+
def __init__(self, master, cards=None, card_width=250, padx=10, pady=10, fg_color="#ebebeb", **kwargs):
|
|
90
|
+
super().__init__(master, fg_color=fg_color, **kwargs)
|
|
91
|
+
self.cards, self.card_width, self.padx, self.pady = cards or [], card_width, padx, pady
|
|
92
|
+
self.bind("<Configure>", self.arrange_cards)
|
|
93
|
+
|
|
94
|
+
def arrange_cards(self, event=None):
|
|
95
|
+
cols = max(1, self.winfo_width() // self.card_width)
|
|
96
|
+
for i, card in enumerate(self.cards): card.grid(row=i // cols, column=i % cols, padx=self.padx, pady=self.pady, sticky="nsew")
|
|
97
|
+
for c in range(cols): self.grid_columnconfigure(c, weight=1)
|
|
98
|
+
|
|
99
|
+
def add_card(self, card): self.cards.append(card); self.arrange_cards()
|
|
100
|
+
def clear(self):
|
|
101
|
+
for card in self.cards: card.grid_forget()
|
|
102
|
+
self.cards.clear()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .db import Base, DBconnection
|
|
2
|
+
|
|
3
|
+
db=DBconnection()
|
|
4
|
+
|
|
5
|
+
class Model(Base):
|
|
6
|
+
__abstract__=True
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def create_table(cls):
|
|
11
|
+
db.create_tables()
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def all(cls):
|
|
15
|
+
return db.session.query(cls).all()
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def filter(cls, **kwargs):
|
|
19
|
+
return db.session.query(cls).filter_by(**kwargs).all()
|
|
20
|
+
|
|
21
|
+
def save(self):
|
|
22
|
+
db.session.add(self)
|
|
23
|
+
db.session.commit()
|
|
24
|
+
|
|
25
|
+
def delete(self):
|
|
26
|
+
db.session.delete(self)
|
|
27
|
+
db.session.commit()
|
|
28
|
+
|
|
29
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .migration import make_migrations, apply_migrations, reset_db
|
|
2
|
+
from .generalcommands import show_apps, runserver, startapp
|
|
3
|
+
|
|
4
|
+
def handle_command(command, app_name=None):
|
|
5
|
+
if command == "makemigrations": make_migrations()
|
|
6
|
+
elif command == "migrate": apply_migrations()
|
|
7
|
+
elif command == "reset_db": reset_db()
|
|
8
|
+
elif command == "show_apps": show_apps()
|
|
9
|
+
elif command == "runserver": runserver()
|
|
10
|
+
elif command=="startapp": startapp(app_name)
|
|
11
|
+
else: print(f"Unknown command: {command}")
|
|
12
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from sqlalchemy import create_engine
|
|
2
|
+
from sqlalchemy.orm import sessionmaker, declarative_base
|
|
3
|
+
|
|
4
|
+
Base = declarative_base()
|
|
5
|
+
|
|
6
|
+
class DBconnection:
|
|
7
|
+
def __init__(self, url="sqlite:///db.sqlite3", echo=True):
|
|
8
|
+
self.engine = create_engine(url, echo=echo)
|
|
9
|
+
self.Session = sessionmaker(bind=self.engine)
|
|
10
|
+
self.session = self.Session()
|
|
11
|
+
|
|
12
|
+
def create_tables(self):
|
|
13
|
+
Base.metadata.create_all(self.engine)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Float, Boolean, Text, Date, DateTime, Time
|
|
2
|
+
from sqlalchemy.dialects.mysql import LONGTEXT
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def IntegerField(primary_key=False, null=False, increment=False):
|
|
7
|
+
return Column(Integer, primary_key=primary_key, nullable=null,autoincrement=increment)
|
|
8
|
+
|
|
9
|
+
def CharField(max_length=255, null=False):
|
|
10
|
+
return Column(String(max_length), nullable=null)
|
|
11
|
+
|
|
12
|
+
def FloatField(null=False):
|
|
13
|
+
return Column(Float, nullable=null)
|
|
14
|
+
|
|
15
|
+
def BooleanField(default=False, null=False):
|
|
16
|
+
return Column(Boolean, default=default, nullable=null)
|
|
17
|
+
|
|
18
|
+
def TextField(long=False, null=False):
|
|
19
|
+
return Column(LONGTEXT if long else Text, nullable=null)
|
|
20
|
+
|
|
21
|
+
def FileField(max_length=255, null=False):
|
|
22
|
+
return Column(String(max_length), nullable=null)
|
|
23
|
+
|
|
24
|
+
def DateField(auto_now=False, auto_now_add=False, null=False):
|
|
25
|
+
default = None
|
|
26
|
+
if auto_now or auto_now_add:
|
|
27
|
+
default = datetime.utcnow().date
|
|
28
|
+
return Column(Date, default=default, nullable=null)
|
|
29
|
+
|
|
30
|
+
def DateTimeField(auto_now=False, auto_now_add=False, null=False):
|
|
31
|
+
default = None
|
|
32
|
+
if auto_now or auto_now_add:
|
|
33
|
+
default = datetime.utcnow
|
|
34
|
+
return Column(DateTime, default=default, nullable=null)
|
|
35
|
+
|
|
36
|
+
def TimeField(auto_now=False, auto_now_add=False, null=False):
|
|
37
|
+
default = None
|
|
38
|
+
if auto_now or auto_now_add:
|
|
39
|
+
default = datetime.utcnow().time
|
|
40
|
+
return Column(Time, default=default, nullable=null)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
import subprocess
|
|
5
|
+
from watchdog.observers import Observer
|
|
6
|
+
from watchdog.events import FileSystemEventHandler
|
|
7
|
+
from fwfiles import projectcontext
|
|
8
|
+
|
|
9
|
+
def show_apps():
|
|
10
|
+
config = projectcontext.get_config()
|
|
11
|
+
for i in config.INSTALLED_APPS:
|
|
12
|
+
print(i)
|
|
13
|
+
|
|
14
|
+
def startapp(name):
|
|
15
|
+
if not name:
|
|
16
|
+
print("Usage: python manager.py startapp <app_name>")
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
if os.path.exists(name):
|
|
20
|
+
print(f"Error: App '{name}' already exists.")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
os.makedirs(os.path.join(name,"views"))
|
|
24
|
+
os.makedirs(os.path.join("migrations",name))
|
|
25
|
+
|
|
26
|
+
files={
|
|
27
|
+
"__init__.py":"",
|
|
28
|
+
"models.py":"",
|
|
29
|
+
"controller.py":"""from .views import index
|
|
30
|
+
|
|
31
|
+
def start(req):
|
|
32
|
+
return index.Index
|
|
33
|
+
""",
|
|
34
|
+
"router.py":"""from . import controller
|
|
35
|
+
|
|
36
|
+
ROUTING={
|
|
37
|
+
"index":controller.start
|
|
38
|
+
}
|
|
39
|
+
""",
|
|
40
|
+
os.path.join("views","__init__.py"):"from . import index\n",
|
|
41
|
+
os.path.join("views","index.py"):'''import customtkinter as ctk
|
|
42
|
+
|
|
43
|
+
class Index(ctk.CTkFrame):
|
|
44
|
+
def __init__(self,master,data=None,**kwargs):
|
|
45
|
+
super().__init__(master,**kwargs)
|
|
46
|
+
ctk.CTkLabel(self,text="Welcome",font=("Arial",24,"bold")).pack(pady=40)
|
|
47
|
+
'''
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for path,content in files.items():
|
|
51
|
+
with open(os.path.join(name,path),"w",encoding="utf-8") as f:
|
|
52
|
+
f.write(content)
|
|
53
|
+
|
|
54
|
+
config=projectcontext.get_config()
|
|
55
|
+
|
|
56
|
+
if name not in config.INSTALLED_APPS:
|
|
57
|
+
config.INSTALLED_APPS.append(name)
|
|
58
|
+
|
|
59
|
+
config_path=config.__file__
|
|
60
|
+
|
|
61
|
+
with open(config_path,"w",encoding="utf-8") as f:
|
|
62
|
+
f.write(f"INSTALLED_APPS={config.INSTALLED_APPS!r}\n\n")
|
|
63
|
+
f.write(f"THEME={config.THEME!r}\n\n")
|
|
64
|
+
f.write(f"DATABASE={config.DATABASE!r}\n")
|
|
65
|
+
|
|
66
|
+
print(f"App '{name}' created successfully.")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RunServer:
|
|
71
|
+
def __init__(self, project_dir=None, entry_script="run.py"):
|
|
72
|
+
self.project_dir = project_dir or os.getcwd()
|
|
73
|
+
self.entry_script = entry_script
|
|
74
|
+
self.process = None
|
|
75
|
+
|
|
76
|
+
def start_process(self):
|
|
77
|
+
if self.process:
|
|
78
|
+
self.process.kill()
|
|
79
|
+
self.process.wait()
|
|
80
|
+
print(f"Starting {self.entry_script} ...")
|
|
81
|
+
self.process = subprocess.Popen([sys.executable, self.entry_script])
|
|
82
|
+
|
|
83
|
+
def on_any_event(self, event):
|
|
84
|
+
if event.src_path.endswith(".py"):
|
|
85
|
+
print(f"[Hot-Reload] Change detected: {event.src_path}")
|
|
86
|
+
self.start_process()
|
|
87
|
+
|
|
88
|
+
def run(self):
|
|
89
|
+
from watchdog.observers import Observer
|
|
90
|
+
from watchdog.events import FileSystemEventHandler
|
|
91
|
+
|
|
92
|
+
class Handler(FileSystemEventHandler):
|
|
93
|
+
def __init__(self, server):
|
|
94
|
+
self.server = server
|
|
95
|
+
def on_any_event(self, event):
|
|
96
|
+
self.server.on_any_event(event)
|
|
97
|
+
|
|
98
|
+
# Start observer
|
|
99
|
+
handler = Handler(self)
|
|
100
|
+
observer = Observer()
|
|
101
|
+
observer.schedule(handler, self.project_dir, recursive=True)
|
|
102
|
+
observer.start()
|
|
103
|
+
|
|
104
|
+
# Start initial process
|
|
105
|
+
self.start_process()
|
|
106
|
+
print(f"Watching {self.project_dir} for changes...")
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
while True:
|
|
110
|
+
time.sleep(1)
|
|
111
|
+
except KeyboardInterrupt:
|
|
112
|
+
observer.stop()
|
|
113
|
+
if self.process:
|
|
114
|
+
self.process.kill()
|
|
115
|
+
observer.join()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ----------------- Command line entry -----------------
|
|
119
|
+
def runserver():
|
|
120
|
+
server = RunServer()
|
|
121
|
+
server.run()
|