Git Product home page Git Product logo

picotui's Introduction

picotui

Picotui is a Text User Interface (TUI) widget library for Python3. It is known to work with CPython3 and Pycopy (Unix version is officially supported for the latter), but should work with any Python3 implementation which allows to access stdin/stdout file descriptors.

You can learn more about it with the help of a virtual Q&A session:

Q: There're a few TUI libraries for Python, why yet another one?

A: Urwid is one well-known such TUI library. Here's an entry from its FAQ: "How do I create drop-downs, floating windows, and scroll bars? -You need to start writing some fairly complex widgets. This functionality hasn't been added to Urwid yet." So, Urwid is a widget library which doesn't have dropdowns. Version 0.8.0 of Urwid was imported into SVN (and later Git) in 2004. Urwid doesn't have dropdowns and stuff for 10+ years.

Q: Hey, but you cut off the answer from Urwid FAQ. It says: "but if you are willing to write it, we do accept patches". Why didn't you implement those widgets for Urwid and contribute them?

A: Why didn't you? No, wait, that's not productive. I didn't implement them for Urwid because I don't like its architecture and the fact that its widget set is rather weak (so it's hard to write new widgets - there are not enough examples to start from). And don't get me wrong, but the fact that nobody wrote those widgets for Urwid during 10+ years, got to mean something. However, I tried to hack on another, less, but still known Python TUI library - Npyscreen. Its widget set is much more advanced and usable. But - it still has some architectural choices which makes extending it and overriding some behaviors problematic. I also found its project management a bit unresponsive. So, after making a dozen of commits to my fork, I thought it's time to get some breath and started picotui.

Q: So, sun must shine bright in the picotui land, and it must be the best library out there?

A: Alas, no. Let me start with the fact that most TUI libraries are based on curses library for terminal screen management. It makes sure that if you update a screen, only the minimal set of updates is made. This was very important at the era of 300 baud serial connections. Let's count: 300 baud is about 30 bytes/s, and the standard VT100 screen is 80*24 = ~2K. Double that for attributes. So, transferring a complete screen to show to user would take 2 mins. If you draw the same screen twice (no changes in content), it would take 4 mins. curses library cuts that back to mere 2 mins. So, alas, picotui doesn't use curses. That's based on the fact that picotui wants to be compatible with Pycopy, and its philosophy is minimalism - if it's possible to do screen output without curses, let's do just that. It's also grounded in the fact that nobody uses 300 baud modems any longer, most apps are run in a local terminal emulator with instant updates, most of the remaining are run over LANs which also offer fast updates. The modern basic serial connection speed is 115200 which is still too slow for real-time fullscreen updates though. That's why I say "alas". Beyond the optimized screen updates, picotui lacks many other things too: e.g., double-buffering (so redrawing the previous screen content behind pop-ups is up to you), it lacks geometry managers, so calculating coordinates is up to you, etc. Yes, just like that - I lacked widgets the most, and that's what I implemented. The rest is just KISS.

Q: But that's really sad!

A: Indeed, it is. The only good news is that now you have a choice: if you want your app work well with 300 baud modems, you can use other libraries, and if you want widgets, you can use picotui.

Q: So many words, where's a mandatory screenshot?

A: Sure:

image

Documentation

Picotui is an experimental WIP project, and the best documentation currently is the source code (https://github.com/pfalcon/picotui/tree/master/picotui) and examples (see below).

Examples

  • example_widgets.py - Shows repertoire of widgets, inside a dialog.
  • example_menu.py - Shows a "fullscreen" application with a main menu.
  • example_dialogs.py - Shows some standard dialogs.
  • examples/ - More assorted examples.

Known Issues

Pay attention to what Unicode font you use in your console. Some Linux distributions, e.g. Ubuntu, are known to have a broken Unicode font installed by default, which causes various visual artifacts (specifically, Ubuntu Mono font isn't really monospace - many Unicode pseudographic characters have double (or so) width, box-drawing symbols have gaps, etc.)

picotui's People

Contributors

kyleperik avatar neuschaefer avatar peterjschroeder avatar pfalcon avatar tau3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

picotui's Issues

v1.4/2.0: Widged redraw(self) method signature changes to redraw(self, x, y)

What: Widget.redraw(self) (and all subclasses) change to Widget.redraw(self, x, y). Widgets are now expected to store coordinates relative to their parents (instead of absolute screen coordinates). Parents pass their (absolute) coordinates as arguments to children' .redraw(x, y) methods. Children add these coordinate arguments to their own (relative) coordinates for drawing.

Why: Originally, picotui was intended to be as efficient and simple as possible. That's why coordinates were calculated just once and used as is. Any geometry management was considered out of scope of the library. While that remains true, there's a desire to make some simple/common scenarios easier. E.g., when screen is resized, a user may want to keep a dialog box centered on it. Currently, that requires going over all child widgets of a dialog and recalculating their (absolute) coordinates. While definitely possible, and how it was intended to be done originally, after additional experience and feedback, and I'd like to simplify that so only coordinates of the dialog itself need to be updated.

When: feedback is welcome whether this change should be dubbed 1.3 (or 1.4) or 2.0.

MS Windows compatibility?

Dependent on termios but that's not available for Windows. Oddly, picotui installed without complaining about this.

Not windows compatible?

Can you clarify if this is Windows compatible?

but should work with any Python3 implementation which allows to access stdin/stdout file descriptors.

My best guess would be no. But I don't want to start on a project just to figure out I have to start over.

Better variable naming in examples

I just learned about this project today and I'm going through the examples and having a horrible time tracking what the hell these variables are. I see variables with names like:

a, s, d, b

I have to constantly backtrack in the code to remember what these obscure names represent. Don't do this. It's a horrible practice and a waste of time.

1.0: Finalize module split for basic definitions

Currently, defines for colors and keys live in screen.py, so if you need to access just keys, you need to import screen, which isn't too pretty. It would be nice to move these defines to more appropriate place, but where and how?

OTOH, we already have symbols.py for UTF-8 pseudographics defines, so basically there're 2 choices:

  • Other go way of small separate modules, e.g. colors.py and keys.py.
  • Introduce a singler bigger module for all these things (including apparently to merge symbols.py into). Unclear how to call it: basics.py? defs.py?

One benefit of the latter choice is that if another define (or a couple) will be required, not related to anything else, this common defines module can easily take it, while with 1st choice, there will be the same "where to put it?" problem.

Better documentation

It's hard to find out how to do the simplest things. Examples are very limited.

breaking dependencies

picotui needs termios

>python example_dialogs.py
  File "...tty.py", line 5, in <module> from termios import *
ModuleNotFoundError: No module named 'termios'

but

>pipenv install pycopy-termios
Installing pycopy-termios…
Adding pycopy-termios to Pipfile's [packages]…
Installation Succeeded
Locking [packages] dependencies…
Locking Failed!
...
pipenv.patched.notpip._internal.exceptions.
Installation Error: Command "python setup.py egg_info" failed with error code 1 ...

мне кажется, вы зря массово удалили setup.py pfalcon/pycopy-lib@4e9a8ad
отсутствие сетапа ломает автоматическую установку по зависимостям

WListBox item selected?

I'm trying to make a simple file chooser dialog using a listbox. I can catch the changed event and get the text of the highlighted item just fine, but how would I know if the user "selected" that item by pressing the enter key?

How do you use this?

I tried this:

2087 >python3 ./widgets_test.py
Traceback (most recent call last):
  File "./widgets_test.py", line 1, in <module>
    from .screen import *
SystemError: Parent module '' not loaded, cannot perform relative import

How should I go about running widgets_test.py ?

Release 1.0 preparation

I guess there were enough changes, and I believe I applied all breaking changes I had in my so far.

So, I'd be ready to make the 1.0 release soon after leave some room for additional testing.

@peterjschroeder , @fiee: Let me know if you'd be interested to test the latest master. (I address to you because you submitted for than one ticket to the project. Feel free to ignore if you aren't interested/too busy.) Thanks.

Rework terminal input handling

Currently, picotui assumes that a single read from terminal returns a single integral sequence of bytes corresponding to:

  1. A complete escape sequence.
  2. A complete UTF-8 char.

This kinda works on a non-loaded Linux system, but doesn't hold in the general case: what the terminal outputs are just raw bytes, and sequences can be partitioned in any way among them, e.g. not complete sequences can be read, or multiple sequences can be read at once, or arbitrary mix thereof.

Crash with Unicode input

I’m using OSX Terminal.app with a German keyboard. As soon as I type an umlaut or accented character, I get this error:

Traceback (most recent call last):
File "picowidgets.py", line 60, in
res = d.loop()
File ".../python3.6/site-packages/picotui/basewidget.py", line 76, in loop
res = self.handle_input(key)
File ".../python3.6/site-packages/picotui/basewidget.py", line 69, in handle_input
res = self.handle_key(inp)
File ".../python3.6/site-packages/picotui/widgets.py", line 104, in handle_key
res = self.focus_w.handle_key(key)
File ".../python3.6/site-packages/picotui/editor.py", line 183, in handle_key
return self.handle_edit_key(key)
File ".../python3.6/site-packages/picotui/editor.py", line 210, in handle_edit_key
l = l[:self.col + self.margin] + str(key, "utf-8") + l[self.col + self.margin:]
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 0: unexpected end of data

`MenuBar` and `MenuBox`

I'm not entirely sure how to use the MenuBar or MenuBox widgets. I read through the code a little bit and what I think I understand is -

MenuBar takes a menu_struct parameter to initialize, which i gather is supposed to be a dictionary like { "label": Widget } where Widget is specifically a type of widget that supports interaction?

sidenote - Not sure if this is a bug, but if I pass a dictionary containing say a WLabel as one of the widgets, the MenuBar will fail to display it because WLabels don't have a self.kbuf attribute (should WLabel and other non-interactive widgets call super().__init__() in their own __init__?)

So if I pass radiobuttons or checkboxes as the widgets for the MenuBar, is the typical use case for this to function as something like a horizontal "multiple choice dropdown" that can expand different interactive widgets?

another sidenote - passing a WDropDown doesn't work well either, since a dropdown's handle_key calls self.owner.redraw(), and self.owner isn't set for dropdowns or comboboxes in a menu bar (it's only set when those items are added to a Dialog). not sure if this is expected behavior either.

WMenuBox seems similar but smaller+vertical rather than horizontal.

picotui should not assume a particular terminal "theme"

As the video in #50 shows, picotui doesn't properly set all character attributes, and at places, they look weird (white-on-black). That's of course because I use Gnome's default black-on-white theme, and for me it works as it should. Again, should not make such assumptions, and set all attributes explicitly.

(Maybe just set black-on-white as the default attribute, if that's possible, but I'm not sure white-on-black users will be happy (if it persists after quit)).

Feature Request: form field validator

hope WTextEntry can accept a regexp as a validator,

I want to implement a text field that accepts numbers only.

also it would be nice to have a password field where inputs are masked with *

Doesn't run on Windows 10, termios missing

I just installed picotui using "pip install picotui".

Next, trying to run one of the examples I get:

$ python example_widgets.py
Traceback (most recent call last):
File "example_widgets.py", line 7, in
with Context():
File "C:\Dropbox\python_uv\picotui-master\picotui\context.py", line 11, in enter
Screen.init_tty()
File "C:\Dropbox\python_uv\picotui-master\picotui\screen.py", line 121, in init_tty
import tty, termios
File "C:\Users\mje\AppData\Local\Programs\Python\Python37\lib\tty.py", line 5, in
from termios import *
ModuleNotFoundError: No module named 'termios

Next, trying to fix this issue,

pip install termios
ERROR: Could not find a version that satisfies the requirement termios (from versions: none)
ERROR: No matching distribution found for termios

Kind regards,
Michael

Suggestion: use stderr for I/O, or make it configurable

Hello, here is a suggestion: it would be nice if picotui used stderr for I/O, and not stdin+stdout.
Why? that would free stdio and stdout for other application-specific things: the app can get its input from stdin and write result to stdout, unix-way, and it would allow to use picotui-base app in unix pipes:
echo "input-data" | picotui-app | consumer

I'm doing exactly that in my picotui-based app, and it works great.
Here's the code I had to add to make it work (thanks to python, no need to modify picotui code, can just patch it):

from picotui.screen import Screen

#################################################################################
# Start patching picotui to use STDERR (FD 2)
#################################################################################
FD_IN = 2
FD_OUT = 2


import os


def wr(*args):
    s = args[-1]
    # TODO: When Python is 3.5, update this to use only bytes
    if isinstance(s, str):
        s = bytes(s, "utf-8")
    os.write(FD_OUT, s)


from picotui.basewidget import Widget
from picotui.defs import KEYMAP as _KEYMAP


def get_input(self):
    if self.kbuf:
        key = self.kbuf[0:1]
        self.kbuf = self.kbuf[1:]
    else:
        key = os.read(FD_IN, 32)
        if key[0] != 0x1b:
            key = key.decode()
            self.kbuf = key[1:].encode()
            key = key[0:1].encode()
    key = _KEYMAP.get(key, key)

    if isinstance(key, bytes) and key.startswith(b"\x1b[M") and len(key) == 6:
        row = key[5] - 33
        col = key[4] - 33
        return [col, row]

    return key


def screen_size(*args):
    import select
    wr(b"\x1b[18t")
    res = select.select([FD_IN], [], [], 0.2)[0]
    if not res:
        return (80, 24)
    resp = os.read(FD_IN, 32)
    assert resp.startswith(b"\x1b[8;") and resp[-1:] == b"t"
    vals = resp[:-1].split(b";")
    return (int(vals[2]), int(vals[1]))


def init_tty(*args):
    import tty, termios
    global ttyattr
    ttyattr = termios.tcgetattr(FD_IN)
    tty.setraw(FD_IN)


def deinit_tty(*args):
    import termios
    termios.tcsetattr(FD_IN, termios.TCSANOW, ttyattr)


def patch_picotui():
    Screen.wr = wr
    Screen.init_tty = init_tty
    Screen.deinit_tty = deinit_tty
    Screen.screen_size = screen_size
    Widget.get_input = get_input

#################################################################################
# End patching picotui
#################################################################################

.. that's it, just introduced constants instead of hard-coded FD 0 and 1

MacOSX terminal problems

This may well be an issue of lack of docs. Trying out on Python 3.4.3 on Mac OS. Run from the command line, widgets_demo produces approximately the right labels framed in a lot of a-with-circumflex, I'm guessing, escapes that didn't? Possibly I don't have my Terminal options set right, but no info on what's needed.

If I step through widgets_demo in Wing IDE, line 183 of screen.py, cls.org_termios = termios.tcgetattr(0) traps out with termios.error: (25, 'Inappropriate ioctl for device'), and the run goes straight to the finally: clause. I could speculate that this is due to the Wing IDE stdout/stderr window again not configured as expected. But that would be...?

termios.error: (19, 'Operation not supported by device')

It gives me the following error:

[~cloud/Picotui]$ python example_widgets.py
Traceback (most recent call last):
  File "/private/var/mobile/Library/Mobile Documents/iCloud~AsheKube~app~a-Shell/Documents/Picotui/example_widgets.py", line 7, in <module>
    with Context():
  File "/var/mobile/Containers/Data/Application/D0804BBC-49FD-4101-8C2D-E0B2EBD81738/Library/lib/python3.11/site-packages/picotui/context.py", line 11, in __enter__
    Screen.init_tty()
  File "/var/mobile/Containers/Data/Application/D0804BBC-49FD-4101-8C2D-E0B2EBD81738/Library/lib/python3.11/site-packages/picotui/screen.py", line 122, in init_tty
    cls.org_termios = termios.tcgetattr(0)
                      ^^^^^^^^^^^^^^^^^^^^
termios.error: (19, 'Operation not supported by device')
[~cloud/Picotui]$ 

Other TUI libs, like Rich, work fine.

Request: packaging

This seems to be a very nice option to rapidly develop TUI applications. What I would really like to see is a .deb (Debian) package for especially Raspbian. Is packaging on the to-do list?

Suggestion - add a "disabled" state to all FocusableWidget instances

Hi,
I'd like to share a feature I implemented and hope that it could help someone.
I needed a way to disable any interactive widget (e. g. disable a drop down until a checkbox is selected) - this feature seems to be only available for buttons.
So, this is what I did.

This is my new FocusableWidget class (added disabled status, disable and enable methods):

class FocusableWidget(Widget):
    # If set to non-False, pressing Enter on this widget finishes
    # dialog, with Dialog.loop() return value being this value.
    finish_dialog = False

    def __init__(self, disabled=False):
        super().__init__()
        self.disabled = disabled

    def enable(self):
        self.disabled = False

    def disable(self):
        self.disabled = True

These are my new find_focusable* methods for Dialog class (added check for disabled status):

    def find_focusable_by_idx(self, from_idx, direction):
        sz = len(self.childs)
        while 0 <= from_idx < sz:
            child = self.childs[from_idx]
            if isinstance(child, FocusableWidget) and not child.disabled:
                return from_idx, child
            from_idx = (from_idx + direction) % sz
        return None, None

    def find_focusable_by_xy(self, x, y):
        i = 0
        for w in self.childs:
            if isinstance(w, FocusableWidget) and w.inside(
                    x, y) and not w.disabled:
                return i, w
            i += 1
        return None, None

And, last, my new change_focus (just added a check for disabled status on top)

    def change_focus(self, widget):
        if widget.disabled:
            return
        if widget is self.focus_w:
            return
        if self.focus_w:
            self.focus_w.focus = False
            self.focus_w.redraw()
        self.focus_w = widget
        widget.focus = True
        widget.redraw()
        widget.set_cursor()

Will this toolkit work on MicroPython?

Dear Paul,

maybe we missed that detail from the documentation, so please bear with us.

We are humbly asking if this widget toolkit will actually work on embedded devices? Currently, we are trying to invoke it on the most recent Pycom firmware based on MicroPython 1.11

Pycom MicroPython 1.20.1.r[v1.11-3138a13] on 2019-10-08; FiPy with ESP32

but we are receiving

>>> from picotui.context import Context
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "picotui/context.py", line 1, in <module>
  File "picotui/screen.py", line 2, in <module>
ImportError: no module named 'signal'

Likewise, the signal module will depend on the ffilib module.

So, we conclude there probably aren't any chances to run this on neither Pycom MicroPython nor Vanilla MicroPython but only on Pycopy?

Thanks already for taking the time to answer our question revolving this. We would really love to get the spirit of TurboVision to our REPL for being able give [1] a convenient leightweight configuration interface without having to resort to a web-based configuration using a captive portal, which would be rather more heavyweight.

With kind regards,
Andreas.

[1] https://github.com/hiveeyes/hiveeyes-micropython-firmware

KDE Konsole doesn't seem to respond to "get screen size" escape sequence

X11 Konsole/bash on Fedora 33 with Python 3.9.1

Firstly, thank you for the project, it reminds me of the good-old Borland Turbo Vision, which was absolutely epic.

However, I just started writing a prototype and the most basic features seem not to work. I guess it's something specific with my setup (Python 3.9?), but I'm still going to report it in case the problem is unknown and global.

Just for the reference, other console tools like bpytop and s-tui work perfectly.

  1. Mouse is not working at all.
  2. TTY size is not retrieved.

I suspect it's all related to console comms but it's hard to say what's going on.

As far as TTY size I always end up going this route with res being []:

picotui/picotui/screen.py

Lines 144 to 146 in 7a26e62

res = select.select([0], [], [], 0.2)[0]
if not res:
return (80, 24)

bpytop uses os.get_terminal_size() which seems to work perfectly:
https://github.com/aristocratos/bpytop/blob/2fbdc35125ad5d9a9f613736f194a33ec69bb14b/bpytop.py#L651

Anybody else has issues with basic functionality like that? I'm running code virtually identical to the https://github.com/pfalcon/picotui/blob/master/example_menu.py

Inconsistent accessors for ~~selection~~ widgets

Currently there's:

  • WRadioButton.selected
  • WListBox.cur_line
  • WPopupList.get_choice()
  • WDropDown.choice

Additionally:

  • WMenuBar.selected
  • WMenuBox.selected
  • WCheckBox.state

That's pretty inconsistent. The towards-consistency approach is apparently to use "selected", as set by ItemSelWidget base class. But that doesn't cover WListBox case, so maybe should go straight to a method. And that doesn't cover setting the current selected item. A separate method or same method without/with param?

Also, the WCheckBox is an interesting case. It can be easily classified as a selection widget too, but does it make sense to follow the same API as for other, clearly multiselection widgets, or would that cross the border of being non-intuitive?

wbutton onclick

I cant find any documentation anywhere
would love to know how to do a onclick run a function

add __index__.py

Please add an (empty) __index__.py to the module folder, otherwise it doesn’t work as a module (at least not installed as an egg).

1.0: WCheckBox, WRadioButton, WListBox, WDropDown now inherit from ChoiceWidget

As an outcome of #13 , a more structure class hierarchy introduced, with ChoiceWidget abstract base class, which says that any subclass should maintain a value chosen (selected) by user as self.choice. Thus, before:

WCheckBox.state
WRadioButton.selected
WListBox.cur_line
WDropDown.choice

after:

WCheckBox.choice
WRadioButton.choice
WListBox.choice
WDropDown.choice

Geometry management questions

When assigning coordinates to widgets, picotui does not consider that some consoles will be different sizes. So when the application is executed on a different size terminal than it was written for, the widgets may be off-screen, wrap, or there may be extra padding.

Here is my current idea on how to fix this.

Define some functions like this

def ax(x):
    return int(x / 191 * Screen.screen_size()[0])
          
def ay(y):
    return int(y / 52 * Screen.screen_size()[1])

191x52 is the console size on my main desktop. By wrapping each coordinate with these functions it should adjust the placement of the widgets depending on the screen size.

Something like this would be beneficial in the library.

I am thinking it could work like this.

Create a variable in Screen which hold a virtual screen, vscreen_size? The user than sets vscreen_size when writing their code and uses those coordinates to place widgets.

Now in the widgets, when x, y, height, weight, etc. is set, it wraps the given coordinates in these new functions (could probably be a single function, as a tuple?) in each widget.

This would remove the need for constant dividing in a user's application to place the widgets in the same proportional location when screen size is different than what is being developed on.

ListBox.items on changed

Modifying the items in a ListBox, even when calling a changed and redraw on it doesn't update the items in the list.

Example:

Create a DropDown list to filter the ListBox

    d.add(2, 3, "Manufacturer:")
    mfrs = list(set([figures[i][4] for i in range(0,len(figures))]))
    mfrs.sort()
    mfrs.insert(0, ('All',))
    filters_mfrs = WDropDown(20, ["%s" % items for items in mfrs], dropdown_h=round(cheight/4))
    d.add(16, 3, filters_mfrs)

Create the ListBox

    figure_list = WListBox(round(cwidth/2.26), cheight-11, ["%s" % items[0] for items in figures])
    d.add(2, 9, figure_list)

When the DropDown is changed, filter the ListBox based on the selection

    def filters_changed(w):
        ffigures = []
        for i in range(0, len(figures)):
            if w.items[w.choice] == "All" or figures[i][4] == w.items[w.choice]:
                ffigures.append(figures[i])
        figure_list.items = [ffigures[i][6] for i in range(0,len(ffigures))][0]
        figure_list.redraw()
    filters_mfrs.on("changed", filters_changed)

[Question] clear_num_pos

Hi, thanks for open-sourcing this cool project.

I'm trying to read the source code but not familiar with control sequences. What does the clear_num_pos under the Screen class do?

def clear_num_pos(num):
if num > 0:
Screen.wr("\x1b[%dX" % num)

I couldn't find references to the control sequence X. I thought it was like backspace. But on my machine, instead of deleting characters on the left, it deletes characters on the right of the cursor. So in order to delete characters, I have to first move the cursor to the left and then use \x1b[1X to clear the characters on the right. Do I understand it right?

Thanks in advance!

Partial docstrings

Hi, thanks for this tool. I'm reading the source code to understand how it works. I've learned a lot about terminal control sequences.

At the same time, I added docstrings to the code when (I think) I understood the functionality or goal of a piece of code. Just wondering, will you be interested in merging back the docstrings? If you would like, I can make a pull request. The current state of the docstring is in this branch.

Though, I don't think I'll add docstring to all classes/functions. I only add docstring to those I want to read and understand.

no module named "picotui"

#```batch
C:\Users\kosty\OneDrive\Рабочий стол\parse chatbot>pip install picotui
Requirement already satisfied: picotui in c:\programdata\anaconda3\lib\site-packages (1.0.1)

C:\Users\kosty\OneDrive\Рабочий стол\parse chatbot>main.py
Traceback (most recent call last):
File "C:\Users\kosty\OneDrive\Рабочий стол\parse chatbot\main.py", line 69, in
from picotui.widgets import *
ModuleNotFoundError: No module named 'picotui'

C:\Users\kosty\OneDrive\Рабочий стол\parse chatbot>

code (from examples):
```python
from picotui.widgets import *
from picotui.menu import *
from picotui.context import Context


# Dialog on the screen
d = None

# This routine is called to redraw screen "in menu's background"
def screen_redraw(s, allow_cursor=False):
    s.attr_color(C_WHITE, C_BLUE)
    s.cls()
    s.attr_reset()
    d.redraw()


# We have two independent widgets on screen: dialog and main menu,
# so can't call their individual loops, and instead should have
# "main loop" to route events to currently active widget, and
# switch the active one based on special events.
def main_loop():
    while 1:
        key = m.get_input()

        if isinstance(key, list):
            # Mouse click
            x, y = key
            if m.inside(x, y):
                m.focus = True

        if m.focus:
            # If menu is focused, it gets events. If menu is cancelled,
            # it loses focus. Otherwise, if menu selection is made, we
            # quit with with menu result.
            res = m.handle_input(key)
            if res == ACTION_CANCEL:
                m.focus = False
            elif res is not None and res is not True:
                return res
        else:
            # If menu isn't focused, it can be focused by pressing F9.
            if key == KEY_F9:
                m.focus = True
                m.redraw()
                continue
            # Otherwise, dialog gets input
            res = d.handle_input(key)
            if res is not None and res is not True:
                return res


with Context():

    d = Dialog(10, 5, 40, 13)
    d.add(1, 1, WLabel("Label:"))
    d.add(1, 2, WListBox(16, 4, ["choice%d" % i for i in range(10)]))
    d.add(1, 7, WDropDown(10, ["Red", "Green", "Yellow"]))

    b = WButton(8, "OK")
    d.add(3, 10, b)
    b.finish_dialog = ACTION_OK

    b = WButton(8, "Cancel")
    d.add(20, 10, b)
    b.finish_dialog = ACTION_CANCEL

    screen_redraw(Screen)
    Screen.set_screen_redraw(screen_redraw)

    menu_file = WMenuBox([("Open...", "Open"), ("Save", "S"), ("Save as...", "Sa"), ("Exit", "ex")])
    menu_edit = WMenuBox([("Copy", "copy"), ("Paste", "paste")])
    m = WMenuBar([("File", menu_file), ("Edit", menu_edit), ("About", "About")])
    m.permanent = True
    m.redraw()

    res = main_loop()


print("Result:", res)

License?

What is the license for this library? I am intending to fork (I love the ideas, but want to implement some changes and am not sure if they're micropython compatible). I want to make sure that my fork is properly licensed.

OR we can discuss adding some of the changes I'm interested in. For instance, adding width and height variables for the controlling terminal as properties on a Screen instance, adding a setup.py (which may or may not help you out overall) and chaging init_tty and deinit_tty (and various accoutrements) to work as a context manager.

Do widgets support hotkeys ?

This has the look and feel of turbovision from back in the day, which (I think) supported that, but I can't see any info on how to do it ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.