diff --git a/TUTORIAL.md b/TUTORIAL.md index d4854a2..8de9960 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -77,6 +77,7 @@ Now it displays "Hello World!" for 5 seconds, "Goodbye world!" for 5 seconds and With that you just created your first dynamic layout. Congratulations! +[helloworld.py](tutorial/helloworld.py) ## Layout hierarchy @@ -118,6 +119,7 @@ button = tg.createbutton(main, a, "Click here!", root) # Now we give the Layout priority to our content Textview so it is bigger than the Button and the title. +# This priority value is called weight and determines how much of the available space goes to each View. tg.setlinearlayoutparams(main, a, content, 10) @@ -125,7 +127,7 @@ tg.setlinearlayoutparams(main, a, content, 10) time.sleep(5) ``` - +[hellolayout.py](tutorial/hellolayout.py) ## Events @@ -173,6 +175,8 @@ time.sleep(5) print("button pressed", count, "times") ``` +[helloevents.py](tutorial/helloevents.py) + ## Picture-in-picture and images Now let's make something you would actually want to use: @@ -207,25 +211,197 @@ time.sleep(5) The program will display the image you specified as a command line argument for 5 seconds in a small window. +[helloimage.py](tutorial/helloimage.py) + +## Advanced LinearLayout usage + +Currently only LinearLayout is supported for laying out the Views on the screen. +It's a simple Layout, but if you nest multiple LinearLayouts and configure them, it can be very flexible. + +It happens often that you want Views to only occupy the space they need in a LinearLayout instead of getting an equal share of the space. + +```python +import termuxgui as tg +import sys +import time + + +ret = tg.connect() +if ret == None: + sys.exit() +main, event = ret + +a, t = tg.activity(main) + +layout = tg.createlinearlayout(main, a) +# Create 3 TextViews +tv1 = tg.createtextview(main, a, "TextView 1", layout) +tv2 = tg.createtextview(main, a, "TextView 2", layout) +tv3 = tg.createtextview(main, a, "TextView 3", layout) +# Now we make them only occupy the space they need. +# We first have to set the Layout weight to 0 to prevent them from using the free space. +tg.setlinearlayoutparams(main, a, tv1, 0) +tg.setlinearlayoutparams(main, a, tv2, 0) +tg.setlinearlayoutparams(main, a, tv3, 0) +# Then we have to set the height to "WRAP_CONTENT". +# You can specify width and height in 3 ways: as an integer in dp, "WRAP_CONTENT" and "MATCH_PARENT". +# "WRAP_CONTENT" makes a View occupy only the space it needs. +# "MATCH_PARENT" makes a view as large as the parent Layout in that dimension. +# Since the TextViews are displayed in a list, we set the height to "WRAP_CONTENT". +tg.setheight(main, a, tv1, "WRAP_CONTENT") +tg.setheight(main, a, tv2, "WRAP_CONTENT") +tg.setheight(main, a, tv3, "WRAP_CONTENT") +time.sleep(5) +``` + +[linearlayout1.py](tutorial/linearlayout1.py) + + +Let's add a row of buttons and also make them as small as they need to be. + + +```python +import termuxgui as tg +import sys +import time + + +ret = tg.connect() +if ret == None: + sys.exit() +main, event = ret + +a, t = tg.activity(main) +layout = tg.createlinearlayout(main, a) +# Create 3 TextViews +tv1 = tg.createtextview(main, a, "TextView 1", layout) +tv2 = tg.createtextview(main, a, "TextView 2", layout) +buttons = tg.createlinearlayout(main, a, layout, False) # use False to create this as a horizontal Layout +tv3 = tg.createtextview(main, a, "TextView 3", layout) +# Now we make them only occupy the space they need. +# We first have to set the Layout weight to 0 to prevent them from using the free space. +tg.setlinearlayoutparams(main, a, tv1, 0) +tg.setlinearlayoutparams(main, a, tv2, 0) +tg.setlinearlayoutparams(main, a, buttons, 0) +tg.setlinearlayoutparams(main, a, tv3, 0) +# Then we have to set the height to "WRAP_CONTENT". +# You can specify width and height in 3 ways: as an integer in dp, "WRAP_CONTENT" and "MATCH_PARENT". +# "WRAP_CONTENT" makes a View occupy only the space it needs. +# "MATCH_PARENT" makes a view as large as the parent Layout in that dimension. +# Since the TextViews are displayed in a list, we set the height to "WRAP_CONTENT". +tg.setheight(main, a, tv1, "WRAP_CONTENT") +tg.setheight(main, a, tv2, "WRAP_CONTENT") +tg.setheight(main, a, buttons, "WRAP_CONTENT") +tg.setheight(main, a, tv3, "WRAP_CONTENT") +tg.createbutton(main, a, "Button1", buttons) +tg.createbutton(main, a, "Button2", buttons) +tg.createbutton(main, a, "Button3", buttons) + +time.sleep(5) +``` + +As you can see, for nested LinearLayouts it is enough to set the height and weight of the nested Layout "WRAP_CONTENT" and 0. + + +[linearlayout2.py](tutorial/linearlayout2.py) + + +## Inputs + +Currently supported inputs are EditText, Button and Checkbox. +Let's use our new LineaLayout knowledge to make a custom input dialog. +The make it a practical example, we will make a dialog frontend for the `youtubedr` package to download videos. +You can install that package if you want to try it out, but the UI works without that. + +```python +import termuxgui as tg +import sys +import time +from subprocess import run + +ret = tg.connect() +if ret == None: + sys.exit() +main, event = ret + +a, t = tg.activity(main, dialog=True) # make this activity a dialog + +layout = tg.createlinearlayout(main, a) + +title = tg.createtextview(main, a, "Download Video", layout) +tg.settextsize(main, a, title, 30) + +# Let's also create a small margin around the title so it looks nicer. +tg.setmargin(main, a, title, 5) + + +# For dialogs, we don't need to set "WRAP_CONTENT", in dialogs views are automatically packed as close as possible. + +tv1 = tg.createtextview(main, a, "Video link:", layout) +et1 = tg.createedittext(main, a, "", layout) + +tv2 = tg.createtextview(main, a, "Filename (empty for automatic filename):", layout) +et2 = tg.createedittext(main, a, "", layout) + + +# This creates an unchecked Checkbox +check = tg.createcheckbox(main, a, "high quality", False, layout) + +# Create 2 buttons next to each other +buttons = tg.createlinearlayout(main, a, layout, True) + +dl = tg.createbutton(main, a, "download", buttons) +cancel = tg.createbutton(main, a, "cancel", buttons) + + +hd = False + +while True: + ev = tg.getevent(event) + if ev["type"] == "destroy" and ev["value"]["finishing"]: + sys.exit() + # Checkboxes also emit a click event when clicked, but they have the extra value "set" indicating whether the box is now checked or unchecked + if ev["type"] == "click" and ev["value"]["id"] == check: + hd = ev["value"]["set"] + if ev["type"] == "click" and ev["value"]["id"] == dl: + link = tg.gettext(main, a, et1) + name = tg.gettext(main, a, et2) + args = ["youtubedr", "download"] + if len(name) != 0: + args.extend(["-o", name]) + if hd: + args.extend(["-q", "1080p"]) + args.append(link) + if len(link) != 0: + try: + tg.finishactivity(main, a) + run(args) + except: + pass + tg.finishtask(main, t) + if ev["type"] == "click" and ev["value"]["id"] == cancel: + tg.finishactivity(main, a) # this handily also exits the program, because finishing the activity destroys it, and that event is send to us +``` +[inputs.py](tutorial/inputs.py) diff --git a/setup.cfg b/setup.cfg index 5071e04..64a3f1c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = termuxgui -version = 0.1.1 +version = 0.1.2 author = Tarek Sander description = Python bindings for the Termux:GUI plugin. long_description = file: README.md diff --git a/src/termuxgui/__init__.py b/src/termuxgui/__init__.py index 40c79e8..4f1ac68 100644 --- a/src/termuxgui/__init__.py +++ b/src/termuxgui/__init__.py @@ -1,5 +1,5 @@ __all__ = ["activity", "bringtasktofront", "connect", "create", "event", "finishactivity", -"finishtask", "setinputmode", "setpipparams", "settaskdescription", "settheme", "totermux", "viewactions"] +"finishtask", "setinputmode", "setpipmode", "setpipparams", "settaskdescription", "settheme", "toast", "totermux", "viewactions"] for m in __all__: exec("from termuxgui."+m+" import *") diff --git a/src/termuxgui/activity.py b/src/termuxgui/activity.py index b8a4786..3ecedee 100644 --- a/src/termuxgui/activity.py +++ b/src/termuxgui/activity.py @@ -2,13 +2,39 @@ from termuxgui.__send_read_msg import __send_read_msg -def activity(mainSocket, tid=None,flags=0,dialog=None,pip=False): +def activity(mainSocket, tid=None,dialog=None,pip=False,overlay=None,lockscreen=None): '''Creates and Activity and returns the Activity and Task id.''' - params = {"flags": flags} + params = {} if dialog != None: params["dialog"] = dialog if tid != None: params["tid"] = tid if pip != None: params["pip"] = pip + if overlay != None: + params["overlay"] = overlay + if lockscreen != None: + params["lockscreen"] = lockscreen return __send_read_msg(mainSocket, dumps({"method": "newActivity", "params": params})) + + + +def keepscreenon(mainSocket, aid, on): + __send_msg(mainSocket, dumps({"method": "keepScreenOn", "params": {"aid": aid, "on": on}})) + + + +def setorientation(mainSocket, aid, orientation): + __send_msg(mainSocket, dumps({"method": "setOrientation", "params": {"aid": aid, "orientation": orientation}})) + + +def setposition(mainSocket, aid, x, y): + __send_msg(mainSocket, dumps({"method": "setPosition", "params": {"aid": aid, "x": x, "y": y}})) + + +def setposition(mainSocket, aid, x, y): + __send_msg(mainSocket, dumps({"method": "setPosition", "params": {"aid": aid, "x": x, "y": y}})) + +def sendoverlayevents(mainSocket, aid, send): + __send_msg(mainSocket, dumps({"method": "sendOverlayTouchEvent", "params": {"aid": aid, "send": send}})) + diff --git a/src/termuxgui/bringtasktofront.py b/src/termuxgui/bringtasktofront.py index 402c81a..f67dc9d 100644 --- a/src/termuxgui/bringtasktofront.py +++ b/src/termuxgui/bringtasktofront.py @@ -5,3 +5,7 @@ def bringtasktofront(mainSocket, tid): '''Bring an existing Task to the front and shows it.''' __send_msg(mainSocket, dumps({"method": "bringTaskToFront", "params": {"tid": tid}})) + + +def movetasktoback(mainSocket, tid): + __send_msg(mainSocket, dumps({"method": "moveTaskToBack", "params": {"tid": tid}})) diff --git a/src/termuxgui/create.py b/src/termuxgui/create.py index a4b132d..723b393 100644 --- a/src/termuxgui/create.py +++ b/src/termuxgui/create.py @@ -3,8 +3,8 @@ from termuxgui.__send_read_msg import __send_read_msg -def createlinearlayout(mainSocket, aid, parent=None): - args = {"aid": aid} +def createlinearlayout(mainSocket, aid, parent=None, vertical=True): + args = {"aid": aid, "vertical": vertical} if parent != None: args["parent"] = parent return __send_read_msg(mainSocket, dumps({"method": "createLinearLayout", "params": args})) @@ -30,8 +30,8 @@ def createtextview(mainSocket, aid, text, parent=None): return __send_read_msg(mainSocket, dumps({"method": "createTextView", "params": args})) -def createedittext(mainSocket, aid, text, parent=None): - args = {"aid": aid, "text": text} +def createedittext(mainSocket, aid, text, parent=None, singleline=False, line=True): + args = {"aid": aid, "text": text, "singleline": singleline, "line": line} if parent != None: args["parent"] = parent return __send_read_msg(mainSocket, dumps({"method": "createEditText", "params": args})) @@ -43,8 +43,8 @@ def createbutton(mainSocket, aid, text, parent=None): return __send_read_msg(mainSocket, dumps({"method": "createButton", "params": args})) -def createcheckbox(mainSocket, aid, checked=False, parent=None): - args = {"aid": aid, "checked": checked} +def createcheckbox(mainSocket, aid, text, checked=False, parent=None): + args = {"aid": aid, "checked": checked, "text": text} if parent != None: args["parent"] = parent return __send_read_msg(mainSocket, dumps({"method": "createCheckbox", "params": args})) diff --git a/src/termuxgui/setpipmode.py b/src/termuxgui/setpipmode.py new file mode 100644 index 0000000..51865b7 --- /dev/null +++ b/src/termuxgui/setpipmode.py @@ -0,0 +1,9 @@ +from json import dumps + +from termuxgui.__send_msg import __send_msg + +def setpipmode(mainSocket, aid, pip): + __send_msg(mainSocket, dumps({"method": "setPiPMode", "params": {"aid": aid, "pip": pip}})) + +def setpipmodeauto(mainSocket, aid, pip): + __send_msg(mainSocket, dumps({"method": "setPiPModeAuto", "params": {"aid": aid, "pip": pip}})) diff --git a/src/termuxgui/toast.py b/src/termuxgui/toast.py new file mode 100644 index 0000000..2c9fdf6 --- /dev/null +++ b/src/termuxgui/toast.py @@ -0,0 +1,6 @@ +from json import dumps + +from termuxgui.__send_msg import __send_msg + +def toast(mainSocket, text, long=False): + __send_msg(mainSocket, dumps({"method": "toast", "params": {"text": text, "long": long}})) diff --git a/src/termuxgui/viewactions.py b/src/termuxgui/viewactions.py index 7ba7fd6..0bfda30 100644 --- a/src/termuxgui/viewactions.py +++ b/src/termuxgui/viewactions.py @@ -5,9 +5,17 @@ from termuxgui.__send_msg import __send_msg + +def showcursor(mainSocket, aid, id, show): + __send_msg(mainSocket, dumps({"method": "showCursor", "params": {"aid": aid, "id": id, "show": show}})) + + def deleteview(mainSocket, aid, id): __send_msg(mainSocket, dumps({"method": "deleteView", "params": {"aid": aid, "id": id}})) +def clearchildren(mainSocket, aid, id): + __send_msg(mainSocket, dumps({"method": "deleteChildren", "params": {"aid": aid, "id": id}})) + def settextsize(mainSocket, aid, id, size): __send_msg(mainSocket, dumps({"method": "setTextSize", "params": {"aid": aid, "id": id, "size": size}})) @@ -28,6 +36,11 @@ def setmargin(mainSocket, aid, id, margin, dir=None): def setlinearlayoutparams(mainSocket, aid, id, weight): __send_msg(mainSocket, dumps({"method": "setLinearLayoutParams", "params": {"aid": aid, "id": id, "weight": weight}})) +def setwidth(mainSocket, aid, id, width): + __send_msg(mainSocket, dumps({"method": "setWidth", "params": {"aid": aid, "id": id, "width": width}})) + +def setheight(mainSocket, aid, id, height): + __send_msg(mainSocket, dumps({"method": "setHeight", "params": {"aid": aid, "id": id, "height": height}})) def settext(mainSocket, aid, id, text): __send_msg(mainSocket, dumps({"method": "setText", "params": {"aid": aid, "id": id, "text": text}})) diff --git a/tutorial/helloevents.py b/tutorial/helloevents.py index 40976b7..0c7b5cf 100644 --- a/tutorial/helloevents.py +++ b/tutorial/helloevents.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import termuxgui as tg import sys import time diff --git a/tutorial/helloimage.py b/tutorial/helloimage.py index 1c21ac7..b744194 100644 --- a/tutorial/helloimage.py +++ b/tutorial/helloimage.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import termuxgui as tg import sys import time diff --git a/tutorial/hellolayout.py b/tutorial/hellolayout.py index 005fa20..b29fd4c 100644 --- a/tutorial/hellolayout.py +++ b/tutorial/hellolayout.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import termuxgui as tg import sys import time diff --git a/tutorial/helloworld.py b/tutorial/helloworld.py index 0d5cdf9..35da6dd 100644 --- a/tutorial/helloworld.py +++ b/tutorial/helloworld.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import termuxgui as tg import sys import time diff --git a/tutorial/inputs.py b/tutorial/inputs.py new file mode 100644 index 0000000..ec06c13 --- /dev/null +++ b/tutorial/inputs.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +import termuxgui as tg +import sys +import time +from subprocess import run + +ret = tg.connect() +if ret == None: + sys.exit() +main, event = ret + +a, t = tg.activity(main, dialog=True) # make this activity a dialog + +layout = tg.createlinearlayout(main, a) + +title = tg.createtextview(main, a, "Download Video", layout) +tg.settextsize(main, a, title, 30) + +# Let's also create a small margin around the title so it looks nicer. +tg.setmargin(main, a, title, 5) + + +# For dialogs, we don't need to set "WRAP_CONTENT", in dialogs views are automatically packed as close as possible. + +tv1 = tg.createtextview(main, a, "Video link:", layout) +et1 = tg.createedittext(main, a, "", layout) + +tv2 = tg.createtextview(main, a, "Filename (empty for automatic filename):", layout) +et2 = tg.createedittext(main, a, "", layout) + + +# This creates an unchecked Checkbox +check = tg.createcheckbox(main, a, "high quality", False, layout) + +# Create 2 buttons next to each other +buttons = tg.createlinearlayout(main, a, layout, True) + +dl = tg.createbutton(main, a, "download", buttons) +cancel = tg.createbutton(main, a, "cancel", buttons) + + +hd = False + +while True: + ev = tg.getevent(event) + if ev["type"] == "destroy" and ev["value"]["finishing"]: + sys.exit() + # Checkboxes also emit a click event when clicked, but they have the extra value "set" indicating whether the box is now checked or unchecked + if ev["type"] == "click" and ev["value"]["id"] == check: + hd = ev["value"]["set"] + if ev["type"] == "click" and ev["value"]["id"] == dl: + link = tg.gettext(main, a, et1) + name = tg.gettext(main, a, et2) + args = ["youtubedr", "download"] + if len(name) != 0: + args.extend(["-o", name]) + if hd: + args.extend(["-q", "1080p"]) + args.append(link) + if len(link) != 0: + try: + tg.finishactivity(main, a) + run(args) + except: + pass + tg.finishtask(main, t) + if ev["type"] == "click" and ev["value"]["id"] == cancel: + tg.finishactivity(main, a) # this handily also exits the program, because finishing the activity destroys it, and that event is send to us diff --git a/tutorial/linearlayout1.py b/tutorial/linearlayout1.py new file mode 100644 index 0000000..386b447 --- /dev/null +++ b/tutorial/linearlayout1.py @@ -0,0 +1,41 @@ + #!/usr/bin/env python3 + +import termuxgui as tg +import sys +import time + + +ret = tg.connect() +if ret == None: + sys.exit() +main, event = ret + +a, t = tg.activity(main) + + + +layout = tg.createlinearlayout(main, a) + +# Create 3 TextViews +tv1 = tg.createtextview(main, a, "TextView 1", layout) +tv2 = tg.createtextview(main, a, "TextView 2", layout) +tv3 = tg.createtextview(main, a, "TextView 3", layout) + +# Now we make them only occupy the space they need. +# We first have to set the Layout weight to 0 to prevent them from using the free space. +tg.setlinearlayoutparams(main, a, tv1, 0) +tg.setlinearlayoutparams(main, a, tv2, 0) +tg.setlinearlayoutparams(main, a, tv3, 0) + +# Then we have to set the height to "WRAP_CONTENT". +# You can specify width and height in 3 ways: as an integer in dp, "WRAP_CONTENT" and "MATCH_PARENT". +# "WRAP_CONTENT" makes a View occupy only the space it needs. +# "MATCH_PARENT" makes a view as large as the parent Layout in that dimension. + +# Since the TextViews are displayed in a list, we set the height to "WRAP_CONTENT". +tg.setheight(main, a, tv1, "WRAP_CONTENT") +tg.setheight(main, a, tv2, "WRAP_CONTENT") +tg.setheight(main, a, tv3, "WRAP_CONTENT") + + +time.sleep(5) diff --git a/tutorial/linearlayout2.py b/tutorial/linearlayout2.py new file mode 100644 index 0000000..e1dc8a8 --- /dev/null +++ b/tutorial/linearlayout2.py @@ -0,0 +1,49 @@ + #!/usr/bin/env python3 + +import termuxgui as tg +import sys +import time + + +ret = tg.connect() +if ret == None: + sys.exit() +main, event = ret + +a, t = tg.activity(main) + + + +layout = tg.createlinearlayout(main, a) + +# Create 3 TextViews +tv1 = tg.createtextview(main, a, "TextView 1", layout) +tv2 = tg.createtextview(main, a, "TextView 2", layout) +buttons = tg.createlinearlayout(main, a, layout, False) # use False to create this as a horizontal Layout +tv3 = tg.createtextview(main, a, "TextView 3", layout) + +# Now we make them only occupy the space they need. +# We first have to set the Layout weight to 0 to prevent them from using the free space. +tg.setlinearlayoutparams(main, a, tv1, 0) +tg.setlinearlayoutparams(main, a, tv2, 0) +tg.setlinearlayoutparams(main, a, buttons, 0) +tg.setlinearlayoutparams(main, a, tv3, 0) + +# Then we have to set the height to "WRAP_CONTENT". +# You can specify width and height in 3 ways: as an integer in dp, "WRAP_CONTENT" and "MATCH_PARENT". +# "WRAP_CONTENT" makes a View occupy only the space it needs. +# "MATCH_PARENT" makes a view as large as the parent Layout in that dimension. + +# Since the TextViews are displayed in a list, we set the height to "WRAP_CONTENT". +tg.setheight(main, a, tv1, "WRAP_CONTENT") +tg.setheight(main, a, tv2, "WRAP_CONTENT") +tg.setheight(main, a, buttons, "WRAP_CONTENT") +tg.setheight(main, a, tv3, "WRAP_CONTENT") + + +tg.createbutton(main, a, "Button1", buttons) +tg.createbutton(main, a, "Button2", buttons) +tg.createbutton(main, a, "Button3", buttons) + + +time.sleep(5)