Objects#
Objects are the core building blocks of a VIStk application. Import from VIStk.Objects.
Root#
Root(Tk, Window) — The application’s main window. Wraps Tk with VIStk attributes.
See also Host — not a subclass of Root, but a standalone coordinator that owns a hidden
Tk root and manages visible windows for it. It adds tabbed screen management, multi-window
lifecycle, and unified navigation. There is no system tray: the Host lives exactly as long as
its windows.
from VIStk.Objects import Root
root = Root()
Attributes:
Attribute |
Type |
Description |
|---|---|---|
|
|
Set to |
|
|
Geometry helper attached to this window |
|
|
Layout manager for this window |
|
|
The loaded VIS project |
Methods:
Method |
Description |
|---|---|
|
Sets the window title and marks the active screen in |
|
Cleanly destroys all child widgets and sets |
|
Registers a function to call after the main loop exits — use for screen redirects. |
|
Executes the registered exit action. |
|
Maximizes the window (zoomed, not absolute fullscreen). |
|
Restores the window to normal size. |
|
Sets the window icon from |
Typical pattern:
root = Root()
root.screenTitle("Home")
root.WindowGeometry.setGeometry(width=1024, height=768, align="center")
root.fullscreen()
# build UI here
while root.Active:
root.update()
Host#
Host — The application host. Owns a hidden Tk() root window and manages one or more
visible DetachedWindow instances. The Host is not a subclass of Root — it is a
standalone class that coordinates window lifecycle, screen loading, and menu configuration.
The hidden root is never shown to the user. All visible UI lives inside DetachedWindow
instances, each of which contains its own HostMenu, SplitView (with TabManager
panes), and InfoRow.
The Host is not a background service and there is no system tray — the 0.5.3 always-Host
refactor removed it. The Host starts with the first window and tears itself down once the last
DetachedWindow closes; a localhost socket provides single-instance forwarding while a Host
is alive.
On the first call to update(), the Host automatically opens the project’s default screen
(from project.json). This deferred open ensures that Host.py has time to configure
default_menu_setup 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()
Attributes:
Attribute |
Type |
Description |
|---|---|---|
|
|
|
|
|
The hidden Tk root. All |
|
|
The loaded VIS project. |
|
|
All live DetachedWindow instances. |
|
|
All active TabManager panes across all windows. |
|
|
The most recently focused pane. |
|
|
Called on every new |
|
|
Current frames per second, updated by |
Methods:
Method |
Description |
|---|---|
|
Unified navigation. Tabbed screens open as tabs in the active window; standalone
screens open as chromeless |
|
Pumps one iteration: opens the startup screen(s) on the first tick, drains the IPC
queue, calls |
|
Call once per loop iteration to maintain |
|
Closes all |
|
Adds a tab named |
|
Registers a live widget for the menubar’s right edge. See Menubar accessories. |
|
Removes the Host from the Windows startup registry ( |
Settings panels#
register_settings_panel(name, setup_fn) lets an application contribute its own tab to the
framework’s Settings window alongside the built-in General tab. name is the notebook tab
label; registering the same name twice replaces the earlier panel. Register before entering the
update loop — typically in .VIS/Host.py — because the panel list is read each time the
Settings surface is built.
setup_fn receives the tab body (a ttk.Frame) and builds into it, mirroring a screen’s
setup(parent). Two arities are accepted:
setup_fn(frame)— self-managed. The panel builds its own widgets and reads/writeshost.Project.Settingsitself.setup_fn(frame, ui)— integrated.uiis the settings controller; fields built withui.add_check(parent, row, label, key),ui.add_int(parent, row, label, key, hint=""), orui.add_combo(parent, row, label, key, values, editable=False)are registered in the window’s read-back registry and ride its own Save and Restore Defaults buttons exactly like the General tab — no per-panel Save button, no apply-on-change surprise.
def my_panel(parent, ui):
ui.add_check(parent, 0, "Enable feature X", "my.feature.enabled")
ui.add_int(parent, 1, "Retry limit", "my.feature.retries", "attempts")
host.register_settings_panel("My Plugin", my_panel)
A panel that raises during setup_fn does not take the Settings window down — the traceback
is printed and the tab shows an inline error message, so a broken panel is visible rather than
blank.
Singleton#
Host.__init__ sets VIStk.Objects._Host._HOST_INSTANCE = self. Project.open() checks
this reference to route navigation. Only one Host should exist per process.
TabManager#
TabManager(Frame) — Manages the tabbed screen area inside the Host window. Created
automatically by Host.__init__ and exposed as host.TabManager. It owns a TabBar strip
along the top edge and a content area where each tab’s Frame lives.
Screen modules are imported by the Host and passed to open_tab. TabManager calls
setup(frame), on_focused(), and on_unfocused() at the appropriate times.
Hook lookup: Hooks (on_focused, on_unfocused, on_quit, configure_menu,
has_unsaved) are read off the tab’s own per-tab namespace — the same module that holds the
rest of the screen’s API. There is no separate hooks module to consult.
Attributes:
Attribute |
Type |
Description |
|---|---|---|
|
|
The tab strip widget |
|
|
Name of the currently focused tab |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Methods:
Method |
Returns |
Description |
|---|---|---|
|
|
Open a new tab. If already open, focuses it instead. |
|
|
Close the named tab, running |
|
|
Focus the named tab. |
|
|
Whether a tab with this name is open. |
|
|
Close and reopen at the same position, re-running |
|
|
Set the info string for the tab. Accepts a plain string or |
TabManager is not normally used directly — host.open() handles all navigation.
DetachedWindow#
DetachedWindow — A floating Toplevel window containing its own SplitView (which wraps
one or more TabManager panes). Every visible application window is a DetachedWindow;
further ones are created by the Host when a standalone screen opens or when a tab is popped out
via the right-click context menu or drag-to-detach. Tracked in host.detached_windows.
Popping a tab out re-runs setup(parent) in the new window, so screen UI state is reset.
Attributes:
dw._split_view—SplitViewmanaging the window’s content areadw.tab_manager— property returningdw._split_view.focused_panedw.HostMenu— menu bar (shared cascades cloned from Host)dw.InfoRow— status bar
Behaviour:
Right-clicking a tab and choosing Open in new window sends the tab back to the main Host.
Dragging a tab and releasing it outside all bars creates a new
DetachedWindow.Dragging a tab from one bar into another registered
TabBarmerges it there.Dragging a tab into a split zone (edge or center) of any pane in any window creates a split or adds the tab to the target pane — cross-window drag-to-split is fully supported.
Force refresh re-imports the screen module and re-runs
setup(parent)in-place.Closing the window runs
on_unfocusedon all tabs across all panes and destroys them.When the Host shuts down, all
DetachedWindowinstances are closed first.
Window icons (0.6.1)#
Windows keeps two icon slots per window — ICON_SMALL (title bar and alt-tab) and ICON_BIG
(taskbar) — but Tk’s iconphoto fills both with a single image, so before 0.6.1 the two were
always identical. DetachedWindow now drives them independently.
Configuration:
defaults.iconinproject.json(read asproject.d_icon) — the taskbar icon, and the only icon on non-Windows platforms.defaults.window_icon(read asproject.d_window_icon) — the title-bar icon. This is the only project-level way to set it. Leaving it unset preserves the pre-0.6.1 behaviour of one shared image.A screen’s
iconandwindow_iconentries override the project defaults, but only for a chromeless (standalone, ``tabbed: false``) window that the screen owns outright.
All four name a file in the project’s Icons/ folder without its extension; any PIL-readable
format works.
Behaviour:
The taskbar slot is filled via
iconphotoexactly as before: a chromeless screen’siconif it has one, elseproject.d_icon.The title-bar slot is filled from
project.d_window_icon, overridden by a chromeless screen’swindow_icon.A tabbed screen can never repaint its window’s title-bar icon. Its window is chromed and shared with whatever other tabs are open, so the internal icon loader is never handed a screen name for it — the constraint is structural, not a runtime check. Set
defaults.window_iconinstead.The title-bar override is Windows-only: it resolves the window’s decorated wrapper HWND and sends
WM_SETICON. On every other platform it is a no-op and the title bar keeps sharing theiconphotoimage.The underlying HICON handles are cached process-wide, because
WM_SETICONdoes not copy the icon — the handle must outlive the call. Each distinct icon name is therefore built at most once per process.
DetachedWindow is created internally — you do not instantiate it directly.
SubRoot#
SubRoot(Toplevel, Window) — A popup or secondary window. Wraps Toplevel with VIStk
attributes.
from VIStk.Objects import SubRoot
popup = SubRoot()
Attributes:
Attribute |
Type |
Description |
|---|---|---|
|
|
Geometry helper for this window |
|
|
Layout manager for this window |
|
|
|
Methods:
Method |
Description |
|---|---|
|
Makes the window modal — blocks input to the parent until this window is closed. Cannot be undone. |
QuestionWindow and WarningWindow are both subclasses of SubRoot.
Window#
Window is a mixin class inherited by both Root and SubRoot. It provides fullscreen
control and icon loading. You do not instantiate it directly.
Methods:
Method |
Description |
|---|---|
|
Maximizes the window. |
|
Restores window size. |
|
Loads |
setIcon uses iconphoto, which fills the title-bar and taskbar slots with the same image.
The independent title-bar icon added in 0.6.1 applies to Host-managed windows only — see
Window icons (0.6.1).
WindowGeometry#
WindowGeometry handles window sizing and positioning. It is automatically attached to Root
and SubRoot as self.WindowGeometry.
getGeometry#
getGeometry(respect_size=False)
Reads the current geometry from the window and stores it internally. If respect_size=True,
uses the actual rendered size (winfo_width/height) instead of the geometry string.
setGeometry#
setGeometry(width, height, x, y, align, size_style, window_ref)
Positions and sizes the window.
Parameter |
Type |
Description |
|---|---|---|
|
|
Width in pixels (or percentage if |
|
|
Height in pixels (or percentage if |
|
|
X position in pixels. Ignored if |
|
|
Y position in pixels. Ignored if |
|
|
Named alignment: |
|
|
|
|
|
Reference window for |
Examples:
# Center an 800x600 window on screen
root.WindowGeometry.setGeometry(width=800, height=600, align="center")
# Center a popup on its parent window — flicker-free (0.5.0+)
from VIStk.Objects import WindowGeometry
WindowGeometry(popup)
# ... build all child widgets first ...
popup.WindowGeometry.center_on(root)
Note
The pre-0.5.0 multi-call pattern (popup.update() →
getGeometry(True) → setGeometry(align="center", ...)) made the
popup briefly visible at the OS default position before jumping to
the centred position. center_on() performs the same math
inside a withdraw() / deiconify() wrap and uses
update_idletasks() (layout-only) instead of update(), so the
window is never drawn at its default position.
Do not call center_on on the root Tk() window — withdraw()
on the main application window hides it entirely.
center_on (0.5.0)#
center_on(window_ref)
Centre this window on window_ref without a visible flicker. Drop-in
replacement for the multi-call update + getGeometry + setGeometry
pattern. Call after all child widgets are built so winfo_width /
winfo_height reflect the final size.
popup = Toplevel(root)
Button(popup, text="OK", command=popup.destroy).pack(padx=50, pady=50)
from VIStk.Objects import WindowGeometry
WindowGeometry(popup)
popup.WindowGeometry.center_on(root)
stripGeometry#
stripGeometry(objects)
Returns raw integer values from the current geometry string.
x, y = root.WindowGeometry.stripGeometry(("x", "y"))
w, h, x, y = root.WindowGeometry.stripGeometry("all")
Layout#
Layout is a proportional grid system for placing frames inside a window or frame using
place(). Rows and columns are defined as fractions that sum to 1.0.
from VIStk.Objects import Layout
layout = Layout(frame)
layout.rowSize([0.1, 0.8, 0.1]) # 10% header, 80% body, 10% footer
layout.colSize([0.25, 0.75]) # 25% sidebar, 75% content
rowSize#
rowSize(rows, minsize=None, maxsize=None)
Sets row proportions. Each value is a float from 0.0 to 1.0; they must sum to exactly 1.0.
minsize and maxsize are optional lists of pixel constraints stored as row_min /
row_max.
layout.rowSize([0.5, 0.5])
layout.rowSize([0.1, 0.7, 0.2])
layout.rowSize([0.1, 0.8, 0.1], minsize=[30, 100, 30])
colSize#
colSize(columns, minsize=None, maxsize=None)
Sets column proportions. Same rules as rowSize. Optional minsize / maxsize stored as
col_min / col_max.
layout.colSize([1.0])
layout.colSize([0.3, 0.7])
layout.colSize([0.25, 0.75], minsize=[150, None])
cell#
cell(row, column, rowspan=None, columnspan=None, padding=0)
Returns a dict of place() kwargs for the given cell. Pass directly to
widget.place(**...). Rows and columns are 0-indexed. The optional padding argument adds
inward pixel padding on all sides.
header = Frame(root)
header.place(**root.Layout.cell(0, 0))
# Span multiple cells
panel = Frame(root)
panel.place(**root.Layout.cell(1, 0, columnspan=2))
# 8px padding inside the cell
card = Frame(root)
card.place(**root.Layout.cell(1, 1, padding=8))
apply#
apply(widget, row, col, rowspan=None, columnspan=None, padding=0)
Places widget in the given cell using absolute pixel coordinates and automatically re-places it
whenever the parent frame is resized. Unlike cell(), apply() enforces any minsize /
maxsize constraints set via rowSize() / colSize().
layout.rowSize([0.1, 0.8, 0.1], minsize=[30, 100, 30])
layout.colSize([0.3, 0.7])
layout.apply(header_frame, row=0, col=0)
layout.apply(body_frame, row=1, col=0, rowspan=1)
Layout is available on Root as root.Layout and on SubRoot as popup.Layout. It
is also the basis for LayoutFrame.
LayoutFrame#
LayoutFrame(Frame) — A standard Tkinter Frame with a Layout object pre-attached as
self.Layout. Use it when you need to subdivide a frame using proportional placement.
from VIStk.Widgets import LayoutFrame
main_area = LayoutFrame(root)
main_area.place(**root.Layout.cell(1, 0))
main_area.Layout.colSize([0.4, 0.6])
main_area.Layout.rowSize([1.0])
left_panel = Frame(main_area)
left_panel.place(**main_area.Layout.cell(0, 0))
right_panel = Frame(main_area)
right_panel.place(**main_area.Layout.cell(0, 1))
VIMG#
VIMG loads and optionally auto-resizes images for Tkinter widgets using PIL. Images are loaded
from the project’s Images/ folder by default.
from VIStk.Objects import VIMG
img = VIMG(label_widget, "logo.png")
label_widget.configure(image=img.holder.image)
Constructor:
Parameter |
Type |
Description |
|---|---|---|
|
|
The widget that will display the image |
|
|
Filename in |
|
|
If |
|
|
Fixed |
|
|
If provided, the image resizes to fit this widget whenever it is resized. |
Auto-resize example:
# Image fills a label and resizes with the window
img_label = Label(root)
img_label.place(**root.Layout.cell(0, 0))
img = VIMG(img_label, "background", fill=img_label)
ArgHandler#
ArgHandler parses command-line arguments passed to a screen script. Each flag is registered
with a keyword and a callback function. Flags are passed with -- on the command line.
from VIStk.Objects import ArgHandler
import sys
handler = ArgHandler()
handler.newFlag("load", lambda args: load_record(args[0]))
handler.newFlag("mode", lambda args: set_mode(args[0]))
handler.handle(sys.argv)
Command line usage:
python myscreen.py --load 1042 --mode readonly
Methods:
Method |
Description |
|---|---|
|
Registers a flag. Accepts |
|
Parses |
The ArgHandler on Root.Project is used internally by the CLI for screen loading with
arguments.
open_active_screen_docs (0.5.0)#
Reads the active screen from the in-process Host singleton, runs
Project.resolve_docs_url() (active screen docs -> project
defaults.docs -> None), and hands the URL to
webbrowser.open(). Returns True on dispatch, False if no
URL is configured.
Intended to be wired into HostMenu.add_project_command for a
one-line top-level Help button:
from VIStk.Widgets import HostMenu
from VIStk.Objects import open_active_screen_docs
host_menu.add_project_command("Help", open_active_screen_docs)
The URL is taken verbatim from project.json; configure entries via
VIS docs set ... (see CLI Reference).