Console
ui.Log collects entries (thread-safe, capped at limit, repeated lines folded) and can capture print and tracebacks. ui.console shows them like a browser’s dev tools console: a filter box, one toggle per level with its count, copy and clear. Levels are debug, info, event, stdout, stderr, warning and error (ui.LOG_LEVELS). With a ui.LogSelection the lines can be selected (click, Shift+click, Ctrl+click) and a selected line shows its detail (a traceback); on_context opens your menu on right click, and sources adds a dropdown to show the lines of one source.
ui.console · Category: Code and tools · Full reference →


log = ui.Log()log.write("info", "Loaded ui/main.pzform")log.write("event", "button1.click")log.write("stdout", "Saving 3 files…")log.write("warning", "settings.json not found, using defaults")log.write("error", "ValueError: invalid literal for int()")pz.container(ui.console(log.entries, on_clear=on_clear, on_copy=on_copy, on_query=on_query), width=600, height=220)When to use
Section titled “When to use”- For a debug panel in your app, or to show what a background job is doing.
import pyzed as pzfrom pyzed import ui
class Debug(pz.App): def __init__(self): self.log = ui.Log(on_write=lambda: pz.post("log")) # redraw when a line arrives self.log.capture() # also collect print() and stderr self.query, self.hidden = "", set()
def view(self): return ui.console(self.log.entries, query=self.query, on_query=lambda q: setattr(self, "query", q), hidden=self.hidden, on_toggle_level=lambda lvl: self.hidden.symmetric_difference_update({lvl}), on_clear=self.log.clear, on_copy=lambda: pz.Command.copy(self.log.text()))Variants
Section titled “Variants”Options
Section titled “Options”

Code
log = ui.Log()for level in ui.LOG_LEVELS: log.write(level, f"{level} message")pz.container(ui.console(log.entries, on_clear=on_clear), width=520, height=220)

hidden={"info", "event"}Code
log = ui.Log()for level in ui.LOG_LEVELS: log.write(level, f"{level} message")pz.container(ui.console(log.entries, hidden={"info", "event"}, on_toggle_level=on_toggle), width=520, height=200)

