From 470fa9e7c686a39f96d6f7849312a4d65a1446cd Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 19 Apr 2020 15:59:48 -0700 Subject: [PATCH] Tweaked the interfaces a bit, added Python documentation, and added "Press 'q' to quit" message. --- C/test.c | 8 ++++- Lua/test.lua | 5 +++ Python/btui.py | 16 ++++----- Python/test.py | 46 +++++++++++++----------- README.md | 98 ++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 117 insertions(+), 56 deletions(-) diff --git a/C/test.c b/C/test.c index 8b6b91d..0e7b9f3 100644 --- a/C/test.c +++ b/C/test.c @@ -19,10 +19,16 @@ int main(void) btui_printf(bt, "%s", title); btui_set_attributes(bt, BTUI_NORMAL); - btui_set_attributes(bt, BTUI_FG_NORMAL | BTUI_FAINT); + btui_set_attributes(bt, BTUI_BOLD); + btui_move_cursor(bt, 0, bt->height-2); + btui_printf(bt, "Press 'q' to quit"); + btui_set_attributes(bt, BTUI_NORMAL); + + btui_set_attributes(bt, BTUI_FAINT); btui_move_cursor(bt, bt->width-16, bt->height-2); btui_printf(bt, "Size = %dx%d", bt->width, bt->height); btui_set_attributes(bt, BTUI_NORMAL); + btui_flush(bt); int mouse_x = -1, mouse_y = -1; diff --git a/Lua/test.lua b/Lua/test.lua index bdcd477..12b9b64 100644 --- a/Lua/test.lua +++ b/Lua/test.lua @@ -32,6 +32,11 @@ btui(function(bt) bt:write(title) end) + bt:withattributes("bold", function() + bt:move(0, bt:height()-1) + bt:write("Press 'q' to quit") + end) + bt:withattributes("faint", function() local s = ("Size: (%dx%d)"):format(bt:width(), bt:height()) bt:move(bt:width()-#s, bt:height()-1) diff --git a/Python/btui.py b/Python/btui.py index bec1a4a..a73114f 100644 --- a/Python/btui.py +++ b/Python/btui.py @@ -1,7 +1,7 @@ import ctypes from contextlib import contextmanager -__all__ = ['btui'] +__all__ = ['open_btui'] # Load the shared library into c types. libbtui = ctypes.CDLL("./libbtui.so") @@ -101,12 +101,6 @@ BTUI_INVERSE_ATTRS = { class BTUI: - def __enter__(self): - self.enable() - - def __exit__(self, *exc): - self.disable() - def enable(self): self._btui = libbtui.btui_enable() @@ -231,4 +225,10 @@ class BTUI: libbtui.btui_draw_shadow(self._btui, int(x), int(y), int(w), int(h)) -btui = BTUI() +_btui = BTUI() +@contextmanager +def open_btui(): + _btui.enable() + try: yield _btui + finally: _btui.disable() + diff --git a/Python/test.py b/Python/test.py index d7ca512..72af8d7 100644 --- a/Python/test.py +++ b/Python/test.py @@ -1,37 +1,41 @@ -from btui import btui +from btui import open_btui -with btui: +with open_btui() as bt: key = None x, y = 1, 1 while key != 'q' and key != 'Ctrl-c': if key == '?': - with btui.disabled(): + with bt.disabled(): input("Press enter to continue.") elif key == 'Ctrl-z': - btui.suspend() + bt.suspend() - btui.clear() + bt.clear() - with btui.fg(.8, .95, .2): - btui.outline_box(x, y, 30, 1) - btui.move(x, y) - btui.write(f"Pressed: {key}") + with bt.fg(.8, .95, .2): + bt.outline_box(x, y, 30, 1) + bt.move(x, y) + bt.write(f"Pressed: {key}") - with btui.attributes("bg_blue", "fg_black"): - title = "Python BTUI Demo" + with bt.attributes("bg_blue", "fg_black"): + title = "Python bt Demo" w = len(title) - center = (btui.width - w) // 2 - btui.fill_box(center - 2, 0, w + 4, 3) - btui.draw_shadow(center - 2, 0, w + 4, 3) - btui.move(center, 1) - btui.write(title) + center = (bt.width - w) // 2 + bt.fill_box(center - 2, 0, w + 4, 3) + bt.draw_shadow(center - 2, 0, w + 4, 3) + bt.move(center, 1) + bt.write(title) - with btui.attributes("faint"): - s = f"Size: ({btui.width}x{btui.height})" - btui.move(btui.width-len(s), btui.height-1) - btui.write(s) + with bt.attributes("bold"): + bt.move(0, bt.height-1) + bt.write("Press 'q' to quit") - key, mx, my = btui.getkey() + with bt.attributes("faint"): + s = f"Size: ({bt.width}x{bt.height})" + bt.move(bt.width-len(s), bt.height-1) + bt.write(s) + + key, mx, my = bt.getkey() if mx: x, y = mx, my diff --git a/README.md b/README.md index 8b994f8..e6a3a86 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ key handling can be improved slightly by polling with zero timeout. ## Language Bindings -BTUI comes with bindings for C and Lua, with plans to add Python bindings. +BTUI comes with bindings for C, Python, and Lua. ### C API @@ -122,38 +122,84 @@ be called with a `BTUI` object, which can be used to do TUI actions. Errors will be propagated out of the function, but the terminal will be cleaned up nicely before the error is printed. Here's a simple example program: - require("btui")(function(bt) - -- Run this code within the TUI, using "bt" as the TUI object - ... - end) +```lua +require("btui")(function(bt) + -- Run this code within the TUI, using "bt" as the TUI object + ... +end) - bt:enable() -- Enables btui (if previously disabled) - bt:disable() -- Disables btui - bt:withdisabled(fn) -- Calls "fn" with btui disabled, then re-enables btui - bt:getkey(timeout=-1) -- Returns a keypress (and optionally, mouse x and y coordinates). The optional timeout argument specifies how long, in tenths of a second, to wait for the next keypress. - bt:write() -- Write text to the terminal - bt:clear(type="screen") -- Clear the terminal. Options are: "screen", "right", "left", "above", "below", "line" - bt:flush() -- Flush the terminal output. Most operations do this anyways. - bt:move(x, y) -- Move the cursor to the given position. (0,0) is the top left corner. - -- R,G,B values are in the range [0.0, 1.0]: - bt:withfg(r,g,b, fn) -- Set the foreground color to (r,g,b), call fn, then reset the foreground color to default - bt:withbg(r,g,b, fn) -- Set the background color to (r,g,b), call fn, then reset the background color to default - bt:linebox(x,y,w,h) -- Draw an outlined box around the given rectangle - bt:fillbox(x,y,w,h) -- Fill the given rectangle with space characters - bt:shadow(x,y,w,h) -- Draw a shaded shadow to the bottom right of the given rectangle - bt:withattributes(attrs..., fn) -- Set the given attributes, call fn, then unset them - bt:setattributes(attrs...) -- Set the given attributes - bt:unsetattributes(attrs...) -- Unset the given attributes - bt:suspend() -- Suspend the current process and drop back into normal terminal mode - bt:width() -- Return the scren width - bt:height() -- Return the screen height +bt:enable() -- Enables btui (if previously disabled) +bt:disable() -- Disables btui +bt:withdisabled(fn) -- Calls "fn" with btui disabled, then re-enables btui +bt:getkey(timeout=-1) -- Returns a keypress (and optionally, mouse x and y coordinates). The optional timeout argument specifies how long, in tenths of a second, to wait for the next keypress. +bt:write() -- Write text to the terminal +bt:clear(type="screen") -- Clear the terminal. Options are: "screen", "right", "left", "above", "below", "line" +bt:flush() -- Flush the terminal output. Most operations do this anyways. +bt:move(x, y) -- Move the cursor to the given position. (0,0) is the top left corner. +-- R,G,B values are in the range [0.0, 1.0]: +bt:withfg(r,g,b, fn) -- Set the foreground color to (r,g,b), call fn, then reset the foreground color to default +bt:withbg(r,g,b, fn) -- Set the background color to (r,g,b), call fn, then reset the background color to default +bt:linebox(x,y,w,h) -- Draw an outlined box around the given rectangle +bt:fillbox(x,y,w,h) -- Fill the given rectangle with space characters +bt:shadow(x,y,w,h) -- Draw a shaded shadow to the bottom right of the given rectangle +bt:withattributes(attrs..., fn) -- Set the given attributes, call fn, then unset them +bt:setattributes(attrs...) -- Set the given attributes +bt:unsetattributes(attrs...) -- Unset the given attributes +bt:suspend() -- Suspend the current process and drop back into normal terminal mode +bt:width() -- Return the scren width +bt:height() -- Return the screen height +``` See [Lua/test.lua](Lua/test.lua) for example usage. Run `make lua` to build the Lua library, and `make testlua` to run the Lua BTUI demo. ### Python API -Coming soon. +The Python library has only one value: `open_btui()`. This is a context manager, +which can be used to safely wrap TUI applications. + +```python +import btui + +with btui.open_btui() as bt: + # Your code here... +``` + +The returned object has the following methods: + +```python +def enable(self): +def disable(self): +@contextmanager +def disabled(self): +def getkey(self, timeout=None): # returns key, mouse_x, mouse_y +@property +def width(self): +@property +def height(self): +def write(self, *args, sep=''): +def write_bytes(self, b): +def move(self, x, y): +def flush(self): +def suspend(self): +def clear(self, mode='screen'): +def set_attributes(self, *attrs): +def unset_attributes(self, *attrs): +@contextmanager +def attributes(self, *attrs): +def set_fg(self, r, g, b): +def set_bg(self, r, g, b): +@contextmanager +def fg(self, r, g, b): +@contextmanager +def bg(self, r, g, b): +def outline_box(self, x, y, w, h): +def fill_box(self, x, y, w, h): +def draw_shadow(self, x, y, w, h): +``` + +See [Python/test.py](Python/test.py) for example code, which can be run with +`make testpython`. ## FAQ