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/
│ ├── defaults.py <- shared imports for all screens and elements
│ └── <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)
from Screens.defaults import *
def loop(): pass # called every tick
def configure_menu(menubar): pass # contribute to HostMenu when tab is active
def on_activate(): pass # tab gained focus
def on_deactivate(): pass # tab lost focus
def setup(parent):
# ALL widget creation goes here
pane = LayoutFrame(parent)
pane.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 critical blocks:
#%Screen Elements
import Screens.MyScreen.f_header
import Screens.MyScreen.f_body
#%Screen Modules
import modules.MyScreen.m_header
import modules.MyScreen.m_body
Inside setup():
#%Build Screen Elements
Screens.MyScreen.f_header.build(pane)
Screens.MyScreen.f_body.build(pane)
Inside loop():
#%Predefined Loop Functions
if bool(getattr(modules.MyScreen.m_header, "_m_header", False)): modules.MyScreen.m_header._m_header()
if bool(getattr(modules.MyScreen.m_body, "_m_body", False)): modules.MyScreen.m_body._m_body()
#%User Defined Loop Functions
pass
stitch replaces the content under #%Screen Elements with fully-qualified imports
for each Screens/<screen>/f_*.py file. #%Screen Modules is replaced with
fully-qualified imports for each modules/<screen>/m_*.py file. #%Build Screen
Elements is replaced with build(pane) calls, and #%Predefined Loop Functions is
replaced with guarded callbacks for each module.
Import conventions:
Screens.*andmodules.*are always imported as full dotted paths (import Screens.MyScreen.f_header), never withfromoras.fromimports inside functions are only used in the__main__guard (from Screens.root import root, frame).
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 |