query="error"Code
log = ui.Log()log.write("info", "started")log.write("error", "ValueError: bad input")pz.container(ui.console(log.entries, query="error", on_query=on_query), width=520, height=140)Events
Section titled “Events”Console has 8 events of its own, listed below. Like every widget it also has the 18 common events (click, double_click, right_click, mouse_enter, drag, key_down, resize…), attached with .on(...): see Events and Events of every component.
| Event | Of | Handler gets | When |
|---|---|---|---|
on_query |
ui.console |
(text) |
Receives each edit of the filter. |
on_toggle_level |
ui.console |
(level) |
Called with the level of the clicked toggle. |
on_clear |
ui.console |
() |
Adds a Clear button. |
on_copy |
ui.console |
() |
Adds a Copy button. |
on_context |
ui.console |
(line) |
Called with the line right-clicked (None under the lines): open a menu there (copy, go to source, clear…). |
on_open |
ui.console |
(line) |
Called with the line double-clicked. |
on_source |
ui.console |
(source) |
Receives the source picked in the dropdown (None for all). |
on_write |
ui.Log |
() |
Called after every write (from any thread). |
Handler gets is what Pyzed passes to your callable: () means nothing. Any handler can also be a message (a value that is not callable), which goes to update().
Handling the events
Section titled “Handling the events”The same events written three ways. Imperative: methods of the app change self. Declarative: the handlers produce messages and one update() decides. Functional: a pure function draws an immutable state and asks for changes with set_state(...). See Imperative, declarative and functional.
class Debug(pz.App): def __init__(self): self.log = ui.Log() self.log.write("info", "Started") self.log.write("error", "ValueError: bad input") self.log.on_write = lambda: pz.post("log") # a line arrived (from any thread) self.query, self.hidden = "", set()
def update(self, message): pass # "log" just redraws
def view(self): return ui.console(self.log.entries, query=self.query, hidden=self.hidden, on_query=lambda q: setattr(self, "query", q), # the filter on_toggle_level=lambda lvl: self.hidden.symmetric_difference_update({lvl}), on_clear=self.log.clear, # adds Clear on_copy=lambda: pz.Command.copy(self.log.text()), # adds Copy on_open=lambda entry: print("open", entry.text)) # double clickfrom dataclasses import dataclass
@dataclassclass Filter: query: str
@dataclassclass ToggleLevel: level: str
class Debug(pz.App): log = ui.Log() query, hidden = "", frozenset()
def update(self, message): match message: case Filter(query): self.query = query case ToggleLevel(level): self.hidden = self.hidden ^ {level} case "clear": self.log.clear()
def view(self): return ui.console(self.log.entries, query=self.query, hidden=self.hidden, on_query=Filter, on_toggle_level=ToggleLevel, on_clear="clear")from dataclasses import dataclass
LOG = ui.Log()LOG.write("info", "Started")
@dataclass(frozen=True)class State: query: str = "" hidden: frozenset = frozenset()
def view(state, set_state): return ui.console(LOG.entries, query=state.query, hidden=state.hidden, on_query=lambda q: set_state(query=q), on_toggle_level=lambda lvl: set_state(hidden=state.hidden ^ {lvl}))Properties and API
Section titled “Properties and API”ui.console
Section titled “ui.console”console( entries, *, query='', on_query=None, hidden=(), on_toggle_level=None, on_clear=None, on_copy=None, actions=(), selection=None, on_context=None, on_open=None, sources=(), source=None, on_source=None, show_source=False, placeholder=None, empty=None, font_size=12.5, id=None, theme=None,)A log view like a browser’s dev tools console: a filter box, one toggle per level with its count, copy and clear, and the entries in a monospace list that keeps scrolling to the newest one.
| Property | Default | Description |
|---|---|---|
entries |
required | ui.LogEntry items (log.entries). |
query |
"" |
Filter text. |
hidden |
() |
Levels not shown. |
actions |
() |
Extra widgets at the end of the toolbar. |
selection |
None |
A ui.LogSelection: the lines can be selected with the mouse, and a selected line with detail shows it below. |
sources |
() |
(value, label) pairs: adds a dropdown that shows the lines of one source. |
source |
None |
The source shown (None for all). |
show_source |
False |
Tags each line with its source. |
placeholder |
None |
Text of the empty filter box. |
empty |
None |
What shows while there are no lines. |
font_size |
12.5 |
Size of the entries. |
id |
None |
Scrollable id. |
theme |
None |
Theme to use instead of the current one (pyzed.theme.current()). |
Events: on_query, on_toggle_level, on_clear, on_copy, on_context, on_open, on_source (events).
ui.Log
Section titled “ui.Log”class LogThe entries of a ui.console: thread-safe, capped at limit, with repeated lines folded into one. write(level, text) adds a line; capture() (or with log:) copies print output and tracebacks into it; pipe(stream) reads a subprocess’s output in a background thread; text() returns everything for copying (text(ids) only those lines). write also takes the line’s source, detail (a traceback) and origin ((path, line)) and returns the entry; find(id) looks one up.
| Property | Default | Description |
|---|---|---|
limit |
5000 |
Most entries kept. |
Events: on_write (events).
ui.LogEntry
Section titled “ui.LogEntry”@dataclassclass LogEntry(level, text, time=time.time(), count=1, source='', detail='', origin=None, id=0)One line of a ui.Log: level, text, time, count (identical lines in a row), source, detail, origin, its number id, clock (HH:MM:SS) and line() (the entry as plain text).
| Property | Default | Description |
|---|---|---|
level |
required | Level name. |
text |
required | The line. |
time |
time() |
When it was written (time.time()). |
count |
1 |
Identical lines in a row. |
source |
"" |
What it comes from ("project", "agent"…); ui.console can show and filter it. |
detail |
"" |
More text shown under the line while it is selected (a traceback). |
origin |
None |
(path, line) of the code it is about. |
id |
0 |
Its number in the log (what a ui.LogSelection keeps). |
ui.LogSelection
Section titled “ui.LogSelection”class LogSelectionThe selected lines of a ui.console, like in a file list: a click selects one line, Shift+click a range from the last one clicked and Ctrl+click (Cmd on macOS) adds or removes one. Keep one in your app and pass it to ui.console(selection=...); select(ids) selects exactly those (Ctrl+A), clear() none, keep(entries) forgets lines that are gone, and ids are the selected entries’ ids for log.text(ids).
ui.LOG_LEVELS
Section titled “ui.LOG_LEVELS”The levels ui.console colors, in the order of its filter buttons: debug, info, event, stdout, stderr, warning, error.