Seite 1 von 1

Simpler Dialog mit urwid

Verfasst: Montag 16. Mai 2022, 16:50
von snafu
Habe mich nun mal ein wenig in die urwid-Bibliothek eingearbeitet. Herausgekommen ist ein kleines Modul zum Erzeugen von sehr einfachen Dialogen mit wahlweise einem oder zwei Buttons. Das Design ist noch extrem billig, aber ich habe schon in Beispielen gesehen, dass mehr möglich ist, wenn eigene Farb-Paletten übergeben werden. Die Buttons verstehen sich immerhin inklusive Callbacks für die Rückgabewerte, an die ich dann über ein try-except-Konstrukt herankomme. So, und hier der Code:

Code: Alles auswählen

#!/usr/bin/env python3
import urwid

class ButtonPressed(Exception):
    def __init__(self, state):
        self.state = state

    def __str__(self):
        return str(self.state)

def button_exit(button, state):
    raise ButtonPressed(state)

def get_buttons(labels_to_action):
    buttons = [
        urwid.Button(label, func, state)
        for label, (func, state) in labels_to_action.items()
    ]
    width = max(map(len, labels_to_action)) + 4
    return urwid.GridFlow(
        buttons, width, 3, 1, "center"
    )

def get_message_box(message, title="", confirm="OK", reject=None):
    text = urwid.Text(message, "center")
    button_actions = {confirm: (button_exit, True)}
    if reject:
        button_actions[reject] = (button_exit, False)
    buttons = get_buttons(button_actions)
    widgets = [urwid.Divider(), text, urwid.Divider(), buttons]
    return urwid.LineBox(urwid.Pile(widgets), title)

def run_loop(box):
    screen_widget = urwid.Filler(
        urwid.Padding(box, "center", ("relative", 50))
    )
    urwid.MainLoop(screen_widget).run()

def main():
    box = get_message_box(
        "Geht das nicht noch etwas schöner?",
        title="Frage", confirm="Ja", reject="Nein"
    )
    try:
        run_loop(box)
    except ButtonPressed as result:
        print(result)

if __name__ == "__main__":
    main()

Re: Simpler Dialog mit urwid

Verfasst: Montag 16. Mai 2022, 18:33
von __blackjack__
@snafu: Rückgabewerte über Ausnahmen, die ja extra erfunden wurden um besondere Ereignisse von normalen Rückgabewerten zu trennen, finde ich ein bisschen schräg als API.

Re: Simpler Dialog mit urwid

Verfasst: Montag 16. Mai 2022, 19:27
von snafu
Mit etwas Farbe sieht es gleich viel ansprechender aus:

Code: Alles auswählen

#!/usr/bin/env python3
import urwid

PALETTE = [
    ("box", "black", "light gray"),
    ("fill-area", "black", "light blue"),
    ("button", "black", "dark cyan"),
    ("button-focus", "white", "dark blue")
]

class ButtonPressed(Exception):
    def __init__(self, state):
        self.state = state

    def __str__(self):
        return str(self.state)


def button_exit(button, state):
    raise ButtonPressed(state)


def get_buttons(labels_to_action):
    buttons = [
        urwid.AttrMap(
            urwid.Button(label, func, state), "button", "button-focus"
        )
        for label, (func, state) in labels_to_action.items()
    ]
    width = max(map(len, labels_to_action)) + 4
    return urwid.GridFlow(buttons, width, 3, 1, "center")

def get_message_box(message, title="", confirm="OK", reject=None):
    text = urwid.Text(message, "center")
    button_actions = {confirm: (button_exit, True)}
    if reject:
        button_actions[reject] = (button_exit, False)
    buttons = get_buttons(button_actions)
    widgets = [urwid.Divider(), text, urwid.Divider(), buttons]
    message_box = urwid.LineBox(urwid.Pile(widgets), title)
    return urwid.AttrMap(message_box, "box")

def run_loop(box, palette=[]):
    padding = urwid.Padding(box, "center", ("relative", 50))
    screen_widget = urwid.AttrMap(urwid.Filler(padding), "fill-area")
    urwid.MainLoop(screen_widget, palette).run()

def main():
    box = get_message_box(
        "Geht das nicht noch etwas schöner?",
        title="Frage", confirm="Ja", reject="Nein"
    )
    try:
        run_loop(box, PALETTE)
    except ButtonPressed as result:
        print(result)

if __name__ == "__main__":
    main()
Das Verwenden von Exceptions zur Flusssteuerung habe ich schon an anderen Stellen gesehen. Irgendwie muss der Rückgabewert ja zum Anwender gelangen, wenn nicht per print() ins Terminal geschrieben werden soll. Gibt es denn dafür eine andere Möglichkeit, außer dass man das Programm objektorientiert umschreibt?