Skip to main content

Yet another pythonic UI library using PyQt5

Project description

Qutie

Yet another pythonic UI library for rapid prototyping using PyQt5.

Quick start

import qutie as ui

app = ui.Application()
window = ui.Widget(
    title="Example",
    icon='orange',
    width=320,
    height=240,
    layout=ui.Column(
        ui.Label("Hello world!"),
        ui.Row(
            ui.Button("Go!", clicked=lambda: ui.show_info(text="Hello world!")),
            ui.Button("Quit", clicked=app.quit)
        )
    )
)
window.show()
app.run()

Documentation

Qutie (pronounced as cutie) provides a simple and easy to use pythonic interface to PyQt5.

Install

pip install qutie

Application

A single Application object must be created before other widgets. To make use of the event system the application event loop must be executed.

import qutie as ui

# Create an application object.
app = ui.Application(name='app', version='1.0')

# Create a window.
window = ui.MainWindow()
window.resize(800, 600)
window.show()

# Run the event loop.
app.run()

Widgets

Any widget can be a top level window or part of another widget using the layout property. All properties can be assigned using the constructor.

window = ui.Widget(title="Example", width=320, height=240)

To make a top level window visible use property visible or call method show().

window.show()
window.visible = True # equivalent to show

Layouts

The simplified layout system provides a horizontal Row and a vertical Column box. Items can be added while constructing the layout or using list like methods append and insert. The consumed space of every child widget can be adjusted using the stretch attribute.

window.layout = ui.Row(
    ui.Column(
        ...
    ),
    ui.Column(
        ui.Row(...),
        ui.Row(...),
        ui.Row(...),
        stretch=(1, 0, 0)
    ),
    stretch=(2, 3)
)

Inputs

# Single line text input
text = ui.Text(value="spam")
# Numeric input
number = ui.Number(value=4, minimum=0, maximum=10, step=1.0, decimals=1)
# A multi line text area
textarea = ui.TextArea(value="Lorem ipsum et dolor.")

Events

Events provide a simplified interface to Qt's signal and slot system. Events can be emitted from any class inheriting from Object by calling method emit().

# Use any callable class attribute as event callback.
window.issue_call = lambda: print("Call to action!")
# Emit an event executing attribute `issue_call` (if callable).
window.emit('issue_call')

Events can also propagate positional and keyword arguments.

# Use any callable class attribute as event callback.
window.update_progress = lambda a, b: print(f"Progress: {a} of {b}")
# Emit an event executing attribute `update_progress` (if callable).
window.emit('update_progress', 42, 100)

Many widgets provide predefined events.

# Assigning callback functions
ui.Number(value=4, changed=on_change, editing_finished=on_edited)

Timers

Call repeating or delayed events using timers.

timer = ui.Timer(interval=1.0, timeout=lambda: print("Done!"))
timer.start()

Function single_shot exposes a convenient single shot timer.

ui.single_shot(interval=1.0, timeout=lambda: print("Done!"))

Note that timer events are only processed when running the application event loop.

Settings

Persistent settings can be stored/restored using a Settings object as context manager. It provides application wide settings as a JSON dictionary.

with ui.Settings() as settings:
    value = settings.get('key', 'default')
    settings['key'] = value

Use attribute filename to inspect the persistent JSON data.

>>> ui.Settings().filename
'/home/user/.config/app.qutie'

Menus

Menu bars and menus behave like python lists.

window = ui.MainWindow()
file_menu = window.menubar.append("&File")
quit_action = file_menu.append("&Quit")
quit_action.triggered = window.close
foo_menu = window.menubar.insert(window.menubar.index(file_menu), "&Foo")
file_menu = window.menubar.remove(file_menu)

Toolbars

Toolbars also behave like python lists, the main window toolbars property behaves like a set.

window = ui.MainWindow()
toolbar = window.toolbars.add("toolbar")
toolbar.append(quit_action)
toolbar.insert(quit_action)
window.toolbars.remove(toolbar)

Workers

The Worker class provides a convenient way to work with background threads. Use attribute target to assign the function to be executed in the background.

def calculate(worker):
    for i in range(100):
        ...

worker = ui.Worker(target=calculate)
worker.start()

Important: use only the event system to propagate information from inside the worker. Do not access widgets from within the worker function.

def calculate(worker):
    for i in range(100):
        # Emit custom events.
        worker.emit('progress', i, 100)
        worker.emit('message', "All ok...")

worker = ui.Worker(target=calculate)
# Assign custom event callbacks.
worker.progress = lambda step, max: print(f"progress: {step}/{max}")
worker.message = lambda msg: print(f"message: {msg}")
worker.start()

To control worker lifetime use method stop() and attribute stopping.

def calculate(worker):
    while not worker.stopping:
        ...

worker = ui.Worker(target=calculate)
worker.start()
...
worker.stop()

To wait for a worker to actually stop use method join().

worker.stop()
worker.join()

Example

A simple dialog with progress bar running a calculation in the background.

import random
import time

import qutie as ui

class Dialog(ui.Dialog):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Create worker
        self.worker = ui.Worker(target=self.calculate)
        self.worker.finished = self.close
        self.worker.update_progress = self.update_progress
        # Create layout
        self.progress_bar = ui.ProgressBar()
        self.layout = self.progress_bar

    def run(self):
        # Start, stop and join worker
        self.worker.start()
        super().run()
        self.worker.stop()
        self.worker.join()

    def update_progress(self, value, maximum):
        self.progress_bar.maximum = maximum
        self.progress_bar.value = value

    def calculate(self, worker):
        n = 32
        for i in range(n):
            if worker.stopping:
                break
            # Emit custom event
            worker.emit('update_progress', i, n)
            time.sleep(random.random())

app = ui.Application()

dialog = Dialog(title="Worker")
dialog.run()

Something missing?

Any underlying PyQt5 instance can be accessed directly using property qt. This also enables to mix in custom PyQt5 classes and instances.

widget.qt.setWindowTitle("Spam!")
widget.qt.customContextMenuRequested.connect(lambda pos: None)
widget.qt.layout().addWidget(QtWidgets.QPusbButton())

License

Qutie is licensed under the GNU General Public License Version 3.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qutie-1.7.0.tar.gz (32.8 kB view details)

Uploaded Source

File details

Details for the file qutie-1.7.0.tar.gz.

File metadata

  • Download URL: qutie-1.7.0.tar.gz
  • Upload date:
  • Size: 32.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/39.0.1 requests-toolbelt/0.9.1 tqdm/4.57.0 CPython/3.6.9

File hashes

Hashes for qutie-1.7.0.tar.gz
Algorithm Hash digest
SHA256 57e86ba014beaea4ff6c3afa5994a235371c942823125988ae9e1cb7c36ac05d
MD5 3bad1ad4eabd41f54e0588160eca847a
BLAKE2b-256 a330141e38e6b838f494fba1dc13abcfa302ece4fa4a70d7f33b11253b31f447

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page