Skip to content

PySide6 Renderer

The PySideRenderer maps template tags to PySide6 (Qt for Python) widgets.

Setup

pip install collagraph[pyside]
import collagraph as cg
from PySide6 import QtWidgets

app = QtWidgets.QApplication()
gui = cg.Collagraph(renderer=cg.PySideRenderer())
gui.render(MyComponent, app)
app.exec()

Available Elements

Template tags map to Qt widget classes. There are common short names available:

Tag Qt Class
<window> QMainWindow
<widget> QWidget
<button> QPushButton
<label> QLabel
<lineedit> QLineEdit
<textedit> QTextEdit
<checkbox> QCheckBox
<radiobutton> QRadioButton
<combobox> QComboBox
<slider> QSlider
<spinbox> QSpinBox
<progressbar> QProgressBar
<groupbox> QGroupBox
<tabwidget> QTabWidget
<scrollarea> QScrollArea
<dock> QDockWidget
<toolbar> QToolBar
<menubar> QMenuBar
<menu> QMenu
<action> QAction
<statusbar> QStatusBar
<treeview> QTreeView
<treewidget> QTreeWidget
<treewidgetitem> QTreeWidgetItem
<dialogbuttonbox> QDialogButtonBox

Name Lookup from Qt Modules

Beyond the short names above, you can use any class from QtWidgets, QtGui, QtCore, or QtCore.Qt directly by name. Tag names in templates must be lower-case (the lookup is case-insensitive):

<qlabel :text="content" />
<qcalendarwidget @selection_changed="on_date" />

You can also use dot notation for nested types:

<widget :layout="{'type': 'QBoxLayout.Direction.TopToBottom'}" />

This means you rarely need to call register_element() -- most Qt classes are available out of the box.

Attributes

Attributes map to setter methods on the Qt widget. The attribute name is converted from kebab-case to the appropriate Qt method:

<label text="Hello" />        <!-- calls setText("Hello") -->
<window title="My App" />     <!-- calls setWindowTitle("My App") -->
<button enabled="false" />    <!-- calls setEnabled(False) -->

Multi-word attribute names may be written with either dashes or underscores; both map to the same camel-cased setter:

<widget maximum-size="(200, 100)" />   <!-- calls setMaximumSize((200, 100)) -->
<widget maximum_size="(200, 100)" />   <!-- same setter -->
<widget window_title="My App" />       <!-- calls setWindowTitle("My App") -->

In Python views, attribute names are keyword arguments, so only the underscore form works (maximum_size=(200, 100)).

Dynamic attributes use Python expressions:

<label :text="f'Count: {count}'" />
<slider :value="self.state['position']" />

Text Content

Widgets that display text can take their text as element content, including {{ }} interpolation:

<label>Count: {{ count }}</label>
<button @clicked="bump">bump</button>

This is supported for QLabel and all QAbstractButton subclasses (<button>, <checkbox>, <radiobutton>, ...). The joined content of all text children is displayed through the widget's setText() method, so text can be mixed with directives:

<label>
  Hello<template v-if="name">, {{ name }}</template>!
</label>

For other widgets, set text via the text attribute instead:

<lineedit :text="initial_value" />

Layouts

Child widgets are laid out using layout elements:

<widget layout="hbox">      <!-- QHBoxLayout -->
  <button text="Left" />
  <button text="Right" />
</widget>

<widget layout="vbox">      <!-- QVBoxLayout -->
  <label text="Top" />
  <label text="Bottom" />
</widget>

<widget layout="grid">      <!-- QGridLayout -->
  <label text="A" row="0" column="0" />
  <label text="B" row="0" column="1" />
</widget>

<widget layout="form">      <!-- QFormLayout -->
  <label text="Name:" />
  <lineedit />
</widget>

Events

Events map to Qt signals. Use the signal name in snake_case:

<button @clicked="handle_click" />
<lineedit @text_edited="handle_text" />
<slider @value_changed="handle_value" />
<combobox @current_index_changed="handle_index" />

Tree Views

Tree views and tree widgets work well with reactive state:

  • Keyed reorders preserve UI state. When items in a v-for with :key are reordered (reversed, shuffled, sorted), the selection and expansion state of tree items moves along with them. See examples/pyside/keyed_tree_demo.cgx.
  • Drag and drop is supported. Drops can be turned into state mutations, with selection and expansion state preserved across the resulting re-render. See examples/pyside/tree_dnd_example.cgx for a complete example with a custom drop-aware QTreeWidget.

Registering Custom Elements

You can register additional Qt classes:

from PySide6 import QtWidgets
from collagraph.renderers.pyside_renderer import PySideRenderer

PySideRenderer.register_element("calendar", QtWidgets.QCalendarWidget)

Then use in templates:

<calendar @selection_changed="on_date" />