Skip to content

Console & GUI faces

This page explains the two shapes a d/Python program can take: a console program that runs top to bottom, and a window program that opens a window and responds to events. By the end you will know which shape fits what you are building and how to write each one. d/OS calls these the program's two faces. A face is how the host launches a program, not a different dialect: the same types, functions, classes, imports, optimizer and compiled bytecode serve both.

Face Who drives Input Output Lifetime
console the module's top-level statements input(), program arguments after --, host services print(), the exit status start → run → halt
window d/OS, which delivers each event key and pointer events in a standard form drawing requests, retained widgets, the window title WINDOW_OPEN → draw/event turns → WINDOW_CLOSE

A console program

Top-level statements run in order. print writes to the shared console; input pauses the program until a line arrives; reaching the end of the file stops the program with status 0.

"""A console application using Python's ordinary entry-point guard."""

def main():
    names = ["Ada", "Grace", "Guido"]
    for index, name in enumerate(names, start=1):
        print(f"{index}: Hello, {name}!")

if __name__ == "__main__":
    main()

__name__ is "__main__" when the module is the one you launched, and also when a module imports __main__ explicitly; __doc__, __package__ and __spec__ exist when you refer to them. On the command line, everything after -- reaches the program through services such as dos.service("READ_ARGS"), described in Console Services.

A window program

A module becomes a window program by decorating functions with the shared handler names: WINDOW_OPEN, WINDOW_DRAW, WINDOW_RESIZE, WINDOW_CLOSE, ON_KEY, ON_POINTER, plus the names of the newer lifecycle states (lifecycle v2). If you leave out a required handler, the compiler fills in an empty one for you. The module's top-level code runs once per launch, before the open handler.

import dos

@dos.on("WINDOW_OPEN")
def opened():
    dos.service("SET_TITLE", "Faces")

@dos.on("WINDOW_DRAW")
def draw():
    dos.intent("GFX", "BEGIN_FRAME")
    dos.intent("GFX", "FILL_RECT", 0, 0, 320, 200, 6371)
    dos.intent("GFX", "DRAW_TEXT", 20, 30, "A window face", 1, -1, 6371)
    dos.intent("GFX", "PRESENT")

@dos.on("WINDOW_RESIZE")
def resized(width, height):
    dos.service("INVALIDATE")

@dos.on("WINDOW_CLOSE")
def closed():
    pass

The resize handler receives a width and a height; a key handler receives a key code; a pointer handler receives x, y and a button mask (a number whose bits say which buttons are held). Declaring a resize handler is what makes the window resizable. d/OS owns the window's state and decides when input is delivered; your handler runs inside a short, bounded turn and returns.

Both faces

dos.app(console=True) lets a window module also be launched as a console program. A compiled program that offers both faces runs as a console program by default; dpython run --native chooses its window face instead, whether you are running source or a .dbc file.

Which one, when

Shape A good fit
interactive console lessons, small tools, calculators, text adventures, anything that has a conversation with the user
console script reports, conversions, tests, the conformance test programs themselves
window program editors, dashboards, games, long-lived applications, interfaces built from retained widgets

The console is not the beginner mode, and the window is not a second language. Ask and Answer continues the console story; Windows & Events continues the window one.