Concepts#
This page covers the architecture and conventions behind a VIStk project. For a hands-on walkthrough, start with Quickstart.
Project Structure#
A VIStk project has the following folder layout:
MyProject/
├── .VIS/
│ ├── project.json <- project registry (screens, versions, metadata)
│ ├── Host.py <- Host entry point (generated by VIS new; not user-editable)
│ ├── Templates/ <- screen and element templates used by the CLI
│ └── project.spec <- PyInstaller spec file (generated on release)
├── Screens/
│ └── <screen>/ <- UI element files, prefixed f_ (e.g. f_header.py)
├── modules/
│ └── <screen>/ <- logic files, prefixed m_ (e.g. m_header.py)
├── Icons/ <- .ico (Windows) or .xbm (Linux) icon files
├── Images/ <- image assets used by VIMG
├── <screen>.py <- main script for each screen
└── dist/ <- compiled binaries (created on release)
project.json is the source of truth for the project. It stores screen names, script
paths, icons, descriptions, version numbers, and release configuration. It is managed
automatically by the CLI and by VINFO/Project — do not edit it by hand.
App Lifecycle#
VIStk supports two runtime models: standalone (original) and Host-based (tabbed).
Standalone mode#
Each screen is its own Python process. Switching screens replaces the current process via
os.execl. This is the original VIStk behaviour and still works for any screen where
tabbed is false.
from VIStk.Objects import Root
root = Root()
root.screenTitle("MyScreen")
root.WindowGeometry.setGeometry(width=800, height=600, align="center")
# ... build your UI ...
if __name__ == "__main__":
while root.Active:
root.update()
Host mode#
The Host is a persistent process that owns a hidden Tk() root. All visible windows
are DetachedWindow instances (Toplevels) that the Host manages. Screens marked
tabbed: true open as tabs inside the active window; standalone screens open as new
DetachedWindow instances.
On the first call to host.update(), the Host automatically opens the project’s default
screen (set in project.json). This deferred open ensures that Host.py has time to
configure shared menus before any window is created.
from VIStk.Objects import Host
from modules.menu import shared_menu_structure
host = Host()
host.default_menu_setup = lambda m: m.build_shared_menu(shared_menu_structure())
while host.Active:
host.tick_fps()
host.update()
Screen navigation from anywhere in the app:
# Routes through Host if running, otherwise os.execl
from VIStk.Structures._Project import Project
Project().open("WorkOrders")
Key rules:
Do not call
root.mainloop()— this bypasses thewhileloop and prevents process switching.Do not call
root.destroy()to quit — callhost.quit_host()instead.Screen scripts must include
if __name__ == "__main__":around the startup code so they can be imported as modules by the Host without executing the top-level loop.Use
Project.open()instead ofProject.load()when a Host may be running — it routes correctly in both modes.
Screen Module Pattern#
Every tabbed screen must follow this pattern. Violating it will crash the Host on import:
# Module-level: only imports and pure data (no widget creation, no Tk calls)
def loop(): pass # called every frame (standalone only)
def configure_menu(menubar): pass # contribute to HostMenu when tab is active
def on_focused(): pass # tab gained focus
def on_unfocused(): pass # tab lost focus
def setup(parent):
# ALL widget creation goes here
from tkinter import ttk
frame = ttk.Frame(parent)
frame.place(relx=0, rely=0, relwidth=1, relheight=1)
if __name__ == "__main__":
from Screens.root import root, frame
setup(frame)
while root.Active:
root.update()
The if __name__ == "__main__": guard is required for tabbed screens. When the Host
imports the module to call setup(), the guard prevents the standalone loop from
running.
Hook lookup priority: If modules/<screen>/m_<screen>.py exists, the Host checks
it first for on_focused, on_unfocused, and configure_menu. The screen script
is used as a fallback.
The f_element Pattern#
Elements are modular UI sections within a screen. Each element has a build(parent)
function that receives the parent frame and places widgets into it.
# Screens/MyScreen/f_header.py
widget_ref = None # module global -- set by build(), read by loop()
def build(parent):
global f_elem, widget_ref
f_elem = ttk.Frame(parent)
f_elem.place(parent.Layout.cell(row, col)) # 0-indexed
widget_ref = ttk.Label(f_elem, text="Header")
widget_ref.pack()
The #% Marker System#
VIStk templates use #% comment markers as structural anchors. The stitch command
uses these to locate and rewrite import blocks in screen scripts.
Warning
Do not delete or rename #% comment lines. They are not standard comments — they are
structural anchors that VIStk searches for by text pattern.
The two critical blocks:
#%Screen Elements
from Screens.MyScreen.f_header import f_header
f_header.build(parent)
from Screens.MyScreen.f_body import f_body
f_body.build(parent)
#%Screen Grid
#%Screen Modules
from modules import MyScreen
#%Handle Arguments
stitch replaces everything between #%Screen Elements and #%Screen Grid with
fresh imports and build() calls for each Screens/<screen>/f_*.py file. Everything
between #%Screen Modules and #%Handle Arguments is replaced with a single package
import: from modules import <ScreenName>.
stitch also generates modules/<screen>/__init__.py with three sections:
#%Modules (Auto-generated)
from . import m_header
from . import m_body
#%Screen Variables
# Hand-edited shared state (e.g. current_wo = None)
#%Exports
# Hand-edited public API (e.g. from .m_header import get_wo_num)
The #%Modules section is regenerated on every stitch. #%Screen Variables and
#%Exports are preserved across stitches.
project.json#
project.json is managed by the CLI and VINFO. Its structure:
{
"MyApp": {
"Screens": {
"Home": {
"script": "Home.py",
"release": true,
"icon": "home",
"desc": "Main dashboard",
"tabbed": true,
"single_instance": false,
"version": "1.0.0",
"current": null
}
},
"defaults": {
"icon": "app",
"default_screen": "Home"
},
"metadata": {
"company": "My Company",
"copyright": "My Company",
"version": "0.1.0"
},
"release_info": {
"location": "./dist/",
"hidden_imports": []
},
"host": {
"script": ".VIS/Host.py"
}
}
}
Per-screen fields:
Field |
Type |
Description |
|---|---|---|
|
string |
Python script filename in the project root |
|
bool |
Whether this screen is compiled to its own binary |
|
string/null |
Icon name (without extension) from |
|
string |
Screen description |
|
bool |
|
|
bool |
Prevent duplicate tabs when |
|
string |
Screen-specific version ( |
|
string/null |
Free-form current-state string |