├── docs ├── .nojekyll ├── .gitignore ├── _static │ ├── custom.css │ ├── file.png │ ├── minus.png │ ├── plus.png │ ├── documentation_options.js │ ├── _sphinx_javascript_frameworks_compat.js │ ├── nature.css │ ├── pygments.css │ ├── language_data.js │ ├── doctools.js │ └── alabaster.css ├── objects.inv ├── _sources │ ├── termuxgui.oo.rst.txt │ ├── termuxgui.rst.txt │ └── index.rst.txt ├── .buildinfo ├── search.html ├── py-modindex.html ├── index.html └── searchindex.js ├── pyproject.toml ├── gendoc.sh ├── docsource ├── termuxgui.oo.rst ├── termuxgui.rst ├── index.rst └── conf.py ├── tutorial ├── helloworld.py ├── helloimage.py ├── widget.py ├── webview.py ├── linearlayout1.py ├── linearlayout2.py ├── hellolayout.py ├── swiperefresh.py ├── inputs2.py ├── inputs.py ├── helloevents.py ├── pagedlist.py ├── pages.py ├── buffers.py └── notify.py ├── src └── termuxgui │ ├── viewgroup.py │ ├── oo │ ├── spinner.py │ ├── webview.py │ ├── imageview.py │ ├── progressbar.py │ ├── switch.py │ ├── space.py │ ├── checkbox.py │ ├── tablayout.py │ ├── togglebutton.py │ ├── radiobutton.py │ ├── viewgroup.py │ ├── button.py │ ├── framelayout.py │ ├── gridlayout.py │ ├── textview.py │ ├── linearlayout.py │ ├── swiperefreshlayout.py │ ├── radiogroup.py │ ├── horizontalscrollview.py │ ├── nestedscrollview.py │ ├── view.py │ ├── edittext.py │ ├── __init__.py │ ├── activity.py │ └── connection.py │ ├── space.py │ ├── framelayout.py │ ├── task.py │ ├── button.py │ ├── gridlayout.py │ ├── linearlayout.py │ ├── checkbox.py │ ├── togglebutton.py │ ├── switch.py │ ├── radiobutton.py │ ├── radiogroup.py │ ├── progressbar.py │ ├── spinner.py │ ├── swiperefreshlayout.py │ ├── compoundbutton.py │ ├── tablayout.py │ ├── edittext.py │ ├── horizontalscrollview.py │ ├── nestedscrollview.py │ ├── imageview.py │ ├── msg.py │ ├── buffer.py │ ├── __init__.py │ ├── event.py │ ├── textview.py │ ├── webview.py │ ├── activity.py │ ├── remoteviews.py │ ├── notification.py │ ├── view.py │ └── connection.py ├── setup.cfg ├── README.md ├── .github └── workflows │ └── python-publish.yml ├── tutorial_oop └── helloworld.py ├── TUTORIAL_OOP.md └── .gitignore /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .doctrees -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tareksander/termux-gui-python-bindings/HEAD/docs/objects.inv -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tareksander/termux-gui-python-bindings/HEAD/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tareksander/termux-gui-python-bindings/HEAD/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tareksander/termux-gui-python-bindings/HEAD/docs/_static/plus.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel" 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | -------------------------------------------------------------------------------- /gendoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" || { echo "cd failed"; exit 1; } 3 | # pypi package: sphinx 4 | sphinx-build -b html docsource/ docs/ 5 | -------------------------------------------------------------------------------- /docsource/termuxgui.oo.rst: -------------------------------------------------------------------------------- 1 | termuxgui.oo package 2 | ==================== 3 | 4 | 5 | Module contents 6 | --------------- 7 | 8 | .. automodule:: termuxgui.oo 9 | :members: 10 | :undoc-members: 11 | :show-inheritance: 12 | -------------------------------------------------------------------------------- /docs/_sources/termuxgui.oo.rst.txt: -------------------------------------------------------------------------------- 1 | termuxgui.oo package 2 | ==================== 3 | 4 | 5 | Module contents 6 | --------------- 7 | 8 | .. automodule:: termuxgui.oo 9 | :members: 10 | :undoc-members: 11 | :show-inheritance: 12 | -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 6d7e59f3f36002c5c4d12ec73f0caec0 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docsource/termuxgui.rst: -------------------------------------------------------------------------------- 1 | termuxgui package 2 | ================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | termuxgui.oo 11 | 12 | Module contents 13 | --------------- 14 | 15 | .. automodule:: termuxgui 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/_sources/termuxgui.rst.txt: -------------------------------------------------------------------------------- 1 | termuxgui package 2 | ================= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | termuxgui.oo 11 | 12 | Module contents 13 | --------------- 14 | 15 | .. automodule:: termuxgui 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | 21 | -------------------------------------------------------------------------------- /tutorial/helloworld.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import time 5 | 6 | with tg.Connection() as c: 7 | # create a new Activity. By default, a new Task as created. 8 | a = tg.Activity(c) 9 | # you can find the Task under a.t 10 | 11 | # create the TextView. 12 | tv = tg.TextView(a, "Hello world!") 13 | 14 | time.sleep(5) 15 | tv.settext("Goodbye world!") 16 | 17 | time.sleep(5) 18 | -------------------------------------------------------------------------------- /tutorial/helloimage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import sys 5 | import time 6 | import io 7 | 8 | image = None 9 | with io.open(sys.argv[1], "rb") as f: 10 | image = f.read() 11 | 12 | with tg.Connection() as c: 13 | a = tg.Activity(c, pip=True) # start the activity in picture-in-picture mode 14 | 15 | iv = tg.ImageView(a) # create a ImageView to display the image 16 | 17 | # Set the image 18 | iv.setimage(image) 19 | 20 | time.sleep(5) 21 | -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.1.5', 4 | LANGUAGE: 'en', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false, 12 | SHOW_SEARCH_SUMMARY: true, 13 | ENABLE_SEARCH_SHORTCUTS: true, 14 | }; -------------------------------------------------------------------------------- /src/termuxgui/viewgroup.py: -------------------------------------------------------------------------------- 1 | from termuxgui.activity import Activity 2 | from termuxgui.view import View 3 | 4 | 5 | class ViewGroup(View): 6 | """This represents a generic ViewGroup.""" 7 | 8 | def __init__(self, activity: Activity, id: int): 9 | View.__init__(self, activity, id) 10 | 11 | def clearchildren(self): 12 | """Removes all child Views of this ViewGroup.""" 13 | self.a.c.send_msg({"method": "deleteChildren", "params": {"aid": self.a.aid, "id": self.id}}) 14 | -------------------------------------------------------------------------------- /src/termuxgui/oo/spinner.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | from termuxgui.oo.view import View 5 | 6 | 7 | class Spinner(View, tg.Spinner): 8 | """This represents a Spinner.""" 9 | 10 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, 11 | visibility: Optional[Literal[0, 1, 2]] = None): 12 | tg.Spinner.__init__(self, activity, parent, visibility) 13 | View.__init__(self, activity, self.id, parent) 14 | 15 | -------------------------------------------------------------------------------- /src/termuxgui/oo/webview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class WebView(View, tg.WebView): 9 | """This represents a WebView.""" 10 | 11 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.WebView.__init__(self, activity, parent, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/termuxgui/oo/imageview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class ImageView(View, tg.ImageView): 9 | """This represents an ImageView.""" 10 | 11 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.ImageView.__init__(self, activity, parent, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/termuxgui/oo/progressbar.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class ProgressBar(View, tg.ProgressBar): 9 | """This represents a ProgressBar.""" 10 | 11 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.ProgressBar.__init__(self, activity, parent, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | 16 | -------------------------------------------------------------------------------- /tutorial/widget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import termuxgui as tg 5 | import time 6 | import os 7 | 8 | with tg.Connection() as c: 9 | wid = sys.argv[1] 10 | # Create a remote layout 11 | rv = tg.RemoteViews(c) 12 | # Add a TextView 13 | tv = rv.addTextView() 14 | while True: 15 | files = os.listdir() 16 | files.sort() 17 | # Set the text 18 | rv.setText(tv, '\n'.join(files)) 19 | # update the widget 20 | rv.updateWidget(wid) 21 | time.sleep(1) 22 | -------------------------------------------------------------------------------- /src/termuxgui/oo/switch.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class Switch(View, tg.Switch): 9 | """This represents a Switch.""" 10 | 11 | def __init__(self, activity: tg.Activity, text: str, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.Switch.__init__(self, activity, text, parent, checked, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | -------------------------------------------------------------------------------- /src/termuxgui/oo/space.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class Space(View, tg.Space): 9 | """This represents a Space. You can use this to create empty space in your layout.""" 10 | 11 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.Space.__init__(self, activity, parent, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | -------------------------------------------------------------------------------- /src/termuxgui/oo/checkbox.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class Checkbox(View, tg.Checkbox): 9 | """This represents a CheckBox.""" 10 | 11 | def __init__(self, activity: tg.Activity, text: str, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.Checkbox.__init__(self, activity, text, parent, checked, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | -------------------------------------------------------------------------------- /src/termuxgui/oo/tablayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | from termuxgui.oo.viewgroup import ViewGroup 5 | 6 | 7 | class TabLayout(ViewGroup, tg.TabLayout): 8 | """This represents a TabLayout.""" 9 | 10 | def __init__(self, activity: tg.Activity, parent: Optional[ViewGroup] = None, 11 | visibility: Optional[Literal[0, 1, 2]] = None): 12 | tg.TabLayout.__init__(self, activity, parent, visibility) 13 | ViewGroup.__init__(self, activity, self.id, parent) 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/termuxgui/oo/togglebutton.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class ToggleButton(View, tg.ToggleButton): 9 | """This represents a ToggleButton.""" 10 | 11 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.ToggleButton.__init__(self, activity, parent, checked, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | -------------------------------------------------------------------------------- /docsource/index.rst: -------------------------------------------------------------------------------- 1 | .. termuxgui documentation master file, created by 2 | sphinx-quickstart on Tue Sep 20 01:21:45 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | termuxgui Python Documentation 7 | ===================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 4 11 | :caption: Contents: 12 | 13 | termuxgui 14 | 15 | 16 | 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. termuxgui documentation master file, created by 2 | sphinx-quickstart on Tue Sep 20 01:21:45 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | termuxgui Python Documentation 7 | ===================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 4 11 | :caption: Contents: 12 | 13 | termuxgui 14 | 15 | 16 | 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | -------------------------------------------------------------------------------- /src/termuxgui/oo/radiobutton.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class RadioButton(View, tg.RadioButton): 9 | """This represents a RadioButton.""" 10 | 11 | def __init__(self, activity: tg.Activity, text: str, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.RadioButton.__init__(self, activity, text, parent, checked, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | -------------------------------------------------------------------------------- /src/termuxgui/oo/viewgroup.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import termuxgui as tg 4 | 5 | from abc import ABC 6 | from termuxgui.oo.view import View 7 | 8 | 9 | class ViewGroup(View, tg.ViewGroup, ABC): 10 | 11 | def __init__(self, activity: tg.Activity, id: int, parent: Optional['ViewGroup']): 12 | View.__init__(self, activity, id, parent) 13 | self.views = [] 14 | 15 | def clearchildren(self): 16 | """Removes all child Views of this ViewGroup.""" 17 | tg.ViewGroup.clearchildren(self) 18 | self.views = [] 19 | -------------------------------------------------------------------------------- /src/termuxgui/oo/button.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.activity import Activity 6 | from termuxgui.oo.view import View 7 | 8 | 9 | class Button(View, tg.Button): 10 | """This represents a Button.""" 11 | 12 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, allcaps: bool = False, 13 | visibility: Optional[Literal[0, 1, 2]] = None): 14 | tg.Button.__init__(self, activity, text, parent, allcaps, visibility) 15 | View.__init__(self, activity, self.id, parent) 16 | -------------------------------------------------------------------------------- /src/termuxgui/oo/framelayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.activity import Activity 6 | from termuxgui.oo.viewgroup import ViewGroup 7 | 8 | 9 | class FrameLayout(ViewGroup, tg.FrameLayout): 10 | """This represents a FrameLayout.""" 11 | 12 | def __init__(self, activity: Activity, parent: Optional[ViewGroup] = None, 13 | visibility: Optional[Literal[0, 1, 2]] = None): 14 | tg.FrameLayout.__init__(self, activity, parent, visibility) 15 | ViewGroup.__init__(self, activity, self.id, parent) 16 | -------------------------------------------------------------------------------- /src/termuxgui/oo/gridlayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.activity import Activity 6 | from termuxgui.oo.viewgroup import ViewGroup 7 | 8 | 9 | class GridLayout(ViewGroup, tg.GridLayout): 10 | """This represents a GridLayout.""" 11 | 12 | def __init__(self, activity: Activity, rows: int, cols: int, parent: Optional[ViewGroup] = None, 13 | visibility: Optional[Literal[0, 1, 2]] = None): 14 | tg.GridLayout.__init__(self, activity, rows, cols, parent, visibility) 15 | ViewGroup.__init__(self, activity, self.id, parent) 16 | -------------------------------------------------------------------------------- /src/termuxgui/oo/textview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class TextView(View, tg.TextView): 9 | """This represents a TextView.""" 10 | 11 | def __init__(self, activity: tg.Activity, text: str, parent: Optional[View] = None, selectabletext: bool = False, 12 | clickablelinks: bool = False, visibility: Optional[Literal[0, 1, 2]] = None): 13 | tg.TextView.__init__(self, activity, text, parent, selectabletext, clickablelinks, visibility) 14 | View.__init__(self, activity, self.id, parent) 15 | -------------------------------------------------------------------------------- /src/termuxgui/oo/linearlayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.activity import Activity 6 | from termuxgui.oo.viewgroup import ViewGroup 7 | 8 | 9 | class LinearLayout(ViewGroup, tg.LinearLayout): 10 | """This represents a LinearLayout.""" 11 | 12 | def __init__(self, activity: Activity, parent: Optional[ViewGroup] = None, vertical: bool = True, 13 | visibility: Optional[Literal[0, 1, 2]] = None): 14 | tg.LinearLayout.__init__(self, activity, parent, vertical, visibility) 15 | ViewGroup.__init__(self, activity, self.id, parent) 16 | -------------------------------------------------------------------------------- /src/termuxgui/oo/swiperefreshlayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | from termuxgui.activity import Activity 7 | from termuxgui.oo.viewgroup import ViewGroup 8 | 9 | 10 | class SwipeRefreshLayout(ViewGroup, tg.SwipeRefreshLayout): 11 | """This represents a SwipeRefreshLayout.""" 12 | 13 | def __init__(self, activity: Activity, parent: Optional[View] = None, 14 | visibility: Optional[Literal[0, 1, 2]] = None): 15 | tg.SwipeRefreshLayout.__init__(self, activity, parent, visibility) 16 | ViewGroup.__init__(self, activity, self.id, parent) 17 | 18 | -------------------------------------------------------------------------------- /src/termuxgui/space.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.view import View 5 | 6 | 7 | class Space(View): 8 | """This represents a Space. You can use this to create empty space in your layout.""" 9 | 10 | def __init__(self, activity: Activity, parent: Optional[View] = None, 11 | visibility: Optional[Literal[0, 1, 2]] = None): 12 | args = {"aid": activity.aid, "visibility": visibility} 13 | if parent is not None: 14 | args["parent"] = parent.id 15 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createSpace", "params": args})) 16 | -------------------------------------------------------------------------------- /src/termuxgui/framelayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.viewgroup import ViewGroup 6 | 7 | 8 | class FrameLayout(ViewGroup): 9 | """This represents a FrameLayout.""" 10 | 11 | def __init__(self, activity: Activity, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | ViewGroup.__init__(self, activity, activity.c.send_read_msg({"method": "createFrameLayout", "params": args})) 17 | -------------------------------------------------------------------------------- /src/termuxgui/oo/radiogroup.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | from termuxgui.oo.view import View 5 | from termuxgui.oo.viewgroup import ViewGroup 6 | 7 | 8 | class RadioGroup(ViewGroup, tg.RadioGroup): 9 | """This represents a RadioGroup. 10 | 11 | Only one RadioButton inside a RadioGroup can be checked at once, and the RadioGroup emits events when the checked button has changed.""" 12 | 13 | def __init__(self, activity: tg.Activity, parent: Optional[View] = None, 14 | visibility: Optional[Literal[0, 1, 2]] = None): 15 | tg.RadioGroup.__init__(self, activity, parent, visibility) 16 | ViewGroup.__init__(self, activity, self.id, parent) 17 | -------------------------------------------------------------------------------- /src/termuxgui/task.py: -------------------------------------------------------------------------------- 1 | from termuxgui.connection import Connection 2 | 3 | 4 | class Task: 5 | """This represents an Android Task. It is automatically created with the Activity.""" 6 | 7 | def __init__(self, connection: Connection, tid: int): 8 | self.c = connection 9 | self.tid = tid 10 | 11 | def finish(self): 12 | """Finishes this Task.""" 13 | self.c.send_msg({"method": "finishTask", "params": {"tid": self.tid}}) 14 | 15 | def bringtofront(self): 16 | """Bring this Task to the front and make it visible to the user. 17 | Might require "overlay over other apps" permission.""" 18 | self.c.send_msg({"method": "bringTaskToFront", "params": {"tid": self.tid}}) 19 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = termuxgui 3 | version = 0.1.6 4 | author = Tarek Sander 5 | description = Python bindings for the Termux:GUI plugin. 6 | long_description = file: README.md 7 | long_description_content_type = text/markdown 8 | url = https://github.com/tareksander/termux-gui-python-bindings 9 | project_urls = 10 | Bug Tracker = https://github.com/tareksander/termux-gui-python-bindings/issues 11 | classifiers = 12 | Programming Language :: Python :: 3 13 | License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) 14 | Operating System :: Other OS 15 | 16 | [options] 17 | package_dir = 18 | = src 19 | packages = find: 20 | python_requires = >=3.10 21 | 22 | [options.packages.find] 23 | where = src 24 | -------------------------------------------------------------------------------- /src/termuxgui/button.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.textview import TextView 5 | from termuxgui.view import View 6 | 7 | 8 | class Button(TextView): 9 | """This represents a Button.""" 10 | 11 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, allcaps: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "text": text, "allcaps": allcaps, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createButton", "params": args})) 17 | -------------------------------------------------------------------------------- /src/termuxgui/gridlayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.viewgroup import ViewGroup 6 | 7 | 8 | class GridLayout(ViewGroup): 9 | """This represents a GridLayout.""" 10 | 11 | def __init__(self, activity: Activity, rows: int, cols: int, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "rows": rows, "cols": cols, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | ViewGroup.__init__(self, activity, activity.c.send_read_msg({"method": "createGridLayout", "params": args})) 17 | -------------------------------------------------------------------------------- /src/termuxgui/linearlayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.view import View 5 | from termuxgui.viewgroup import ViewGroup 6 | 7 | 8 | class LinearLayout(ViewGroup): 9 | """This represents a LinearLayout.""" 10 | 11 | def __init__(self, activity: Activity, parent: Optional[View] = None, vertical: bool = True, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "vertical": vertical, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | ViewGroup.__init__(self, activity, activity.c.send_read_msg({"method": "createLinearLayout", "params": args})) 17 | -------------------------------------------------------------------------------- /src/termuxgui/checkbox.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.compoundbutton import CompoundButton 5 | from termuxgui.view import View 6 | 7 | 8 | class Checkbox(CompoundButton): 9 | """This represents a CheckBox.""" 10 | 11 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "text": text, "checked": checked, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | self.checked = checked 17 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createCheckbox", "params": args})) 18 | -------------------------------------------------------------------------------- /src/termuxgui/togglebutton.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.compoundbutton import CompoundButton 6 | 7 | 8 | class ToggleButton(CompoundButton): 9 | """This represents a ToggleButton.""" 10 | 11 | def __init__(self, activity: Activity, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "checked": checked, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | self.checked = checked 17 | CompoundButton.__init__(self, activity, activity.c.send_read_msg({"method": "createToggleButton", "params": args})) 18 | -------------------------------------------------------------------------------- /src/termuxgui/switch.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.compoundbutton import CompoundButton 6 | 7 | 8 | class Switch(CompoundButton): 9 | """This represents a Switch.""" 10 | 11 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "text": text, "checked": checked, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | self.checked = checked 17 | CompoundButton.__init__(self, activity, activity.c.send_read_msg({"method": "createSwitch", "params": args})) 18 | -------------------------------------------------------------------------------- /src/termuxgui/oo/horizontalscrollview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.activity import Activity 6 | from termuxgui.oo.framelayout import FrameLayout 7 | from termuxgui.oo.viewgroup import ViewGroup 8 | 9 | 10 | class HorizontalScrollView(FrameLayout, tg.HorizontalScrollView): 11 | """This represents a HorizontalScrollView.""" 12 | 13 | def __init__(self, activity: Activity, parent: Optional[ViewGroup] = None, fillviewport: bool = False, 14 | snapping: bool = False, nobar: bool = False, 15 | visibility: Optional[Literal[0, 1, 2]] = None): 16 | tg.HorizontalScrollView.__init__(self, activity, parent, fillviewport, snapping, nobar, visibility) 17 | ViewGroup.__init__(self, activity, self.id, parent) 18 | -------------------------------------------------------------------------------- /src/termuxgui/radiobutton.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.compoundbutton import CompoundButton 6 | 7 | 8 | class RadioButton(CompoundButton): 9 | """This represents a RadioButton.""" 10 | 11 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, checked: bool = False, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "text": text, "checked": checked, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | self.checked = checked 17 | CompoundButton.__init__(self, activity, activity.c.send_read_msg({"method": "createRadioButton", "params": args})) 18 | -------------------------------------------------------------------------------- /src/termuxgui/oo/nestedscrollview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.view import View 6 | from termuxgui.activity import Activity 7 | from termuxgui.oo.framelayout import FrameLayout 8 | from termuxgui.oo.viewgroup import ViewGroup 9 | 10 | 11 | class NestedScrollView(FrameLayout, tg.NestedScrollView): 12 | """This represents a NestedScrollView.""" 13 | 14 | def __init__(self, activity: Activity, parent: Optional[View] = None, fillviewport: bool = False, 15 | snapping: bool = False, nobar: bool = False, 16 | visibility: Optional[Literal[0, 1, 2]] = None): 17 | tg.NestedScrollView.__init__(self, activity, parent, fillviewport, snapping, nobar, visibility) 18 | ViewGroup.__init__(self, activity, self.id, parent) 19 | 20 | -------------------------------------------------------------------------------- /src/termuxgui/oo/view.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from typing import Optional, TYPE_CHECKING 3 | 4 | import termuxgui as tg 5 | from termuxgui.activity import Activity 6 | 7 | 8 | class View(tg.View, ABC): 9 | """This represents a generic View.""" 10 | 11 | def __init__(self, activity: Activity, id: int, parent: Optional['termuxgui.oo.viewgroup.ViewGroup']): 12 | tg.View.__init__(self, activity, id) 13 | self.parent = parent 14 | if self.parent: 15 | parent.views.append(self) 16 | else: 17 | activity.root = self 18 | 19 | def delete(self): 20 | """Deletes this View from the layout.""" 21 | if self.parent: 22 | self.parent.views.remove(self) 23 | tg.View.delete(self) 24 | 25 | 26 | if TYPE_CHECKING: 27 | import termuxgui.oo.viewgroup 28 | -------------------------------------------------------------------------------- /src/termuxgui/radiogroup.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.linearlayout import LinearLayout 6 | from termuxgui.viewgroup import ViewGroup 7 | 8 | 9 | class RadioGroup(LinearLayout): 10 | """This represents a RadioGroup. 11 | 12 | Only one RadioButton inside a RadioGroup can be checked at once, and the RadioGroup emits events when the checked button has changed.""" 13 | 14 | def __init__(self, activity: Activity, parent: Optional[View] = None, 15 | visibility: Optional[Literal[0, 1, 2]] = None): 16 | args = {"aid": activity.aid, "visibility": visibility} 17 | if parent is not None: 18 | args["parent"] = parent.id 19 | ViewGroup.__init__(self, activity, activity.c.send_read_msg({"method": "createRadioGroup", "params": args})) 20 | -------------------------------------------------------------------------------- /tutorial/webview.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time 4 | import termuxgui as tg 5 | import sys 6 | 7 | with tg.Connection() as c: 8 | a = tg.Activity(c) 9 | 10 | # create a WebView 11 | wv = tg.WebView(a) 12 | 13 | # Set the webpage content 14 | wv.setdata("

Hello HTML

This is a paragraph

") 15 | 16 | time.sleep(3) 17 | 18 | # Load a web page via a URL 19 | wv.loaduri("https://www.google.com") 20 | 21 | time.sleep(3) 22 | 23 | # Prompt the user to allow Javascript 24 | wv.allowjavascript(True) 25 | 26 | # Run Javascript in the page 27 | wv.evaluatejs('''document.body.innerHTML = "

Hello JS

Set via Javascript

"''') 28 | 29 | for ev in c.events(): 30 | if ev.type == tg.Event.destroy and ev.value["finishing"]: 31 | sys.exit() 32 | -------------------------------------------------------------------------------- /src/termuxgui/progressbar.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.view import View 5 | 6 | 7 | class ProgressBar(View): 8 | """This represents a ProgressBar.""" 9 | 10 | def __init__(self, activity: Activity, parent: Optional[View] = None, 11 | visibility: Optional[Literal[0, 1, 2]] = None): 12 | args = {"aid": activity.aid, "visibility": visibility} 13 | if parent is not None: 14 | args["parent"] = parent.id 15 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createProgressBar", "params": args})) 16 | 17 | def setprogress(self, progress: int): 18 | """Sets the progress of the ProgressBar. The progress has to be an integer from 0 to 100 (inclusive).""" 19 | self.a.c.send_msg({"method": "setProgress", "params": {"aid": self.a.aid, "id": self.id, "progress": progress}}) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Termux:GUI Python Bindings 2 | A Python library to interact with the Termux:GUI plugin. 3 | 4 | See [here](https://github.com/tareksander/termux-gui-python-bindings/blob/main/TUTORIAL.md) for a tutorial 5 | and [here](https://github.com/tareksander/termux-gui-python-bindings/blob/main/TUTORIAL_OOP.md) for using the more object-oriented subpackage. 6 | 7 | There is also [documentation](https://tareksander.github.io/termux-gui-python-bindings/index.html) generated from the docstrings. 8 | 9 | 10 | ### Installing and updating: 11 | Just use `pip install termuxgui` to install from pypi. 12 | 13 | 14 | ### License 15 | 16 | The license is the [Mozilla Public License 2.0](https://www.mozilla.org/en-US/MPL/2.0/). 17 | TL;DR: You can use this library in your own projects, regardless of the license you choose for it. Modifications to this library have to be published under the MPL 2.0 (or a GNU license in some cases) though. 18 | -------------------------------------------------------------------------------- /src/termuxgui/oo/edittext.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | import termuxgui as tg 4 | 5 | from termuxgui.oo.view import View 6 | 7 | 8 | class EditText(View, tg.EditText): 9 | """This represents an EditText.""" 10 | 11 | def __init__(self, activity: tg.Activity, text: str, parent: Optional[View] = None, singleline: bool = False, 12 | line: bool = True, blockinput: bool = False, 13 | inputtype: Literal["text", "textMultiLine", "phone", "date", "time", "datetime", "number", 14 | "numberDecimal", "numberPassword", "numberSigned", "numberDecimalSigned", 15 | "textEmailAddress", "textPassword"] = "text", 16 | visibility: Optional[Literal[0, 1, 2]] = None): 17 | tg.EditText.__init__(self, activity, text, parent, singleline, line, blockinput, inputtype, visibility) 18 | View.__init__(self, activity, self.id, parent) 19 | 20 | -------------------------------------------------------------------------------- /src/termuxgui/spinner.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.view import View 5 | 6 | 7 | class Spinner(View): 8 | """This represents a Spinner.""" 9 | 10 | def __init__(self, activity: Activity, parent: Optional[View] = None, 11 | visibility: Optional[Literal[0, 1, 2]] = None): 12 | args = {"aid": activity.aid, "visibility": visibility} 13 | if parent is not None: 14 | args["parent"] = parent.id 15 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createSpinner", "params": args})) 16 | 17 | def setlist(self, list: List[str]): 18 | """Sets the list of items displayed in the Spinner.""" 19 | self.a.c.send_msg({"method": "setList", "params": {"aid": self.a.aid, "id": self.id, "list": list}}) 20 | 21 | def selectitem(self, item: int): 22 | """Sets the selected item of the Spinner.""" 23 | self.a.c.send_msg({"method": "selectItem", "params": {"aid": self.a.aid, "id": self.id, "item": item}}) 24 | -------------------------------------------------------------------------------- /src/termuxgui/swiperefreshlayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.viewgroup import ViewGroup 6 | 7 | 8 | class SwipeRefreshLayout(ViewGroup): 9 | """This represents a SwipeRefreshLayout.""" 10 | 11 | def __init__(self, activity: Activity, parent: Optional[View] = None, 12 | visibility: Optional[Literal[0, 1, 2]] = None): 13 | args = {"aid": activity.aid, "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | ViewGroup.__init__(self, activity, 17 | activity.c.send_read_msg({"method": "createSwipeRefreshLayout", "params": args})) 18 | 19 | def setrefreshing(self, refreshing: bool): 20 | """Sets whether the SwipeRefreshLayout displays the refresh animation. 21 | 22 | You have to call this with False when you refreshed the contents.""" 23 | self.a.c.send_msg( 24 | {"method": "setRefreshing", "params": {"aid": self.a.aid, "id": self.id, "refresh": refreshing}}) 25 | -------------------------------------------------------------------------------- /src/termuxgui/compoundbutton.py: -------------------------------------------------------------------------------- 1 | from termuxgui.activity import Activity 2 | from termuxgui.button import Button 3 | from termuxgui.event import Event 4 | from termuxgui.view import View 5 | 6 | 7 | class CompoundButton(Button): 8 | """This represents a CompoundButton. 9 | 10 | This class doesn't correspond to a particular View, it is used to provide common methods for all CompoundButtons.""" 11 | 12 | def __init__(self, activity: Activity, id: int): 13 | View.__init__(self, activity, id) 14 | self.checked = False 15 | 16 | def handleevent(self, e: Event): 17 | """Use this to let the CompoundButton track whether it is checked or not. Just pass every Event here.""" 18 | if e.type == Event.click and e.aid == self.a.aid and e.id == self.id: 19 | self.checked = e.value["set"] 20 | View.handleevent(self, e) 21 | 22 | def setchecked(self, checked: bool): 23 | """Explicitly set the checked status of this CompoundButton.""" 24 | self.checked = checked 25 | self.a.c.send_msg({"method": "setChecked", "params": {"aid": self.a.aid, "id": self.id, "checked": checked}}) 26 | -------------------------------------------------------------------------------- /docsource/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | import sys 10 | import os 11 | 12 | sys.path.append(os.path.dirname(os.getcwd())+"/src/") 13 | 14 | project = 'termuxgui' 15 | copyright = '2022, Tarek Sander' 16 | author = 'Tarek Sander' 17 | release = '0.1.5' 18 | 19 | # -- General configuration --------------------------------------------------- 20 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 21 | 22 | extensions = [ 23 | 'sphinx.ext.autodoc', 24 | ] 25 | 26 | 27 | templates_path = ['_templates'] 28 | exclude_patterns = [] 29 | 30 | 31 | 32 | # -- Options for HTML output ------------------------------------------------- 33 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 34 | 35 | html_theme = 'nature' 36 | html_static_path = ['_static'] 37 | -------------------------------------------------------------------------------- /src/termuxgui/tablayout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.horizontalscrollview import HorizontalScrollView 6 | from termuxgui.viewgroup import ViewGroup 7 | 8 | 9 | class TabLayout(HorizontalScrollView): 10 | """This represents a TabLayout.""" 11 | 12 | def __init__(self, activity: Activity, parent: Optional[View] = None, 13 | visibility: Optional[Literal[0, 1, 2]] = None): 14 | args = {"aid": activity.aid, "visibility": visibility} 15 | if parent is not None: 16 | args["parent"] = parent.id 17 | ViewGroup.__init__(self, activity, activity.c.send_read_msg({"method": "createTabLayout", "params": args})) 18 | 19 | def setlist(self, list: List[str]): 20 | """Sets the list tabs.""" 21 | self.a.c.send_msg({"method": "setList", "params": {"aid": self.a.aid, "id": self.id, "list": list}}) 22 | 23 | def selecttab(self, tab: int): 24 | """Selects a tab in the TabLayout. tab is the index of the tab to select, starting at 0.""" 25 | self.a.c.send_msg({"method": "selectTab", "params": {"aid": self.a.aid, "id": self.id, "tab": tab}}) 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /tutorial/linearlayout1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import time 5 | 6 | with tg.Connection() as c: 7 | a = tg.Activity(c) 8 | 9 | layout = tg.LinearLayout(a) 10 | 11 | # Create 3 TextViews 12 | tv1 = tg.TextView(a, "TextView 1", layout) 13 | tv2 = tg.TextView(a, "TextView 2", layout) 14 | tv3 = tg.TextView(a, "TextView 3", layout) 15 | 16 | # Now we make them only occupy the space they need. 17 | # We first have to set the layout weight to 0 to prevent them from using the free space. 18 | tv1.setlinearlayoutparams(0) 19 | tv2.setlinearlayoutparams(0) 20 | tv3.setlinearlayoutparams(0) 21 | 22 | # Then we have to set the height to "WRAP_CONTENT". 23 | # You can specify width and height in 3 ways: as an integer in dp, "WRAP_CONTENT" and "MATCH_PARENT". 24 | # "WRAP_CONTENT" makes a View occupy only the space it needs. 25 | # "MATCH_PARENT" makes a view as large as the parent Layout in that dimension. 26 | # To prevent mistyping, "WRAP_CONTENT" and "MATCH_PARENT" are also defined as constants in the View class. 27 | 28 | # Since the TextViews are displayed in a list, we set the height to "WRAP_CONTENT". 29 | tv1.setheight(tg.View.WRAP_CONTENT) 30 | tv2.setheight(tg.View.WRAP_CONTENT) 31 | tv3.setheight(tg.View.WRAP_CONTENT) 32 | 33 | time.sleep(5) 34 | -------------------------------------------------------------------------------- /src/termuxgui/edittext.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.textview import TextView 5 | from termuxgui.view import View 6 | 7 | 8 | class EditText(TextView): 9 | """This represents an EditText.""" 10 | 11 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, singleline: bool = False, 12 | line: bool = True, blockinput: bool = False, 13 | inputtype: Literal["text", "textMultiLine", "phone", "date", "time", "datetime", "number", 14 | "numberDecimal", "numberPassword", "numberSigned", "numberDecimalSigned", 15 | "textEmailAddress", "textPassword"] = "text", 16 | visibility: Optional[Literal[0, 1, 2]] = None): 17 | args = {"aid": activity.aid, "text": text, "singleline": singleline, "line": line, "blockinput": blockinput, 18 | "type": inputtype, "visibility": visibility} 19 | if parent is not None: 20 | args["parent"] = parent.id 21 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createEditText", "params": args})) 22 | 23 | def showcursor(self, show: bool): 24 | """Sets whether the cursor position should be shown.""" 25 | self.a.c.send_msg({"method": "showCursor", "params": {"aid": self.a.aid, "id": self.id, "show": show}}) 26 | -------------------------------------------------------------------------------- /src/termuxgui/horizontalscrollview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.framelayout import FrameLayout 6 | from termuxgui.viewgroup import ViewGroup 7 | 8 | 9 | class HorizontalScrollView(FrameLayout): 10 | """This represents a HorizontalScrollView.""" 11 | 12 | def __init__(self, activity: Activity, parent: Optional[View] = None, fillviewport: bool = False, 13 | snapping: bool = False, nobar: bool = False, 14 | visibility: Optional[Literal[0, 1, 2]] = None): 15 | args = {"aid": activity.aid, "fillviewport": fillviewport, "snapping": snapping, "nobar": nobar, 16 | "visibility": visibility} 17 | if parent is not None: 18 | args["parent"] = parent.id 19 | ViewGroup.__init__(self, activity, activity.c.send_read_msg({"method": "createHorizontalScrollView", "params": args})) 20 | 21 | def getscrollposition(self) -> List[int]: 22 | """Gets the x and x scroll positions.""" 23 | return self.a.c.send_read_msg({"method": "getScrollPosition", "params": {"aid": self.a.aid, "id": self.id}}) 24 | 25 | def setscrollposition(self, x: int, y: int, smooth: bool = True): 26 | """Sets the x and x scroll positions. smooth specifies whether is's a smooth scoll or not.""" 27 | self.a.c.send_msg({"method": "setScrollPosition", "params": {"aid": self.a.aid, "id": self.id, "x": x, "y": y, "soft": smooth}}) 28 | -------------------------------------------------------------------------------- /src/termuxgui/nestedscrollview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List, Literal 2 | 3 | from termuxgui.view import View 4 | from termuxgui.activity import Activity 5 | from termuxgui.framelayout import FrameLayout 6 | from termuxgui.viewgroup import ViewGroup 7 | 8 | 9 | class NestedScrollView(FrameLayout): 10 | """This represents a NestedScrollView.""" 11 | 12 | def __init__(self, activity: Activity, parent: Optional[View] = None, fillviewport: bool = False, 13 | snapping: bool = False, nobar: bool = False, 14 | visibility: Optional[Literal[0, 1, 2]] = None): 15 | args = {"aid": activity.aid, "fillviewport": fillviewport, "snapping": snapping, "nobar": nobar, 16 | "visibility": visibility} 17 | if parent is not None: 18 | args["parent"] = parent.id 19 | ViewGroup.__init__(self, activity, 20 | activity.c.send_read_msg({"method": "createNestedScrollView", "params": args})) 21 | 22 | def getscrollposition(self) -> List[int]: 23 | """Gets the x and x scroll positions.""" 24 | return self.a.c.send_read_msg({"method": "getScrollPosition", "params": {"aid": self.a.aid, "id": self.id}}) 25 | 26 | def setscrollposition(self, x: int, y: int, smooth: bool = True): 27 | """Sets the x and x scroll positions. smooth specifies whether it's a smooth scroll or not.""" 28 | self.a.c.send_msg({"method": "setScrollPosition", 29 | "params": {"aid": self.a.aid, "id": self.id, "x": x, "y": y, "soft": smooth}}) 30 | -------------------------------------------------------------------------------- /src/termuxgui/imageview.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from typing import Optional, Literal 3 | 4 | from termuxgui.buffer import Buffer 5 | from termuxgui.activity import Activity 6 | from termuxgui.view import View 7 | 8 | 9 | class ImageView(View): 10 | """This represents an ImageView.""" 11 | 12 | def __init__(self, activity: Activity, parent: Optional[View] = None, 13 | visibility: Optional[Literal[0, 1, 2]] = None): 14 | args = {"aid": activity.aid, "visibility": visibility} 15 | if parent is not None: 16 | args["parent"] = parent.id 17 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createImageView", "params": args})) 18 | 19 | def setimage(self, img: bytes): 20 | """Sets the image of the ImageView. 21 | The image has to be a bytes object containing the image in png or jpeg format.""" 22 | self.a.c.send_msg({"method": "setImage", "params": {"aid": self.a.aid, "id": self.id, 23 | "img": base64.standard_b64encode(img).decode("ascii")}}) 24 | 25 | def setbuffer(self, b: Buffer): 26 | """Makes the ImageView use a shared buffer as the image source.""" 27 | self.a.c.send_msg({"method": "setBuffer", "params": {"aid": self.a.aid, "id": self.id, "bid": b.bid}}) 28 | 29 | def refresh(self): 30 | """Redraws the ImageView. You have to use this after blitting the Buffer when using a buffer.""" 31 | self.a.c.send_msg({"method": "refreshImageView", "params": {"aid": self.a.aid, "id": self.id}}) 32 | -------------------------------------------------------------------------------- /tutorial/linearlayout2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import time 5 | 6 | with tg.Connection() as c: 7 | a = tg.Activity(c) 8 | 9 | layout = tg.LinearLayout(a) 10 | 11 | # Create 3 TextViews 12 | tv1 = tg.TextView(a, "TextView 1", layout) 13 | tv2 = tg.TextView(a, "TextView 2", layout) 14 | buttons = tg.LinearLayout(a, layout, False) # use False to create this as a horizontal layout 15 | tv3 = tg.TextView(a, "TextView 3", layout) 16 | 17 | # Now we make them only occupy the space they need. 18 | # We first have to set the layout weight to 0 to prevent them from using the free space. 19 | tv1.setlinearlayoutparams(0) 20 | tv2.setlinearlayoutparams(0) 21 | buttons.setlinearlayoutparams(0) 22 | tv3.setlinearlayoutparams(0) 23 | 24 | # Then we have to set the height to "WRAP_CONTENT". 25 | # You can specify width and height in 3 ways: as an integer in dp, "WRAP_CONTENT" and "MATCH_PARENT". 26 | # "WRAP_CONTENT" makes a View occupy only the space it needs. 27 | # "MATCH_PARENT" makes a view as large as the parent layout in that dimension. 28 | 29 | # Since the TextViews are displayed in a list, we set the height to "WRAP_CONTENT". 30 | tv1.setheight(tg.View.WRAP_CONTENT) 31 | tv2.setheight(tg.View.WRAP_CONTENT) 32 | buttons.setheight(tg.View.WRAP_CONTENT) 33 | tv3.setheight(tg.View.WRAP_CONTENT) 34 | 35 | bt1 = tg.Button(a, "Button1", buttons) 36 | bt2 = tg.Button(a, "Button2", buttons) 37 | bt3 = tg.Button(a, "Button3", buttons) 38 | 39 | time.sleep(5) 40 | -------------------------------------------------------------------------------- /tutorial_oop/helloworld.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Some classes like RemoteViews and Buffer are still required from termuxgui, so import both 4 | import termuxgui.oo as tgo 5 | import termuxgui as tg 6 | 7 | 8 | # Layouts can be defined as classes, to make them reusable 9 | class HelloWorldLayout(tgo.LinearLayout): 10 | def __init__(self, activity: tgo.Activity): 11 | # Initialize the LinearLayout as the root Layout 12 | super().__init__(activity) 13 | # define a TextView 14 | tgo.TextView(self.a, "Hello World", self) 15 | # define a Button 16 | b = tgo.Button(self.a, "Click me!", self) 17 | # Also new is the event system: 18 | # You can provide callables as on_* methods which will be called 19 | # if the corresponding Event is triggered on the View. 20 | # The methods get the event and the View as parameters. 21 | b.on_click = lambda e, v: print(e.type, e.value, v.id) 22 | 23 | 24 | # Activities are now classes 25 | class HelloWorldActivity(tgo.Activity): 26 | 27 | def __init__(self, c, t): 28 | super().__init__(c, t) 29 | # Add the Layout to the Activity 30 | HelloWorldLayout(self) 31 | 32 | 33 | with tgo.Connection() as c: 34 | # Activities aren't created directly with the constructor anymore, but with the launch method of the connection 35 | c.launch(HelloWorldActivity) 36 | 37 | # The event loop is build into the connection. 38 | # It automatically quits if there aren't any Activities left. 39 | c.event_loop() 40 | -------------------------------------------------------------------------------- /src/termuxgui/oo/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | __all__ = ["Activity", "Button", "Checkbox", "Connection", "EditText", "FrameLayout", "GridLayout", 3 | "HorizontalScrollView", "ImageView", "LinearLayout", "NestedScrollView","ProgressBar", 4 | "RadioButton", "RadioGroup", "Space", "Spinner","SwipeRefreshLayout", "Switch", 5 | "TabLayout", "TextView", "ToggleButton", "View", "ViewGroup", "WebView"] 6 | 7 | from termuxgui.oo.activity import Activity 8 | from termuxgui.oo.button import Button 9 | from termuxgui.oo.checkbox import Checkbox 10 | from termuxgui.oo.connection import Connection 11 | from termuxgui.oo.edittext import EditText 12 | from termuxgui.oo.framelayout import FrameLayout 13 | from termuxgui.oo.gridlayout import GridLayout 14 | from termuxgui.oo.horizontalscrollview import HorizontalScrollView 15 | from termuxgui.oo.imageview import ImageView 16 | from termuxgui.oo.linearlayout import LinearLayout 17 | from termuxgui.oo.nestedscrollview import NestedScrollView 18 | from termuxgui.oo.progressbar import ProgressBar 19 | from termuxgui.oo.radiobutton import RadioButton 20 | from termuxgui.oo.radiogroup import RadioGroup 21 | from termuxgui.oo.space import Space 22 | from termuxgui.oo.spinner import Spinner 23 | from termuxgui.oo.swiperefreshlayout import SwipeRefreshLayout 24 | from termuxgui.oo.switch import Switch 25 | from termuxgui.oo.tablayout import TabLayout 26 | from termuxgui.oo.textview import TextView 27 | from termuxgui.oo.togglebutton import ToggleButton 28 | from termuxgui.oo.view import View 29 | from termuxgui.oo.viewgroup import ViewGroup 30 | from termuxgui.oo.webview import WebView 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/termuxgui/msg.py: -------------------------------------------------------------------------------- 1 | from json import loads 2 | import array 3 | import socket 4 | from typing import Any, Tuple, Optional 5 | 6 | 7 | def read_msg(s: socket.socket) -> Any: 8 | b = b'' 9 | togo = 4 10 | while togo > 0: 11 | read = s.recv(togo) 12 | b = b + read 13 | togo = togo - len(read) 14 | togo = int.from_bytes(b, "big") 15 | b = b'' 16 | while togo > 0: 17 | read = s.recv(togo) 18 | b = b + read 19 | togo = togo - len(read) 20 | return loads(b.decode("utf-8")) 21 | 22 | 23 | def send_msg(c: socket.socket, msg: str): 24 | m = bytes(msg, "utf-8") 25 | c.sendall((len(m)).to_bytes(4, "big")) 26 | c.sendall(m) 27 | 28 | 29 | def send_read_msg(s: socket.socket, msg: str) -> Any: 30 | send_msg(s, msg) 31 | return read_msg(s) 32 | 33 | 34 | def read_msg_fd(s: socket.socket) -> Tuple[Any, Optional[int]]: 35 | b = b'' 36 | togo = 4 37 | while togo > 0: 38 | read = s.recv(togo) 39 | b = b + read 40 | togo = togo - len(read) 41 | togo = int.from_bytes(b, "big") 42 | b = b'' 43 | fds = array.array("i") 44 | while togo > 0: 45 | read, ancdata, _, _ = s.recvmsg(togo, socket.CMSG_LEN(fds.itemsize)) 46 | for cmsg_level, cmsg_type, cmsg_data in ancdata: 47 | if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: 48 | fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) 49 | b = b + read 50 | togo = togo - len(read) 51 | if len(fds) != 0: 52 | return loads(b.decode("utf-8")), fds[0] 53 | else: 54 | return loads(b.decode("utf-8")), None 55 | -------------------------------------------------------------------------------- /tutorial/hellolayout.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import time 5 | 6 | with tg.Connection() as c: 7 | a = tg.Activity(c) 8 | 9 | # For each View or layout you create you can specify the parent Layout to create a hierarchy. 10 | # If you don't specify a parent, it will replace the current root View. 11 | # We first create a LinearLayout as our root View. 12 | root = tg.LinearLayout(a) 13 | 14 | # Then we create a TextView that we will use as a title 15 | title = tg.TextView(a, "Awesome Title", root) 16 | 17 | # We set the font size a bit bigger 18 | title.settextsize(30) 19 | 20 | contenttext = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor " \ 21 | "invidunt utlabore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et " \ 22 | "accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata " \ 23 | "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur " \ 24 | "sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore " \ 25 | "magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo " \ 26 | "dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est " \ 27 | "Lorem ipsum dolor sit amet." 28 | # Now we create a TextView for the main content 29 | content = tg.TextView(a, contenttext, root) 30 | 31 | # And we add a Button at the end 32 | button = tg.Button(a, "Click here!", root) 33 | 34 | # Now we give the Layout priority to our content Textview, so it is bigger than the Button and the title. 35 | content.setlinearlayoutparams(10) 36 | 37 | time.sleep(5) 38 | -------------------------------------------------------------------------------- /src/termuxgui/buffer.py: -------------------------------------------------------------------------------- 1 | from mmap import mmap 2 | import os 3 | from typing import Literal 4 | 5 | import termuxgui.msg as msg 6 | 7 | 8 | class Buffer: 9 | """This represents a raw ARGB888 image buffer. 10 | 11 | This is only available on Android 8.1+. 12 | Buffers consume much memory, so be sure to close any Buffer you don't need any more. 13 | Preferably use "with" to get the memory object of the buffer so it is automatically closed. 14 | The memory object is a shared memory object that can be used like a bytes object or a file object. 15 | You can also directly access the memory object as b.mem. 16 | 17 | A RuntimeError is raised when a Buffer can't be created.""" 18 | 19 | def __init__(self, connection, w: int, h: int, format: Literal["ARGB888"] = "ARGB888"): 20 | self.c = connection 21 | self.c.send_msg({"method": "addBuffer", "params": {"w": w, "h": h, "format": format}}) 22 | ret = msg.read_msg_fd(self.c._main) 23 | if len(ret) == 1: 24 | raise RuntimeError("Could not create Buffer") 25 | self.bid, self.fd = ret 26 | self.mem = mmap(self.fd, w * h * 4) 27 | 28 | def __enter__(self): 29 | return self.mem 30 | 31 | def __exit__(self, type, value, traceback): 32 | self.remove() 33 | return False 34 | 35 | def remove(self): 36 | """Removes this buffer, freeing the memory.""" 37 | self.c.send_msg({"method": "deleteBuffer", "params": {"bid": self.bid}}) 38 | self.mem.close() 39 | os.close(self.fd) 40 | 41 | def blit(self): 42 | """Blits the buffer to the underlying image in the plugin. 43 | To update ImageViews using this buffer, they have to be refreshed afterwards.""" 44 | self.mem.flush() 45 | self.c.send_msg({"method": "blitBuffer", "params": {"bid": self.bid}}) 46 | -------------------------------------------------------------------------------- /src/termuxgui/oo/activity.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import termuxgui as tg 4 | 5 | from abc import ABC 6 | from enum import Enum 7 | 8 | 9 | class Activity(tg.Activity, ABC): 10 | """Abstract base class for Activities.""" 11 | 12 | class Type(Enum): 13 | NORMAL = 1 14 | PIP = 2 15 | LOCKSCREEN = 3 16 | DIALOG = 4 17 | DIALOG_NO_CANCEL_OUTSIDE = 5 18 | 19 | def get_type(self) -> Type: 20 | """Return the type of your Activity here.""" 21 | return Activity.Type.NORMAL 22 | 23 | def intercept_back(self) -> bool: 24 | """Specify if you want to intercept back events.""" 25 | return False 26 | 27 | def __init__(self, c: tg.Connection, t: Optional[tg.Task]): 28 | args = {"intercept": self.intercept_back()} 29 | match self.get_type(): 30 | case Activity.Type.PIP: 31 | args["pip"] = True 32 | case Activity.Type.DIALOG: 33 | args["dialog"] = True 34 | case Activity.Type.DIALOG_NO_CANCEL_OUTSIDE: 35 | args["dialog"] = True 36 | args["canceloutside"] = False 37 | if t is not None: 38 | args["tid"] = t.tid 39 | tg.Activity.__init__(self, c, **args) 40 | self.root = None 41 | 42 | def on_create(self): 43 | pass 44 | 45 | def on_start(self): 46 | pass 47 | 48 | def on_resume(self): 49 | pass 50 | 51 | def on_pause(self, finishing: bool): 52 | pass 53 | 54 | def on_stop(self, finishing: bool): 55 | pass 56 | 57 | def on_destroy(self, finishing: bool): 58 | pass 59 | 60 | def on_back(self): 61 | pass 62 | 63 | def on_userleavehint(self): 64 | pass 65 | 66 | def on_pipchanged(self, pip: bool): 67 | pass 68 | 69 | def on_config(self, config: tg.activity.Configuration): 70 | pass 71 | -------------------------------------------------------------------------------- /tutorial/swiperefresh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import sys 5 | 6 | with tg.Connection() as c: 7 | a = tg.Activity(c) 8 | 9 | # Intercept the back button. Instead of finishing the Activity, it now sends an event instead 10 | a.interceptbackbutton(True) 11 | # Also possible: a = tg.Activity(c, intercept=True) 12 | 13 | # Create a SwipeRefreshLayout as the root View 14 | ref = tg.SwipeRefreshLayout(a) 15 | 16 | # Add a LinearLayout as a child. A SwipeRefreshLayout can only have one child, so you should use a layout. 17 | layout = tg.LinearLayout(a, ref) 18 | 19 | refreshes = 0 20 | clicks = 0 21 | longclicks = 0 22 | 23 | tv1 = tg.TextView(a, "Refresh: " + str(refreshes), layout) 24 | tv2 = tg.TextView(a, "Clicks: " + str(clicks), layout) 25 | tv3 = tg.TextView(a, "Long clicks: " + str(longclicks), layout) 26 | 27 | # You can make other Views than Buttons send click events 28 | tv2.sendclickevent(True) 29 | 30 | # A long click event happens when you click a View and hold for a while 31 | tv3.sendlongclickevent(True) 32 | 33 | for ev in c.events(): 34 | if ev.type == tg.Event.destroy and ev.value["finishing"]: 35 | sys.exit() 36 | if ev.type == tg.Event.click: 37 | clicks += 1 38 | tv2.settext("Clicks: " + str(clicks)) 39 | if ev.type == tg.Event.longClick: 40 | longclicks += 1 41 | tv3.settext("Long clicks: " + str(longclicks)) 42 | if ev.type == tg.Event.refresh: 43 | refreshes += 1 44 | tv1.settext("Refresh: " + str(refreshes)) 45 | # Set that we are done with refreshing, so that the animation stops and the gesture can trigger again 46 | ref.setrefreshing(False) 47 | # Back button presses generate an event now 48 | if ev.type == tg.Event.back: 49 | c.toast("User pressed the back button", True) 50 | -------------------------------------------------------------------------------- /src/termuxgui/__init__.py: -------------------------------------------------------------------------------- 1 | """This is a library for interacting with the Termux:GUI plugin from python. 2 | 3 | You don't need to include all the submodules, all needed classes are automatically included in the package scope upon 4 | importing the package. """ 5 | 6 | __all__ = ['Activity', 'Buffer', 'Button', 'Checkbox', 'CompoundButton', 'Connection', 'EditText', 'Event', 7 | 'FrameLayout', "GridLayout", 'HorizontalScrollView', 'ImageView', 'LinearLayout', 'NestedScrollView', 8 | 'Notification', 'ProgressBar', 'RadioButton', 'RadioGroup', 'RemoteViews', 'Space', 'Spinner', 9 | 'SwipeRefreshLayout', 'Switch', 'TabLayout', 'Task', 'TextView', 'ToggleButton', 'View', 'ViewGroup', 10 | 'WebView'] 11 | 12 | from termuxgui.activity import Activity 13 | from termuxgui.buffer import Buffer 14 | from termuxgui.button import Button 15 | from termuxgui.checkbox import Checkbox 16 | from termuxgui.compoundbutton import CompoundButton 17 | from termuxgui.connection import Connection 18 | from termuxgui.edittext import EditText 19 | from termuxgui.event import Event 20 | from termuxgui.framelayout import FrameLayout 21 | from termuxgui.gridlayout import GridLayout 22 | from termuxgui.horizontalscrollview import HorizontalScrollView 23 | from termuxgui.imageview import ImageView 24 | from termuxgui.linearlayout import LinearLayout 25 | from termuxgui.nestedscrollview import NestedScrollView 26 | from termuxgui.notification import Notification 27 | from termuxgui.progressbar import ProgressBar 28 | from termuxgui.radiobutton import RadioButton 29 | from termuxgui.radiogroup import RadioGroup 30 | from termuxgui.remoteviews import RemoteViews 31 | from termuxgui.space import Space 32 | from termuxgui.spinner import Spinner 33 | from termuxgui.swiperefreshlayout import SwipeRefreshLayout 34 | from termuxgui.switch import Switch 35 | from termuxgui.tablayout import TabLayout 36 | from termuxgui.task import Task 37 | from termuxgui.textview import TextView 38 | from termuxgui.togglebutton import ToggleButton 39 | from termuxgui.view import View 40 | from termuxgui.viewgroup import ViewGroup 41 | from termuxgui.webview import WebView 42 | 43 | -------------------------------------------------------------------------------- /src/termuxgui/event.py: -------------------------------------------------------------------------------- 1 | class Event: 2 | """This represents an Event in the GUI. 3 | 4 | The class variables are the available event types. 5 | 6 | Use ev.type == Event.eventtype to check for event types. 7 | 8 | ev.type contains the event type and ev.value is a dictionary containing the values of the event, if any.""" 9 | 10 | # Event types 11 | # View events 12 | click = "click" 13 | longClick = "longClick" 14 | focusChange = "focusChange" 15 | key = "key" 16 | touch = "touch" 17 | refresh = "refresh" 18 | selected = "selected" # used for RadioGroups 19 | itemselected = "itemselected" 20 | text = "text" 21 | notification = "notification" 22 | notificationDismissed = "notificationDismissed" 23 | notificationaction = "notificationaction" 24 | webviewNavigation = "webviewNavigation" 25 | webviewHTTPError = "webviewHTTPError" 26 | webviewError = "webviewError" 27 | webviewDestroyed = "webviewDestroyed" 28 | webviewProgress = "webviewProgress" 29 | webviewConsoleMessage = "webviewConsoleMessage" 30 | remoteclick = "remoteclick" 31 | 32 | # activity events 33 | create = "create" 34 | start = "start" 35 | resume = "resume" 36 | pause = "pause" 37 | stop = "stop" 38 | destroy = "destroy" 39 | userleavehint = "UserLeaveHint" 40 | pipchanged = "pipchanged" 41 | config = "config" 42 | back = "back" 43 | 44 | # general events 45 | screenon = "screen_on" 46 | screen_off = "screen_off" 47 | timezone = "timezone" 48 | locale = "locale" 49 | airplane = "airplane" 50 | 51 | # special overlay events 52 | overlaytouch = "overlayTouch" 53 | overlayScale = "overlayScale" 54 | 55 | # touch event action 56 | touch_up = "up" 57 | touch_down = "down" 58 | touch_pointer_up = "pointer_up" 59 | touch_pointer_down = "pointer_down" 60 | touch_cancel = "cancel" 61 | touch_move = "move" 62 | 63 | def __init__(self, ev: dict): 64 | self.type = ev["type"] 65 | self.value = {} 66 | try: 67 | self.value = ev["value"] 68 | if type(self.value) is dict: 69 | self.aid = self.value["aid"] 70 | self.id = self.value["id"] 71 | except KeyError: 72 | pass 73 | -------------------------------------------------------------------------------- /tutorial/inputs2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import sys 5 | 6 | with tg.Connection() as c: 7 | a = tg.Activity(c, dialog=True) 8 | 9 | layout = tg.LinearLayout(a) 10 | 11 | title = tg.TextView(a, "Input Demo", layout) 12 | title.settextsize(30) 13 | title.setmargin(5) 14 | 15 | # Switches have a text displayed on the left and a switch that can be set on the right 16 | switch = tg.Switch(a, "Switch", layout) 17 | 18 | # ToggleButtons are more like Buttons, but have an on/off state, but you can't set the text 19 | tb = tg.ToggleButton(a, layout) 20 | 21 | # RadioGoups are containers for RadioButtons. 22 | # Inside a RadioGroup, only one RadioButton can be set. 23 | rg = tg.RadioGroup(a, layout) 24 | 25 | # The RadioButtons are created in a list, so you can check which of them is checked more easily 26 | rbs = [tg.RadioButton(a, "Radio 1", rg), tg.RadioButton(a, "Radio 2", rg), tg.RadioButton(a, "Radio 3", rg)] 27 | 28 | # Spinners display a drop-down menu with strings to choose from 29 | spinner = tg.Spinner(a, layout) 30 | 31 | strings = ["Option 1", "Option 2", "Option 3"] 32 | 33 | # You can set the list of values to choose from 34 | spinner.setlist(strings) 35 | 36 | for ev in c.events(): 37 | if ev.type == tg.Event.destroy and ev.value["finishing"]: 38 | sys.exit() 39 | # Checked events work the same for Switches, ToggleButtons and Checkboxes 40 | if ev.type == tg.Event.click and ev.value["id"] == switch: 41 | print("Switch checked: ", ev.value["set"]) 42 | if ev.type == tg.Event.click and ev.value["id"] == tb: 43 | print("ToggleButton checked: ", ev.value["set"]) 44 | # A RadioGroup emits a selected event when a RadioButton is selected 45 | if ev.type == tg.Event.selected and ev.value["id"] == rg: 46 | # We can now use the index method of the list to find out which RadioButton is checked 47 | print("RadioButton checked: ", rbs.index(ev.value["selected"])) 48 | # Spinners emit an itemselected event 49 | if ev.type == tg.Event.itemselected and ev.value["id"] == spinner: 50 | # for itemselected events, selected is the selected value as a string 51 | print("Spinner selected: ", ev.value["selected"]) 52 | -------------------------------------------------------------------------------- /src/termuxgui/textview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.view import View 5 | 6 | 7 | class TextView(View): 8 | """This represents a TextView.""" 9 | 10 | def __init__(self, activity: Activity, text: str, parent: Optional[View] = None, selectabletext: bool = False, 11 | clickablelinks: bool = False, visibility: Optional[Literal[0, 1, 2]] = None): 12 | args = {"aid": activity.aid, "text": text, "selectableText": selectabletext, "clickableLinks": clickablelinks, 13 | "visibility": visibility} 14 | if parent is not None: 15 | args["parent"] = parent.id 16 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createTextView", "params": args})) 17 | 18 | def settextsize(self, size: int): 19 | """Sets the text size for this TextView.""" 20 | self.a.c.send_msg({"method": "setTextSize", "params": {"aid": self.a.aid, "id": self.id, "size": size}}) 21 | 22 | def gettext(self) -> str: 23 | """Gets the text of this TextView.""" 24 | return self.a.c.send_read_msg({"method": "getText", "params": {"aid": self.a.aid, "id": self.id}}) 25 | 26 | def settext(self, text: str): 27 | """Sets the text of this TextView.""" 28 | self.a.c.send_msg({"method": "setText", "params": {"aid": self.a.aid, "id": self.id, "text": text}}) 29 | 30 | def settextcolor(self, color: int): 31 | """Sets the text color of this TextView. The color format is the same as for Activity.settheme().""" 32 | self.a.c.send_msg({"method": "setTextColor", "params": {"aid": self.a.aid, "id": self.id, "color": color}}) 33 | 34 | def sendtextevent(self, send: bool): 35 | """Sets whether ot not text events are send for this TextView.""" 36 | self.a.c.send_msg({"method": "sendTextEvent", "params": {"aid": self.a.aid, "id": self.id, "send": send}}) 37 | 38 | def setgravity(self, horizontal: Literal[0, 1, 2], vertical: Literal[0, 1, 2]): 39 | """Sets the text gravity for this TextView. 40 | The values are: 0: left/top, 1: center, 2: right/bottom. 41 | Right and left are inverted for right-to-left layouts.""" 42 | self.a.c.send_msg({"method": "setGravity", "params": {"aid": self.a.aid, "id": self.id, 43 | "horizontal": horizontal, "vertical": vertical}}) 44 | -------------------------------------------------------------------------------- /tutorial/inputs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import sys 5 | from subprocess import run 6 | 7 | with tg.Connection() as c: 8 | a = tg.Activity(c, dialog=True) # make this activity a dialog 9 | 10 | layout = tg.LinearLayout(a) 11 | 12 | title = tg.TextView(a, "Download Video", layout) 13 | title.settextsize(30) 14 | 15 | # Let's also create a small margin around the title so it looks nicer. 16 | title.setmargin(5) 17 | 18 | # For dialogs, we don't need to set "WRAP_CONTENT", in dialogs views are automatically packed as close as possible. 19 | 20 | tv1 = tg.TextView(a, "Video link:", layout) 21 | et1 = tg.EditText(a, "", layout) 22 | 23 | tv2 = tg.TextView(a, "Filename (empty for automatic filename):", layout) 24 | et2 = tg.EditText(a, "", layout) 25 | 26 | # This creates an unchecked Checkbox 27 | check = tg.Checkbox(a, "high quality", layout, False) 28 | 29 | # Create 2 buttons next to each other 30 | buttons = tg.LinearLayout(a, layout, False) 31 | 32 | dl = tg.Button(a, "download", buttons) 33 | cancel = tg.Button(a, "cancel", buttons) 34 | 35 | hd = False 36 | 37 | for ev in c.events(): 38 | if ev.type == tg.Event.destroy and ev.value["finishing"]: 39 | sys.exit() 40 | # Checkboxes also emit a click event when clicked, but they have the extra value "set" indicating whether the box is now checked or unchecked. 41 | # The id of the event is the View id. Comparing View object with view ids is supported and works as expected. 42 | if ev.type == tg.Event.click and ev.value["id"] == check: 43 | hd = ev.value["set"] 44 | if ev.type == tg.Event.click and ev.value["id"] == dl: 45 | link = et1.gettext() 46 | name = et2.gettext() 47 | args = ["youtubedr", "download"] 48 | if len(name) != 0: 49 | args.extend(["-o", name]) 50 | if hd: 51 | args.extend(["-q", "1080p"]) 52 | args.append(link) 53 | if len(link) != 0: 54 | try: 55 | a.finish() 56 | run(args) 57 | except: 58 | pass 59 | if ev.type == tg.Event.click and ev.value["id"] == cancel: 60 | a.finish() # this handily also exits the program, because finishing the activity destroys it, and that event is send to us 61 | -------------------------------------------------------------------------------- /tutorial/helloevents.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import time 5 | import threading 6 | 7 | with tg.Connection() as c: 8 | a = tg.Activity(c) 9 | 10 | # For each View or Layout you create you can specify the parent Layout to create a hierarchy. 11 | # If you don't specify a parent, it will replace the current root View. 12 | # We first create a LinearLayout as our root View. 13 | root = tg.LinearLayout(a) 14 | 15 | # Then we create a TextView that we will use as a title 16 | title = tg.TextView(a, "Awesome Title", root) 17 | 18 | # We set the font size a bit bigger 19 | title.settextsize(30) 20 | 21 | contenttext = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod " \ 22 | "tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At " \ 23 | "vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, " \ 24 | "no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit " \ 25 | "amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut " \ 26 | "labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam " \ 27 | "et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata " \ 28 | "sanctus est Lorem ipsum dolor sit amet." 29 | # Now we create a TextView for the main content 30 | content = tg.TextView(a, contenttext, root) 31 | 32 | # And we add a Button at the end 33 | button = tg.Button(a, "Click here!", root) 34 | 35 | # Now we give the layout priority to our content Textview so it is bigger than the Button and the title. 36 | content.setlinearlayoutparams(10) 37 | 38 | count = 0 39 | 40 | # create a thread to handle the events, so we can still exit the program after 5 seconds no matter what 41 | def handleEvents(): 42 | global count 43 | for ev in c.events(): # waits for events from the gui 44 | # checks for click events. We don't need to check the id, as there is only one Button in our example 45 | if ev.type == tg.Event.click: 46 | count = count + 1 47 | 48 | 49 | watcher = threading.Thread(target=handleEvents, daemon=True) 50 | watcher.start() 51 | 52 | time.sleep(5) 53 | print("button pressed", count, "times") 54 | -------------------------------------------------------------------------------- /TUTORIAL_OOP.md: -------------------------------------------------------------------------------- 1 | # Python Bindings OOP Tutorial 2 | 3 | This is the tutorial for the object-oriented version of this library. 4 | Make sure you're familiar with the normal version a bit first, many things aren't explicitly repeated here. 5 | 6 | Make sure you installed the library like explained in the README. 7 | This tutorial assumes you have the basic understanding of the Android GUI from the [crash course](https://github.com/termux/termux-gui). 8 | The full source code can also be found in the tutorial_oop folder. 9 | 10 | ## Basic structure 11 | 12 | Activities are now defined as classes, Layouts can be defined as classes for reusability, 13 | the event loop is now build into the connection and with it comes an event system. 14 | You can also override the on_* methods of your Activity to catch Activity events. 15 | 16 | 17 | 18 | 19 | 20 | ```python 21 | # Some classes like RemoteViews and Buffer are still required from termuxgui, so import both 22 | import termuxgui.oo as tgo 23 | import termuxgui as tg 24 | 25 | 26 | # Layouts can be defined as classes, to make them reusable 27 | class HelloWorldLayout(tgo.LinearLayout): 28 | def __init__(self, activity: tgo.Activity): 29 | # Initialize the LinearLayout as the root Layout 30 | super().__init__(activity) 31 | # define a TextView 32 | tgo.TextView(self.a, "Hello World", self) 33 | # define a Button 34 | b = tgo.Button(self.a, "Click me!", self) 35 | # Also new is the event system: 36 | # You can provide callables as on_* methods which will be called 37 | # if the corresponding Event is triggered on the View. 38 | # The methods get the event and the View as parameters. 39 | b.on_click = lambda e, v: print(e.type, e.value, v.id) 40 | 41 | 42 | # Activities are now classes 43 | class HelloWorldActivity(tgo.Activity): 44 | 45 | def __init__(self, c, t): 46 | super().__init__(c, t) 47 | # Add the Layout to the Activity 48 | HelloWorldLayout(self) 49 | 50 | 51 | with tgo.Connection() as c: 52 | # Activities aren't created directly with the constructor anymore, but with the launch method of the connection 53 | c.launch(HelloWorldActivity) 54 | 55 | # The event loop is build into the connection. 56 | # It automatically quits if there aren't any Activities left. 57 | c.event_loop() 58 | ``` 59 | 60 | [helloworld.py](tutorial_oop/helloworld.py) 61 | 62 | 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # IntelliJ 132 | /.idea -------------------------------------------------------------------------------- /src/termuxgui/webview.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.activity import Activity 4 | from termuxgui.view import View 5 | 6 | 7 | class WebView(View): 8 | """This represents a WebView.""" 9 | 10 | def __init__(self, activity: Activity, parent: Optional[View] = None, 11 | visibility: Optional[Literal[0, 1, 2]] = None): 12 | args = {"aid": activity.aid, "visibility": visibility} 13 | if parent is not None: 14 | args["parent"] = parent.id 15 | View.__init__(self, activity, activity.c.send_read_msg({"method": "createWebView", "params": args})) 16 | 17 | def allowjavascript(self, allow: bool): 18 | """Sets whether Javascript execution is allowed in the WebView. 19 | If requesting to allow, the user is prompted and can deny the request. 20 | Blocks until the user responded. 21 | Returns wheter Javascript is enabled after the call.""" 22 | self.a.c.send_read_msg({"method": "allowJavascript", "params": {"aid": self.a.aid, "id": self.id, "allow": allow}}) 23 | 24 | def allowcontenturi(self, allow: bool): 25 | """Sets whether it is allowed to load content from a content:// URI.""" 26 | self.a.c.send_msg({"method": "allowContentURI", "params": {"aid": self.a.aid, "id": self.id, "allow": allow}}) 27 | 28 | def allownavigation(self, allow: bool): 29 | """Sets whether the user and Javascript are allowed to navigate to different sites.""" 30 | self.a.c.send_msg({"method": "allowNavigation", "params": {"aid": self.a.aid, "id": self.id, "allow": allow}}) 31 | 32 | def setdata(self, data: str): 33 | """Sets the document data.""" 34 | self.a.c.send_msg({"method": "setData", "params": {"aid": self.a.aid, "id": self.id, "doc": data}}) 35 | 36 | def loaduri(self, uri: str): 37 | """Loads a URI.""" 38 | self.a.c.send_msg({"method": "loadURI", "params": {"aid": self.a.aid, "id": self.id, "uri": uri}}) 39 | 40 | def goback(self): 41 | """Goes back in the history.""" 42 | self.a.c.send_msg({"method": "goBack", "params": {"aid": self.a.aid, "id": self.id}}) 43 | 44 | def goforward(self): 45 | """Goes forward in the history.""" 46 | self.a.c.send_msg({"method": "goForward", "params": {"aid": self.a.aid, "id": self.id}}) 47 | 48 | def evaluatejs(self, code: str): 49 | """Runs Javascript in the WebView, if Javascript is enabled.""" 50 | self.a.c.send_msg({"method": "evaluateJS", "params": {"aid": self.a.aid, "id": self.id, "code": code}}) 51 | -------------------------------------------------------------------------------- /tutorial/pagedlist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import termuxgui as tg 4 | import sys 5 | 6 | with tg.Connection() as c: 7 | a = tg.Activity(c) 8 | 9 | # set the amount of items on each page 10 | pagesize = 20 11 | 12 | # generate content 13 | pagedcontent = [] 14 | for i in range(1, 201): 15 | pagedcontent.append(f'Item {i}') 16 | 17 | page = 0 18 | 19 | # the paged list will be contained in this layout, and can be placed anywhere in the layout 20 | pagedlayout = tg.LinearLayout(a) 21 | 22 | # construct the controls at the top 23 | pagedcontrols = tg.LinearLayout(a, pagedlayout, vertical=False) 24 | pagedcontrols.setlinearlayoutparams(0) 25 | pagedcontrols.setheight(tg.View.WRAP_CONTENT) 26 | 27 | # buttons to go forward and back, and a display 28 | pagedback = tg.Button(a, "-", pagedcontrols) 29 | pagedpageview = tg.TextView(a, "1/1", pagedcontrols) 30 | pagedforward = tg.Button(a, "+", pagedcontrols) 31 | 32 | pagedback.setwidth(tg.View.WRAP_CONTENT) 33 | pagedforward.setwidth(tg.View.WRAP_CONTENT) 34 | 35 | pagedpageview.setmargin(5, "left") 36 | pagedpageview.setmargin(5, "right") 37 | pagedpageview.setgravity(1, 1) # Center the text 38 | 39 | # make the NestedScrollView for the content 40 | pagedscrollview = tg.NestedScrollView(a, pagedlayout) 41 | pagedscrollviewlayout = tg.LinearLayout(a, pagedscrollview) 42 | 43 | # Generate the Views for the content ahead of time, so the transition between pages 44 | # takes less time, and the user doesn't see a black flicker while the Views are removed 45 | pagedviews = [] 46 | for i in range(0, pagesize): 47 | pagedviews.append(tg.TextView(a, "", pagedscrollviewlayout)) 48 | pagedviews[i].settextsize(25) 49 | 50 | def topage(p: int): 51 | global page 52 | page = p 53 | # update the page indicator 54 | pagedpageview.settext(f"{p+1}/{int(len(pagedcontent)/pagesize)}") 55 | # set the new content 56 | for i in range(0, pagesize): 57 | pagedviews[i].settext(pagedcontent[p*pagesize+i]) 58 | 59 | topage(0) 60 | 61 | for ev in c.events(): 62 | if ev.type == tg.Event.destroy and ev.value["finishing"]: 63 | sys.exit() 64 | if ev.type == tg.Event.click: 65 | if ev.value["id"] == pagedback and page > 0: 66 | topage(page-1) 67 | if ev.value["id"] == pagedforward and page < (len(pagedcontent)/pagesize)-1: 68 | topage(page+1) 69 | -------------------------------------------------------------------------------- /tutorial/pages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import termuxgui as tg 5 | import time 6 | import threading 7 | 8 | with tg.Connection() as c: 9 | a = tg.Activity(c) 10 | 11 | root = tg.LinearLayout(a) 12 | 13 | # Create the TabLayout first, so it's at the top 14 | tabs = tg.TabLayout(a, root) 15 | 16 | # Set the tabs 17 | tabs.setlist(["Page 1", "Page 2", "Page 3"]) 18 | 19 | tabs.setlinearlayoutparams(0) 20 | tabs.setheight(tg.View.WRAP_CONTENT) 21 | 22 | # Create a HorizontalScrollView without a visible scrollbar that snaps to the nearest item 23 | sv = tg.HorizontalScrollView(a, root, fillviewport=True, snapping=True, nobar=True) 24 | 25 | # wait until the activity is displayed and teh size is known 26 | while sv.getdimensions()[0] == 0: 27 | time.sleep(0.001) 28 | 29 | # Get the width one page should have 30 | pagew = sv.getdimensions()[0] 31 | 32 | # Create a horizontal layout 33 | svl = tg.LinearLayout(a, sv, vertical=False) 34 | 35 | # Create 3 pages 36 | page1 = tg.TextView(a, "Page 1", svl) 37 | page2 = tg.TextView(a, "Page 2", svl) 38 | page3 = tg.TextView(a, "Page 3", svl) 39 | 40 | # Set the width to one page-width 41 | page1.setwidth(pagew, True) 42 | page2.setwidth(pagew, True) 43 | page3.setwidth(pagew, True) 44 | 45 | # access to the gui objects has to be synchronized between threads 46 | conlcok = threading.Lock() 47 | 48 | tabselected = False 49 | tab = 0 50 | 51 | # create a thread to watch the scroll position and select the tab accordingly 52 | def watchscrollview(): 53 | global tabselected 54 | while True: 55 | with conlcok: 56 | p = round(sv.getscrollposition()[0]/pagew) 57 | if not tabselected: 58 | if p != tab: 59 | tabs.selecttab(p) 60 | tabselected = True 61 | elif p == tab: 62 | tabselected = False 63 | time.sleep(0.01) 64 | threading.Thread(target=watchscrollview, daemon=True).start() 65 | 66 | # getting events needs no synchronisation... 67 | for ev in c.events(): 68 | if ev.type == tg.Event.destroy: 69 | sys.exit() 70 | # TabLayout also emits an itemselected event, but has the tab index as the value 71 | if ev.type == tg.Event.itemselected and ev.value["id"] == tabs: 72 | # ...but calling any other method does 73 | with conlcok: 74 | tabselected = True 75 | tab = ev.value["selected"] 76 | # Scroll to the selected tab 77 | sv.setscrollposition(pagew*tab, 0, True) 78 | -------------------------------------------------------------------------------- /tutorial/buffers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time 4 | from ctypes import * 5 | from sdl2 import * 6 | import sdl2.ext 7 | import gc 8 | 9 | import termuxgui as tg 10 | 11 | with tg.Connection() as c: 12 | a = tg.Activity(c) 13 | time.sleep(0.5) # wait for the activity to show 14 | 15 | im = tg.ImageView(a) 16 | 17 | # This create a pixel buffer with size 500x500 18 | b = tg.Buffer(c, 500, 500) 19 | 20 | # This sets the ImageView to display the buffer 21 | im.setbuffer(b) 22 | 23 | with b as mem: 24 | # This creates a c void pointer from the shared memory 25 | memp = cast(pointer(c_uint8.from_buffer(mem, 0)), c_void_p) 26 | 27 | # We initialize the video system of SDL 28 | SDL_Init(SDL_INIT_VIDEO) 29 | 30 | # Then we create a SDL surface from our buffer. We pass in the pointer to our buffer, the buffer width and 31 | # height, the bit depth (the format is always ARGB8888, so it's 32 bits), wthe pitch (bytes per row), 32 | # and the masks for the color values. The mask determines the format and is the same for all buffers 33 | surf = SDL_CreateRGBSurfaceFrom(memp, 500, 500, 32, 4 * 500, c_uint(0xff), c_uint(0xff00), c_uint(0xff0000), 34 | c_uint(0xff000000)) 35 | 36 | # in general you would write: 37 | # SDL_CreateRGBSurfaceFrom(memp, width, height, 32, 4*width, c_uint(0xff), c_uint(0xff00), c_uint(0xff0000), c_uint(0xff000000)) 38 | 39 | white = sdl2.ext.Color(255, 255, 255, 255) # Color in RGBA format 40 | red = sdl2.ext.Color(255, 0, 0, 255) 41 | green = sdl2.ext.Color(0, 255, 0, 255) 42 | blue = sdl2.ext.Color(0, 0, 255, 255) 43 | 44 | for i in range(490): 45 | # clear the buffer 46 | sdl2.ext.fill(surf, white) 47 | 48 | # make a red square go from the top left to the bottom right 49 | sdl2.ext.fill(surf, red, (i, i, 10, 10)) 50 | 51 | # make a green square go from the top right to the bottom left 52 | sdl2.ext.fill(surf, green, (490 - i, i, 10, 10)) 53 | 54 | # make a blue square go from the top middle to the bottom middle 55 | sdl2.ext.fill(surf, blue, (245, i, 10, 10)) 56 | 57 | # blit the shared memory buffer 58 | b.blit() 59 | # now we have to request that the ImageView redraws itself to reflect the new contents of the buffer 60 | im.refresh() 61 | 62 | time.sleep(0.01) 63 | 64 | # Make sure the shared memory can be closed 65 | del memp # free the pointer 66 | gc.collect() # let the garbage collector run, so the pointer is really discarded 67 | 68 | # do proper cleanup of SDL 69 | SDL_FreeSurface(surf) 70 | SDL_Quit() 71 | 72 | time.sleep(0.2) 73 | -------------------------------------------------------------------------------- /src/termuxgui/oo/connection.py: -------------------------------------------------------------------------------- 1 | from typing import Type, Optional 2 | 3 | import termuxgui as tg 4 | from termuxgui.oo.activity import Activity 5 | from termuxgui.oo.view import View 6 | 7 | 8 | def _dispatch_event_recursive(e: tg.Event, v: View) -> bool: 9 | if v.id == e.id: 10 | if hasattr(v, "on_"+e.type): 11 | getattr(v, "on_"+e.type)(e, v) 12 | return True 13 | if hasattr(v, "views"): 14 | views = getattr(v, "views") 15 | for child in views: 16 | ret = _dispatch_event_recursive(e, child) 17 | if ret: 18 | return True 19 | return False 20 | 21 | 22 | class Connection(tg.Connection): 23 | """This represents a connection to the Termux:GUI plugin and contains all functions that don't act on any 24 | particular View, Activity or Task. """ 25 | 26 | __activities: dict[str, Activity] = {} 27 | 28 | """Launches an Activity with the specified parameters.""" 29 | def launch(self, activity: Type[Activity], t: Optional[tg.Task] = None, *args, **kwargs): 30 | a = activity(self, t, *args, *kwargs) 31 | self.__activities[a.aid] = a 32 | 33 | """Runs the event loop. Dispatches events to Activities and Views. Exits if there aren't any Activities anymore.""" 34 | def event_loop(self): 35 | for e in self.events(): 36 | if hasattr(e, "aid"): 37 | a = self.__activities[e.aid] 38 | if a is not None: 39 | match [e.type]: 40 | case [tg.Event.create]: 41 | a.on_create() 42 | case [tg.Event.start]: 43 | a.on_start() 44 | case [tg.Event.resume]: 45 | a.on_resume() 46 | case [tg.Event.pause]: 47 | a.on_pause(e.value["finishing"]) 48 | case [tg.Event.stop]: 49 | a.on_stop(e.value["finishing"]) 50 | case [tg.Event.destroy]: 51 | a.on_destroy(e.value["finishing"]) 52 | del self.__activities[e.aid] 53 | case [tg.Event.back]: 54 | a.on_back() 55 | case [tg.Event.userleavehint]: 56 | a.on_userleavehint() 57 | case [tg.Event.pipchanged]: 58 | a.on_pipchanged(e.value) 59 | case [tg.Event.config]: 60 | a.on_config(e.value) 61 | case [tg.Event.click | tg.Event.longClick | tg.Event.focusChange | tg.Event.key | 62 | tg.Event.touch | tg.Event.refresh | tg.Event.selected | tg.Event.itemselected | 63 | tg.Event.text | tg.Event.webviewNavigation | tg.Event.webviewHTTPError | 64 | tg.Event.webviewError | tg.Event.webviewDestroyed | tg.Event.webviewProgress | 65 | tg.Event.webviewConsoleMessage]: 66 | if a.root is not None: 67 | _dispatch_event_recursive(e, a.root) 68 | case _: 69 | pass 70 | if len(self.__activities) == 0: 71 | return 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Search — termuxgui 0.1.5 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 38 | 39 |
40 |
41 |
42 |
43 | 44 |

Search

45 | 46 | 54 | 55 | 56 |

57 | Searching for multiple words only shows matches that contain 58 | all words. 59 |

60 | 61 | 62 |
63 | 64 | 65 | 66 |
67 | 68 | 69 | 70 |
71 | 72 |
73 | 74 | 75 |
76 |
77 |
78 |
79 | 83 |
84 |
85 | 98 | 102 | 103 | -------------------------------------------------------------------------------- /docs/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Python Module Index — termuxgui 0.1.5 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 |

Python Module Index

43 | 44 |
45 | t 46 |
47 | 48 | 49 | 50 | 52 | 53 | 55 | 58 | 59 | 60 | 63 |
 
51 | t
56 | termuxgui 57 |
    61 | termuxgui.oo 62 |
64 | 65 | 66 |
67 |
68 |
69 |
70 | 84 |
85 |
86 | 99 | 103 | 104 | -------------------------------------------------------------------------------- /tutorial/notify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import base64 4 | import sys 5 | import termuxgui as tg 6 | 7 | 8 | image = base64.standard_b64decode("""iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAFVUlEQVR4XtWaTUhtVRTHl1qZWpkg9mXhy8yejR6BRRMn0uRNUmgQcQfRoIiiSFQQIi1SauigDxokWvEcCBKiBBE9kohI571wpmaPNCuRlx/t1v/ss87Zd+/7sa/3nCMt+HGP96y91tpnr7U/zpWoeqlh6pibw89yYuqi7blJLXOT/WUotzH3MBdCcI3vCglswFZmUijwh5nnmY+Yb5lfmN+YP0Jwje9wDzrQRRtTMukIhl4ETzTHfMX8zagKQRu0hY0misX0kZhInsv1S8zPlB/Qv8ypJ9A128IWbEpNYDQSqw+zMB9nfqDYMYI5Dj/tJ1yOQm1hGz5EfCaFkmIaeJ05odi5XCcBbElHcA1fImfuhNnwA4qd4anZKZAEsAnb8jeKXaTiTpizweekDUr+2o6TxvTzBcW14N0Js3g+JNdoFpj+PqY4Jq/Cljn+NXKNZYnp9w3SYq8/jsgc3Ev5BWsbzwqzsJ8kLUXXCTPvZao0i+q8kBh+pDjGgiu2DM+LpBug92nMNpUiCySuXyEtTipJjxopXmGdeb6hoUGNjY2p1dXVgMXFRdXW1mY7TAOJ5RpzB2nJGwXpUY60YtG87+zsVDs7O+r09DRgenpaNTY2OnopIDG9QFqiUTCnJmyuoFQy9wcHB9Xu7m7QgcPDQzUyMuLopIDE9DXFTz+IXRaILop3lUVHANTX16upqSl1dHQUdGJra0v19fWpmpoaRzdBJCbE+ChpCWKXaQlDAwWvwm1paVErKytRKq2tran29nZHL2EktpdJSxC7jMAn4c2ST9+kq6tLbWxsBB04OTlRs7Ozqrm52dFLEIlthvK3+MEf31lKXuRyObW3txd04uDgQA0PD6eZShLb92RNpThd4chnKnlRV1enJiYmolTa399X/f39aXVCYttgWsiQ+5gdS8mb1tZWtbS0pI6Pj6N66O7udvQSQGLDGbuDDOlk9sKbFXcA9PT0qM3NzWgkZmZmVFNTk6NXJRIbXhQ8QoZgCv2/dUCm0kAeID0sppI355BC10lnTSQoiDMX8fj4+HkUcRsZUs+sWkpe2NMothUpBQ8kNmz3sfEMBPMp+NRSKou9kM3NzWW1kH1GehUO1gJcYCHDXhs3sVyX3U5gK7G8vBylzvr6etpbCTOuIdIx520lLjF/hgolR8HezG1vb2e5mfuLeYK0BCMg22msxt+EShVtp0dHRx0dk97eXjU/P+/F0NCQ0z5EYrrKNJOW6CggdfBqqFQ0jc5yoBkYGIj0y7GwsOC0Jzd9JN5I5IBwP+ljGxQTO1IivaRNOSYnJ532FMeCqb6DtDgHe/ToVmaMtHLRUcgYM463SMfoHOoh0iP8mvIT6QYlayEjJIZ1pp20FH3NiJ5hUXua+Yd0QyeVMkR8I5ZnSMdW9MWWCDpxJ/Mu6cYYvpLTakrAp6TOe6RjKpg6tshR7V7K/s20YPq7QjoWxOT1cheCesBQXWC+pNhwFulk+lhiHiT/n3DzBA2Qc9i24j29GM3qBw48+YdIx+CVOoVEirqjtrYWeXiD8p0l0RHb1g329T7p+b6q4EWQTreQzsNnKZ5iTedmwfkgE4P9EGD7OdK+4LPitCkmKB4YxMHnIj+htylesU2k+Mpht7vGNt/hzx7SPuDLu2ArERQThvVu5hI7xaqNzdU+uUGVA22uhjawE4ZNr3m+WpF9eANzF9PNXOZA3iRdeDgtYc/yK/N7CK7xHe5dCXUvk36zABuwBZvO/iZNgTMUGBzjnT0C6WAuMo8xfcxTIbjGd7gHHeiijZyqMg3cFowIgkDeAhkd/P/D7SG4lqcsOmhTdZ7/B+8oI+no0VGgAAAAAElFTkSuQmCC""") 9 | 10 | with tg.Connection() as c: 11 | a = tg.Activity(c) 12 | 13 | root = tg.LinearLayout(a) 14 | 15 | normalb = tg.Button(a, "Normal notification", root) 16 | expandb = tg.Button(a, "Expandable notification", root) 17 | imgb = tg.Button(a, "Image notification", root) 18 | customb = tg.Button(a, "Custom notification", root) 19 | 20 | rv = tg.RemoteViews(c) 21 | cb = rv.addButton() 22 | rv.setText(cb, "Button") 23 | 24 | for ev in c.events(): 25 | if ev.type == tg.Event.destroy: 26 | sys.exit() 27 | if ev.type == tg.Event.click: 28 | id = ev.value["id"] 29 | if id == normalb: 30 | # Create a new high priority notification in the channel "test" 31 | n = tg.Notification(c, "test", 3) 32 | # Set the text 33 | n.settitle("normal") 34 | n.setcontent("A normal text notification") 35 | # display it 36 | n.notify() 37 | if id == expandb: 38 | n = tg.Notification(c, "test", 3) 39 | n.settitle("expandable") 40 | # sets the expanded text 41 | n.setlargetext("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") 42 | n.notify() 43 | if id == imgb: 44 | n = tg.Notification(c, "test", 3) 45 | n.settitle("image") 46 | n.setcontent("expand to see the image") 47 | # set the image, and display it as a thumbnail when the notification is collapsed 48 | n.setlargeimageasthumbnail(True) 49 | n.setlargeimage(image) 50 | n.notify() 51 | if id == customb: 52 | n = tg.Notification(c, "test", 3) 53 | # Set a custom layout for the notification 54 | n.setlayout(rv) 55 | n.notify() 56 | # catch the event from the button in the custom notification 57 | if ev.type == tg.Event.remoteclick: 58 | c.toast("notification button clicked", True) 59 | -------------------------------------------------------------------------------- /docs/_static/_sphinx_javascript_frameworks_compat.js: -------------------------------------------------------------------------------- 1 | /* 2 | * _sphinx_javascript_frameworks_compat.js 3 | * ~~~~~~~~~~ 4 | * 5 | * Compatability shim for jQuery and underscores.js. 6 | * 7 | * WILL BE REMOVED IN Sphinx 6.0 8 | * xref RemovedInSphinx60Warning 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | 18 | /** 19 | * small helper function to urldecode strings 20 | * 21 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL 22 | */ 23 | jQuery.urldecode = function(x) { 24 | if (!x) { 25 | return x 26 | } 27 | return decodeURIComponent(x.replace(/\+/g, ' ')); 28 | }; 29 | 30 | /** 31 | * small helper function to urlencode strings 32 | */ 33 | jQuery.urlencode = encodeURIComponent; 34 | 35 | /** 36 | * This function returns the parsed url parameters of the 37 | * current request. Multiple values per key are supported, 38 | * it will always return arrays of strings for the value parts. 39 | */ 40 | jQuery.getQueryParameters = function(s) { 41 | if (typeof s === 'undefined') 42 | s = document.location.search; 43 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 44 | var result = {}; 45 | for (var i = 0; i < parts.length; i++) { 46 | var tmp = parts[i].split('=', 2); 47 | var key = jQuery.urldecode(tmp[0]); 48 | var value = jQuery.urldecode(tmp[1]); 49 | if (key in result) 50 | result[key].push(value); 51 | else 52 | result[key] = [value]; 53 | } 54 | return result; 55 | }; 56 | 57 | /** 58 | * highlight a given string on a jquery object by wrapping it in 59 | * span elements with the given class name. 60 | */ 61 | jQuery.fn.highlightText = function(text, className) { 62 | function highlight(node, addItems) { 63 | if (node.nodeType === 3) { 64 | var val = node.nodeValue; 65 | var pos = val.toLowerCase().indexOf(text); 66 | if (pos >= 0 && 67 | !jQuery(node.parentNode).hasClass(className) && 68 | !jQuery(node.parentNode).hasClass("nohighlight")) { 69 | var span; 70 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); 71 | if (isInSVG) { 72 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 73 | } else { 74 | span = document.createElement("span"); 75 | span.className = className; 76 | } 77 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 78 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 79 | document.createTextNode(val.substr(pos + text.length)), 80 | node.nextSibling)); 81 | node.nodeValue = val.substr(0, pos); 82 | if (isInSVG) { 83 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); 84 | var bbox = node.parentElement.getBBox(); 85 | rect.x.baseVal.value = bbox.x; 86 | rect.y.baseVal.value = bbox.y; 87 | rect.width.baseVal.value = bbox.width; 88 | rect.height.baseVal.value = bbox.height; 89 | rect.setAttribute('class', className); 90 | addItems.push({ 91 | "parent": node.parentNode, 92 | "target": rect}); 93 | } 94 | } 95 | } 96 | else if (!jQuery(node).is("button, select, textarea")) { 97 | jQuery.each(node.childNodes, function() { 98 | highlight(this, addItems); 99 | }); 100 | } 101 | } 102 | var addItems = []; 103 | var result = this.each(function() { 104 | highlight(this, addItems); 105 | }); 106 | for (var i = 0; i < addItems.length; ++i) { 107 | jQuery(addItems[i].parent).before(addItems[i].target); 108 | } 109 | return result; 110 | }; 111 | 112 | /* 113 | * backward compatibility for jQuery.browser 114 | * This will be supported until firefox bug is fixed. 115 | */ 116 | if (!jQuery.browser) { 117 | jQuery.uaMatch = function(ua) { 118 | ua = ua.toLowerCase(); 119 | 120 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 121 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 122 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 123 | /(msie) ([\w.]+)/.exec(ua) || 124 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 125 | []; 126 | 127 | return { 128 | browser: match[ 1 ] || "", 129 | version: match[ 2 ] || "0" 130 | }; 131 | }; 132 | jQuery.browser = {}; 133 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 134 | } 135 | -------------------------------------------------------------------------------- /docs/_static/nature.css: -------------------------------------------------------------------------------- 1 | /* 2 | * nature.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- nature theme. 6 | * 7 | * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: Arial, sans-serif; 18 | font-size: 100%; 19 | background-color: #fff; 20 | color: #555; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 0 0 0 230px; 32 | } 33 | 34 | hr { 35 | border: 1px solid #B1B4B6; 36 | } 37 | 38 | div.document { 39 | background-color: #eee; 40 | } 41 | 42 | div.body { 43 | background-color: #ffffff; 44 | color: #3E4349; 45 | padding: 0 30px 30px 30px; 46 | font-size: 0.9em; 47 | } 48 | 49 | div.footer { 50 | color: #555; 51 | width: 100%; 52 | padding: 13px 0; 53 | text-align: center; 54 | font-size: 75%; 55 | } 56 | 57 | div.footer a { 58 | color: #444; 59 | text-decoration: underline; 60 | } 61 | 62 | div.related { 63 | background-color: #6BA81E; 64 | line-height: 32px; 65 | color: #fff; 66 | text-shadow: 0px 1px 0 #444; 67 | font-size: 0.9em; 68 | } 69 | 70 | div.related a { 71 | color: #E2F3CC; 72 | } 73 | 74 | div.sphinxsidebar { 75 | font-size: 0.75em; 76 | line-height: 1.5em; 77 | } 78 | 79 | div.sphinxsidebarwrapper{ 80 | padding: 20px 0; 81 | } 82 | 83 | div.sphinxsidebar h3, 84 | div.sphinxsidebar h4 { 85 | font-family: Arial, sans-serif; 86 | color: #222; 87 | font-size: 1.2em; 88 | font-weight: normal; 89 | margin: 0; 90 | padding: 5px 10px; 91 | background-color: #ddd; 92 | text-shadow: 1px 1px 0 white 93 | } 94 | 95 | div.sphinxsidebar h4{ 96 | font-size: 1.1em; 97 | } 98 | 99 | div.sphinxsidebar h3 a { 100 | color: #444; 101 | } 102 | 103 | 104 | div.sphinxsidebar p { 105 | color: #888; 106 | padding: 5px 20px; 107 | } 108 | 109 | div.sphinxsidebar p.topless { 110 | } 111 | 112 | div.sphinxsidebar ul { 113 | margin: 10px 20px; 114 | padding: 0; 115 | color: #000; 116 | } 117 | 118 | div.sphinxsidebar a { 119 | color: #444; 120 | } 121 | 122 | div.sphinxsidebar input { 123 | border: 1px solid #ccc; 124 | font-family: sans-serif; 125 | font-size: 1em; 126 | } 127 | 128 | div.sphinxsidebar .searchformwrapper { 129 | margin-left: 20px; 130 | margin-right: 20px; 131 | } 132 | 133 | /* -- body styles ----------------------------------------------------------- */ 134 | 135 | a { 136 | color: #005B81; 137 | text-decoration: none; 138 | } 139 | 140 | a:hover { 141 | color: #E32E00; 142 | text-decoration: underline; 143 | } 144 | 145 | div.body h1, 146 | div.body h2, 147 | div.body h3, 148 | div.body h4, 149 | div.body h5, 150 | div.body h6 { 151 | font-family: Arial, sans-serif; 152 | background-color: #BED4EB; 153 | font-weight: normal; 154 | color: #212224; 155 | margin: 30px 0px 10px 0px; 156 | padding: 5px 0 5px 10px; 157 | text-shadow: 0px 1px 0 white 158 | } 159 | 160 | div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } 161 | div.body h2 { font-size: 150%; background-color: #C8D5E3; } 162 | div.body h3 { font-size: 120%; background-color: #D8DEE3; } 163 | div.body h4 { font-size: 110%; background-color: #D8DEE3; } 164 | div.body h5 { font-size: 100%; background-color: #D8DEE3; } 165 | div.body h6 { font-size: 100%; background-color: #D8DEE3; } 166 | 167 | a.headerlink { 168 | color: #c60f0f; 169 | font-size: 0.8em; 170 | padding: 0 4px 0 4px; 171 | text-decoration: none; 172 | } 173 | 174 | a.headerlink:hover { 175 | background-color: #c60f0f; 176 | color: white; 177 | } 178 | 179 | div.body p, div.body dd, div.body li { 180 | line-height: 1.5em; 181 | } 182 | 183 | div.admonition p.admonition-title + p { 184 | display: inline; 185 | } 186 | 187 | div.note { 188 | background-color: #eee; 189 | border: 1px solid #ccc; 190 | } 191 | 192 | div.seealso { 193 | background-color: #ffc; 194 | border: 1px solid #ff6; 195 | } 196 | nav.contents, 197 | aside.topic, 198 | 199 | div.topic { 200 | background-color: #eee; 201 | } 202 | 203 | div.warning { 204 | background-color: #ffe4e4; 205 | border: 1px solid #f66; 206 | } 207 | 208 | p.admonition-title { 209 | display: inline; 210 | } 211 | 212 | p.admonition-title:after { 213 | content: ":"; 214 | } 215 | 216 | pre { 217 | padding: 10px; 218 | line-height: 1.2em; 219 | border: 1px solid #C6C9CB; 220 | font-size: 1.1em; 221 | margin: 1.5em 0 1.5em 0; 222 | -webkit-box-shadow: 1px 1px 1px #d8d8d8; 223 | -moz-box-shadow: 1px 1px 1px #d8d8d8; 224 | } 225 | 226 | code { 227 | background-color: #ecf0f3; 228 | color: #222; 229 | /* padding: 1px 2px; */ 230 | font-size: 1.1em; 231 | font-family: monospace; 232 | } 233 | 234 | .viewcode-back { 235 | font-family: Arial, sans-serif; 236 | } 237 | 238 | div.viewcode-block:target { 239 | background-color: #f4debf; 240 | border-top: 1px solid #ac9; 241 | border-bottom: 1px solid #ac9; 242 | } 243 | 244 | div.code-block-caption { 245 | background-color: #ddd; 246 | color: #222; 247 | border: 1px solid #C6C9CB; 248 | } -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #8f5902; font-style: italic } /* Comment */ 9 | .highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ 10 | .highlight .g { color: #000000 } /* Generic */ 11 | .highlight .k { color: #204a87; font-weight: bold } /* Keyword */ 12 | .highlight .l { color: #000000 } /* Literal */ 13 | .highlight .n { color: #000000 } /* Name */ 14 | .highlight .o { color: #ce5c00; font-weight: bold } /* Operator */ 15 | .highlight .x { color: #000000 } /* Other */ 16 | .highlight .p { color: #000000; font-weight: bold } /* Punctuation */ 17 | .highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ 18 | .highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ 19 | .highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ 20 | .highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ 21 | .highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ 22 | .highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ 23 | .highlight .gd { color: #a40000 } /* Generic.Deleted */ 24 | .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ 25 | .highlight .gr { color: #ef2929 } /* Generic.Error */ 26 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 27 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 28 | .highlight .go { color: #000000; font-style: italic } /* Generic.Output */ 29 | .highlight .gp { color: #8f5902 } /* Generic.Prompt */ 30 | .highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ 31 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 32 | .highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ 33 | .highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ 34 | .highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ 35 | .highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ 36 | .highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ 37 | .highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ 38 | .highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ 39 | .highlight .ld { color: #000000 } /* Literal.Date */ 40 | .highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */ 41 | .highlight .s { color: #4e9a06 } /* Literal.String */ 42 | .highlight .na { color: #c4a000 } /* Name.Attribute */ 43 | .highlight .nb { color: #204a87 } /* Name.Builtin */ 44 | .highlight .nc { color: #000000 } /* Name.Class */ 45 | .highlight .no { color: #000000 } /* Name.Constant */ 46 | .highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ 47 | .highlight .ni { color: #ce5c00 } /* Name.Entity */ 48 | .highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ 49 | .highlight .nf { color: #000000 } /* Name.Function */ 50 | .highlight .nl { color: #f57900 } /* Name.Label */ 51 | .highlight .nn { color: #000000 } /* Name.Namespace */ 52 | .highlight .nx { color: #000000 } /* Name.Other */ 53 | .highlight .py { color: #000000 } /* Name.Property */ 54 | .highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */ 55 | .highlight .nv { color: #000000 } /* Name.Variable */ 56 | .highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */ 57 | .highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ 58 | .highlight .w { color: #f8f8f8 } /* Text.Whitespace */ 59 | .highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */ 60 | .highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ 61 | .highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ 62 | .highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ 63 | .highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ 64 | .highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ 65 | .highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ 66 | .highlight .sc { color: #4e9a06 } /* Literal.String.Char */ 67 | .highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ 68 | .highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ 69 | .highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ 70 | .highlight .se { color: #4e9a06 } /* Literal.String.Escape */ 71 | .highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ 72 | .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ 73 | .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ 74 | .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ 75 | .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ 76 | .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ 77 | .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ 78 | .highlight .fm { color: #000000 } /* Name.Function.Magic */ 79 | .highlight .vc { color: #000000 } /* Name.Variable.Class */ 80 | .highlight .vg { color: #000000 } /* Name.Variable.Global */ 81 | .highlight .vi { color: #000000 } /* Name.Variable.Instance */ 82 | .highlight .vm { color: #000000 } /* Name.Variable.Magic */ 83 | .highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, is available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | termuxgui Python Documentation — termuxgui 0.1.5 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 |

termuxgui Python Documentation

45 |
46 |

Contents:

47 | 60 |
61 |
62 |
63 |

Indices and tables

64 | 69 |
70 | 71 | 72 |
73 |
74 |
75 |
76 | 110 |
111 |
112 | 128 | 132 | 133 | -------------------------------------------------------------------------------- /src/termuxgui/activity.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal 2 | 3 | from termuxgui.connection import Connection, Configuration 4 | from termuxgui.task import Task 5 | 6 | 7 | class Activity: 8 | """This represents an Android Activity. 9 | 10 | Use a.c to access the Connection object of an Activity and a.t to access the Task object.""" 11 | 12 | def __init__(self, connection: Connection, tid: Optional[str] = None, dialog: Optional[bool] = None, 13 | pip: Optional[bool] = False, overlay: Optional[bool] = None, lockscreen: Optional[bool] = None, 14 | canceloutside: bool = True, intercept: bool = False): 15 | """When an Activity could not be created, a RuntimeError is raised.""" 16 | self.c = connection 17 | params = {"canceloutside": canceloutside, "intercept": intercept} 18 | if dialog is not None: 19 | params["dialog"] = dialog 20 | if tid is not None: 21 | params["tid"] = tid 22 | if pip is not None: 23 | params["pip"] = pip 24 | if overlay is not None: 25 | params["overlay"] = overlay 26 | if lockscreen is not None: 27 | params["lockscreen"] = lockscreen 28 | if tid is None: 29 | self.aid, tid = self.c.send_read_msg({"method": "newActivity", "params": params}) 30 | else: 31 | self.aid = self.c.send_read_msg({"method": "newActivity", "params": params}) 32 | self.t = Task(connection, tid) 33 | if self.aid == -1: 34 | raise RuntimeError("Could not create Activity") 35 | 36 | def finish(self): 37 | """Finishes an Activity.""" 38 | self.c.send_msg({"method": "finishActivity", "params": {"aid": self.aid}}) 39 | 40 | def setinputmode(self, mode: Literal["resize", "pan"]): 41 | """Sets the input mode for an Activity.""" 42 | self.c.send_msg({"method": "setInputMode", "params": {"aid": self.aid, "mode": mode}}) 43 | 44 | def keepscreenon(self, on: bool): 45 | """Sets whether the Activity should keep the screen on while it is showing.""" 46 | self.c.send_msg({"method": "keepScreenOn", "params": {"aid": self.aid, "on": on}}) 47 | 48 | def setpipmode(self, pip: bool): 49 | """Goes into / out of picture-in-picture mode. 50 | 51 | When you exit pip, you Activity is put in the recent tasks list and not shown to the user.""" 52 | self.c.send_msg({"method": "setPiPMode", "params": {"aid": self.aid, "pip": pip}}) 53 | 54 | def setpipmodeauto(self, pip: bool): 55 | """Set whether the Activity should automatically enter picture-in-picture mode 56 | when the user leaves the Activity.""" 57 | self.c.send_msg({"method": "setPiPModeAuto", "params": {"aid": self.aid, "pip": pip}}) 58 | 59 | def setpipparams(self, num: int, den: int): 60 | """Sets the PiP parameters for the Activity, currently only the aspect ration.""" 61 | self.c.send_msg({"method": "setPiPParams", "params": {"aid": self.aid, "num": num, "den": den}}) 62 | 63 | def settaskdescription(self, label: str, img: str): 64 | """Sets the Task icon. img has to be a PNG or JPEG image as a base64 encoded string.""" 65 | self.c.send_msg({"method": "setTaskDescription", "params": {"aid": self.aid, "img": img, "label": label}}) 66 | 67 | def settheme(self, statusbarcolor: int, colorprimary: int, windowbackground: int, textcolor: int, coloraccent: int): 68 | """Sets the Theme for the Activity.""" 69 | self.c.send_msg({"method": "setTheme", 70 | "params": {"aid": self.aid, "statusBarColor": statusbarcolor, "colorPrimary": colorprimary, 71 | "windowBackground": windowbackground, "textColor": textcolor, 72 | "colorAccent": coloraccent}}) 73 | 74 | def setorientation(self, orientation: Literal["behind", "fullSensor", "fullUser", "landscape", "locked", "nosensor", 75 | "portrait", "reverseLandscape", "reversePortrait", "sensorLandscape", 76 | "sensorPortrait", "user", "userLandscape", "userPortrait"]): 77 | """Sets the preferred orientation of the Activity.""" 78 | self.c.send_msg({"method": "setOrientation", "params": {"aid": self.aid, "orientation": orientation}}) 79 | 80 | def setposition(self, x: int, y: int): 81 | """Sets the screen position of the Activity if it is an overlay.""" 82 | self.c.send_msg({"method": "setPosition", "params": {"aid": self.aid, "x": x, "y": y}}) 83 | 84 | def sendoverlayevents(self, send: bool): 85 | """Sets whether or not you want to receive overlay events if the Activity is an overlay.""" 86 | self.c.send_msg({"method": "sendOverlayTouchEvent", "params": {"aid": self.aid, "send": send}}) 87 | 88 | def movetoback(self): 89 | """Moves this Activity's Task to the recents screen and hides it.""" 90 | self.c.send_msg({"method": "moveTaskToBack", "params": {"aid": self.aid}}) 91 | 92 | def requestunlock(self): 93 | """Requests the lockscreen to unlock. 94 | If the lockscreen is just protected by a swipe, it is unlocked immediately. 95 | If a PIN, password or pattern has to be used, brings up the UI to let the user unlock the lockscreen.""" 96 | self.c.send_msg({"method": "requestUnlock", "params": {"aid": self.aid}}) 97 | 98 | def getconfiguration(self) -> Configuration: 99 | """Reads the current configuration of this Activity.""" 100 | return self.c.send_read_msg({"method": "getConfiguration", "params": {"aid": self.aid}}) 101 | 102 | def hidesoftkeyboard(self): 103 | """Hides the soft keyboard if this Activity has the soft keyboard focus.""" 104 | self.c.send_msg({"method": "hideSoftKeyboard", "params": {"aid": self.aid}}) 105 | 106 | def interceptbackbutton(self, intercept: bool): 107 | """Sets whether to intercept a back button press and send an event instead.""" 108 | self.c.send_msg({"method": "interceptBackButton", "params": {"aid": self.aid, "intercept": intercept}}) 109 | 110 | def setsecure(self, secure: bool): 111 | """Sets the secure flag for the Activity, making screenshots impossible and showing a blank screen in the 112 | task switcher.""" 113 | self.c.send_msg({"method": "setSecure", "params": {"aid": self.aid, "secure": secure}}) 114 | 115 | -------------------------------------------------------------------------------- /src/termuxgui/remoteviews.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from typing import Optional, Literal, Union 3 | 4 | from termuxgui.connection import Connection 5 | 6 | 7 | class RemoteViews: 8 | """This represents a remote Layout. You can use remote Layouts for custom notifications or widgets.""" 9 | 10 | def __init__(self, connection: Connection): 11 | self.c = connection 12 | self.rid = self.c.send_read_msg({"method": "createRemoteLayout"}) 13 | 14 | def __eq__(self, other): 15 | if type(other) is int: 16 | return self.rid == other 17 | if isinstance(other, RemoteViews): 18 | return self.rid == other.rid 19 | 20 | def delete(self): 21 | """Deletes this remote Layout.""" 22 | self.c.send_msg({"method": "deleteRemoteLayout", "params": {"rid": self.rid}}) 23 | 24 | def addFrameLayout(self, parent: Optional[int] = None) -> int: 25 | """Creates a FrameLayout in this remote Layout and returns the id, or -1 if it could not be created. 26 | The id can be used in further methods to manipulate the View.""" 27 | args = {"rid": self.rid} 28 | if parent is not None: 29 | args["parent"] = parent 30 | return self.c.send_read_msg({"method": "addRemoteFrameLayout", "params": args}) 31 | 32 | def addLinearLayout(self, parent: Optional[int] = None, vertical: Optional[bool] = None) -> int: 33 | """Creates a LinearLayout in this remote Layout and returns the id, or -1 if it could not be created. 34 | The id can be used in further methods to manipulate the View.""" 35 | args = {"rid": self.rid} 36 | if vertical is not None: 37 | args["vertical"] = vertical 38 | if parent is not None: 39 | args["parent"] = parent 40 | return self.c.send_read_msg({"method": "addRemoteLinearLayout", "params": args}) 41 | 42 | def addTextView(self, parent: Optional[int] = None) -> int: 43 | """Creates a TextView in this remote Layout and returns the id, or -1 if it could not be created. 44 | The id can be used in further methods to manipulate the View.""" 45 | args = {"rid": self.rid} 46 | if parent is not None: 47 | args["parent"] = parent 48 | return self.c.send_read_msg({"method": "addRemoteTextView", "params": args}) 49 | 50 | def addButton(self, parent: Optional[int] = None) -> int: 51 | """Creates a Button in this remote Layout and returns the id, or -1 if it could not be created. 52 | The id can be used in further methods to manipulate the View.""" 53 | args = {"rid": self.rid} 54 | if parent is not None: 55 | args["parent"] = parent 56 | return self.c.send_read_msg({"method": "addRemoteButton", "params": args}) 57 | 58 | def addImageView(self, parent: Optional[int] = None) -> int: 59 | """Creates an ImageView in this remote Layout and returns the id, or -1 if it could not be created. 60 | The id can be used in further methods to manipulate the View.""" 61 | args = {"rid": self.rid} 62 | if parent is not None: 63 | args["parent"] = parent 64 | return self.c.send_read_msg({"method": "addRemoteImageView", "params": args}) 65 | 66 | def addProgressBar(self, parent: Optional[int] = None) -> int: 67 | """Creates a ProgressBar in this remote Layout and returns the id, or -1 if it could not be created. 68 | The id can be used in further methods to manipulate the View.""" 69 | args = {"rid": self.rid} 70 | if parent is not None: 71 | args["parent"] = parent 72 | return self.c.send_read_msg({"method": "addRemoteProgressBar", "params": args}) 73 | 74 | def setBackgroundColor(self, id: int, color: int): 75 | """Sets the background color of a View in this remote Layout.""" 76 | self.c.send_msg({"method": "setRemoteBackgroundColor", "params": {"rid": self.rid, "id": id, "color": color}}) 77 | 78 | def setProgress(self, id: int, progress: int, max: int): 79 | """Sets the progress and maximum value of a ProgressBar in this remote Layout.""" 80 | self.c.send_msg( 81 | {"method": "setRemoteProgressBar", "params": {"rid": self.rid, "id": id, "max": max, "progress": progress}}) 82 | 83 | def setText(self, id: int, text: str): 84 | """Sets the text of a TextView in this remote Layout.""" 85 | self.c.send_msg({"method": "setRemoteText", "params": {"rid": self.rid, "id": id, "text": text}}) 86 | 87 | def setTextSize(self, id: int, size: int, px: bool): 88 | """Sets the text size of a TextView in this remote Layout. 89 | If px is true, the size will be in pixels. If not, it will be in dip.""" 90 | self.c.send_msg({"method": "setRemoteTextSize", "params": {"rid": self.rid, "id": id, "size": size, "px": px}}) 91 | 92 | def setTextColor(self, id: int, color: int): 93 | """Sets the text color of a TextView in this remote Layout.""" 94 | self.c.send_msg({"method": "setRemoteTextColor", "params": {"rid": self.rid, "id": id, "color": color}}) 95 | 96 | def setVisibility(self, id: int, vis: Literal[0, 1, 2]): 97 | """Sets the visibility of a View in this remote Layout. 98 | 99 | 0 = gone, 1 = hidden, 2 = visible. While hidden, views are not visible but still take up space in the layout. 100 | Gone views do not take up layout space.""" 101 | self.c.send_msg({"method": "setRemoteVisibility", "params": {"rid": self.rid, "id": id, "vis": vis}}) 102 | 103 | def setPadding(self, id: int, left: int, top: int, right: int, bottom: int): 104 | """Sets the padding of a View in this remote Layout.""" 105 | self.c.send_msg({"method": "setRemotePadding", 106 | "params": {"rid": self.rid, "id": id, "left": left, "top": top, "right": right, 107 | "bottom": bottom}}) 108 | 109 | def setImage(self, id: int, img: Union[str, bytes]): 110 | """Sets the image of an ImageView in this remote Layout. 111 | The image has to be a bytes object containing the image in png or jpeg format, 112 | or a string containing the base64 encoded version of it.""" 113 | if isinstance(img, bytes): 114 | img = base64.standard_b64encode(img).decode("ascii") 115 | self.c.send_msg({"method": "setRemoteImage", "params": {"rid": self.rid, "id": id, "img": img}}) 116 | 117 | def updateWidget(self, wid: str): 118 | """Sets the content of the Widget with ID wid to this remote Layout.""" 119 | self.c.send_msg({"method": "setWidgetLayout", "params": {"rid": self.rid, "wid": wid}}) 120 | -------------------------------------------------------------------------------- /src/termuxgui/notification.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from typing import Literal, List 3 | 4 | from termuxgui.remoteviews import RemoteViews 5 | from termuxgui.connection import Connection 6 | 7 | 8 | class Notification: 9 | """This represents a notification.""" 10 | 11 | def __init__(self, connection: Connection, channel: str, importance: Literal[0, 1, 2, 3, 4]): 12 | self.c = connection 13 | self.channel = channel 14 | self.importance = importance 15 | self.id = None 16 | self.ongoing = False 17 | self.layout = None 18 | self.expandedlayout = None 19 | self.hudlayout = None 20 | self.title = None 21 | self.content = None 22 | self.largeimage = None 23 | self.largetext = None 24 | self.largeimageasthumbnail = False 25 | self.icon = None 26 | self.alertonce = True 27 | self.showtimestamp = True 28 | self.timestamp = None 29 | self.actions = None 30 | 31 | def createchannel(self): 32 | """Creates the notification channel for the notification. This is called automatically when you call notify.""" 33 | self.c.send_msg({"method": "createNotificationChannel", "params": 34 | {"id": self.channel, "importance": self.importance, "name": self.channel}}) 35 | 36 | def seticon(self, icon: bytes): 37 | """Set the icon of the notification. 38 | icon has to be a bytes object containing the icon in png or jpeg format.""" 39 | self.icon = base64.standard_b64encode(icon).decode("ascii") 40 | 41 | def settitle(self, title: str): 42 | """Sets the title of the notification, removing any set custom layout.""" 43 | self.title = title 44 | self.layout = None 45 | self.expandedlayout = None 46 | self.hudlayout = None 47 | 48 | def setcontent(self, content: str): 49 | """Sets the content of the notification, removing any set custom layout.""" 50 | self.content = content 51 | self.layout = None 52 | self.expandedlayout = None 53 | self.hudlayout = None 54 | 55 | def setlargeimage(self, img: bytes): 56 | """Sets a large image for the notification, removing any set custom layout.""" 57 | self.largeimage = base64.standard_b64encode(img).decode("ascii") 58 | self.layout = None 59 | self.expandedlayout = None 60 | self.hudlayout = None 61 | self.largetext = None 62 | 63 | def setlargetext(self, text: str): 64 | """Sets the text after expanding the notification, removing any set custom layout.""" 65 | self.largetext = text 66 | self.layout = None 67 | self.expandedlayout = None 68 | self.hudlayout = None 69 | self.largeimage = None 70 | 71 | def setlayout(self, layout: RemoteViews): 72 | """Sets a custom layout for the notification. 73 | Removes any title, content largetext or largeimage.""" 74 | self.layout = layout.rid 75 | self.largeimage = None 76 | self.largetext = None 77 | self.title = None 78 | self.content = None 79 | 80 | def setexpandedlayout(self, layout: RemoteViews): 81 | """Sets a custom expanded layout for the notification. 82 | Removes any title, content largetext or largeimage.""" 83 | self.expandedlayout = layout.rid 84 | self.largeimage = None 85 | self.largetext = None 86 | self.title = None 87 | self.content = None 88 | 89 | def sethudlayout(self, layout: RemoteViews): 90 | """Sets a custom hud layout for the notification. 91 | Removes any title, content largetext or largeimage.""" 92 | self.hudlayout = layout.rid 93 | self.largeimage = None 94 | self.largetext = None 95 | self.title = None 96 | self.content = None 97 | 98 | def setongoing(self, ongoing: bool): 99 | """Ongoing notifications can't be dismissed by the user.""" 100 | self.ongoing = ongoing 101 | 102 | def setalertonce(self, alertonce: bool): 103 | """If you call notify more than once, whether to alert the user every time.""" 104 | self.alertonce = alertonce 105 | 106 | def setshowtimestamp(self, show: bool): 107 | """Whether the timestamp is shown in the notification""" 108 | self.showtimestamp = show 109 | 110 | def settimestamp(self, timestamp: int): 111 | """Sets the timestamp of the notification. The format is the unix time in milliseconds.""" 112 | self.timestamp = timestamp 113 | 114 | def setactions(self, actions: List[str]): 115 | """Sets the displayed actions for the notification.""" 116 | self.actions = actions 117 | 118 | def setlargeimageasthumbnail(self, asthumbnail: bool): 119 | """Sets whether to show a large image as a thumbnail in the collapsed notification.""" 120 | self.largeimageasthumbnail = asthumbnail 121 | 122 | def notify(self): 123 | """Creates/updates the notification.""" 124 | args = { 125 | "ongoing": self.ongoing, 126 | "largeImageAsThumbnail": self.largeimageasthumbnail, 127 | "channel": self.channel, 128 | "importance": self.importance, 129 | "alertOnce": self.alertonce, 130 | "showTimestamp": self.showtimestamp, 131 | } 132 | if self.layout is not None: 133 | args["layout"] = self.layout 134 | if self.expandedlayout is not None: 135 | args["expandedLayout"] = self.expandedlayout 136 | if self.hudlayout is not None: 137 | args["hudLayout"] = self.hudlayout 138 | 139 | if self.title is not None: 140 | args["title"] = self.title 141 | if self.content is not None: 142 | args["content"] = self.content 143 | 144 | if self.largeimage is not None: 145 | args["largeImage"] = self.largeimage 146 | if self.largetext is not None: 147 | args["largeText"] = self.largetext 148 | 149 | if self.icon is not None: 150 | args["icon"] = self.icon 151 | 152 | if self.timestamp is not None: 153 | args["timestamp"] = self.timestamp 154 | 155 | if self.actions is not None: 156 | args["actions"] = self.actions 157 | 158 | if self.id is None: 159 | self.createchannel() 160 | else: 161 | args["id"] = self.id 162 | self.id = self.c.send_read_msg({"method": "createNotification", "params": args}) 163 | 164 | def cancel(self): 165 | """Cancels the notification.""" 166 | if self.id is not None: 167 | self.c.send_msg({"method": "cancelNotification", "params": {"id": self.id}}) 168 | -------------------------------------------------------------------------------- /src/termuxgui/view.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Literal, List, Union 2 | 3 | from termuxgui.event import Event 4 | from termuxgui.activity import Activity 5 | 6 | 7 | class View: 8 | """This represents a generic View.""" 9 | 10 | """Visibility constant for gone views""" 11 | GONE = 0 12 | 13 | """Visibility constant for hidden views""" 14 | HIDDEN = 1 15 | 16 | """Visibility constant for visible views""" 17 | VISIBLE = 2 18 | 19 | """Special size constant to only take up the space needed.""" 20 | WRAP_CONTENT: str = "WRAP_CONTENT" 21 | 22 | """Special size constant to take up the space of the parent View.""" 23 | MATCH_PARENT: str = "MATCH_PARENT" 24 | 25 | def __init__(self, activity: Activity, id: int): 26 | self.a = activity 27 | self.id = id 28 | if self.id == -1: 29 | raise RuntimeError("Could not create View") 30 | 31 | def __eq__(self, other): 32 | if type(other) is int: 33 | return self.id == other 34 | if isinstance(other, View): 35 | return self.id == other.id 36 | 37 | def delete(self): 38 | """Deletes this View from the layout.""" 39 | self.a.c.send_msg({"method": "deleteView", "params": {"aid": self.a.aid, "id": self.id}}) 40 | 41 | def setmargin(self, margin: int, dir: Optional[Literal["top", "bottom", "left", "right"]] = None): 42 | """Sets the margins of this View for one or all directions.""" 43 | args = {"aid": self.a.aid, "id": self.id, "margin": margin} 44 | if dir is not None: 45 | args["dir"] = dir 46 | self.a.c.send_msg({"method": "setMargin", "params": args}) 47 | 48 | def setwidth(self, width: Union[int, str], px: bool = False): 49 | """Sets the width of this View. 50 | Can be either an integer describing the value in dp, "MATCH_PARENT" or "WRAP_CONTENT". 51 | If px is true, the values are in pixels instead.""" 52 | self.a.c.send_msg( 53 | {"method": "setWidth", "params": {"aid": self.a.aid, "id": self.id, "width": width, "px": px}}) 54 | 55 | def setheight(self, height: Union[int, str], px: bool = False): 56 | """Sets the height of this View. 57 | Can be either an integer describing the value in dp, "MATCH_PARENT" or "WRAP_CONTENT". 58 | If px is true, the values are in pixels instead.""" 59 | self.a.c.send_msg( 60 | {"method": "setHeight", "params": {"aid": self.a.aid, "id": self.id, "height": height, "px": px}}) 61 | 62 | def setdimensions(self, width: Union[int, str], 63 | height: Union[int, str], px: bool = False): 64 | """Sets the dimensions of this view. width and height are the same as for setwidth and setheight. 65 | If px is true, the values are in pixels instead.""" 66 | self.setwidth(width, px) 67 | self.setheight(height, px) 68 | 69 | def setlinearlayoutparams(self, weight: int, position: Optional[int] = None): 70 | """Sets the LinearLayout weight and/or position for this view.""" 71 | self.a.c.send_msg({"method": "setLinearLayoutParams", 72 | "params": {"aid": self.a.aid, "id": self.id, "weight": weight, "position": position}}) 73 | 74 | def setgridlayoutparams(self, row: int, col: int, rowsize: int = 1, colsize: int = 1, 75 | alignmentrow: Literal["top", "bottom", "left", "right", "center", "baseline", "fill"] = "center", 76 | alignmentcol: Literal["top", "bottom", "left", "right", "center", "baseline", "fill"] = "center"): 77 | """Sets the GridLayout position for this View. 78 | The rows and columns start at 0. 79 | rowsize and colsize define how many row/column cells a View takes up. 80 | alignmentrow and alignmentcol specify how the View is positioned in its cells,""" 81 | self.a.c.send_msg({"method": "setGridLayoutParams", 82 | "params": {"aid": self.a.aid, "id": self.id, "row": row, "col": col, 83 | "colsize": colsize, "rowsize": rowsize, 84 | "alignmentrow": alignmentrow, "alignmentcol": alignmentcol}}) 85 | 86 | def sendtouchevent(self, send: bool): 87 | """Sets whether ot not touch events are sent for this View.""" 88 | self.a.c.send_msg({"method": "sendTouchEvent", "params": {"aid": self.a.aid, "id": self.id, "send": send}}) 89 | 90 | def sendclickevent(self, send: bool): 91 | """Sets whether ot not click events are sent for this View.""" 92 | self.a.c.send_msg({"method": "sendClickEvent", "params": {"aid": self.a.aid, "id": self.id, "send": send}}) 93 | 94 | def sendlongclickevent(self, send: bool): 95 | """Sets whether ot not long click events are sent for this View.""" 96 | self.a.c.send_msg({"method": "sendLongClickEvent", "params": {"aid": self.a.aid, "id": self.id, "send": send}}) 97 | 98 | def sendfocuschangeevent(self, send: bool): 99 | """Sets whether ot not focus change events are sent for this View.""" 100 | self.a.c.send_msg( 101 | {"method": "sendFocusChangeEvent", "params": {"aid": self.a.aid, "id": self.id, "send": send}}) 102 | 103 | def getdimensions(self) -> List[int]: 104 | """Gets the width and height of a View in the layout as a list. The values are in pixels.""" 105 | return self.a.c.send_read_msg({"method": "getDimensions", "params": {"aid": self.a.aid, "id": self.id}}) 106 | 107 | def setbackgroundcolor(self, color: int): 108 | """Sets the background color for this view.""" 109 | self.a.c.send_msg( 110 | {"method": "setBackgroundColor", "params": {"aid": self.a.aid, "id": self.id, "color": color}}) 111 | 112 | def setvisibility(self, vis: Literal[0, 1, 2]): 113 | """Sets the visibility for this view. 114 | 115 | 0 = gone, 1 = hidden, 2 = visible. While hidden, views are not visible but still take up space in the layout. 116 | Gone views do not take up layout space.""" 117 | self.a.c.send_msg({"method": "setVisibility", "params": {"aid": self.a.aid, "id": self.id, "vis": vis}}) 118 | 119 | def focus(self, forcesoft: bool = False): 120 | """Sets the Focus to this View and opens the soft keyboard if the View has keyboard input.""" 121 | self.a.c.send_msg( 122 | {"method": "requestFocus", "params": {"aid": self.a.aid, "id": self.id, "forcesoft": forcesoft}}) 123 | 124 | def handleevent(self, e: Event): 125 | """Handles an Event for this View. Subclasses can override this to provide event handling.""" 126 | if hasattr(self, "eventlistener"): 127 | self.eventlistener(self, e) 128 | 129 | def setclickable(self, clickable: bool): 130 | """Sets whether the View is clickable by the user.""" 131 | self.a.c.send_msg({"method": "setClickable", "params": {"aid": self.a.aid, "id": self.id, "clickable": clickable}}) 132 | 133 | -------------------------------------------------------------------------------- /src/termuxgui/connection.py: -------------------------------------------------------------------------------- 1 | from socket import socket, AF_UNIX, SOCK_STREAM, timeout, SOL_SOCKET, SO_PEERCRED 2 | from subprocess import run, DEVNULL 3 | from secrets import choice 4 | from string import ascii_letters, digits 5 | from os import getuid 6 | from struct import unpack 7 | from json import dumps 8 | from select import select 9 | from typing import TypedDict, Literal, Optional, Any, Union, Iterator 10 | from threading import RLock 11 | 12 | from termuxgui.event import Event 13 | import termuxgui.msg as tgmsg 14 | 15 | 16 | def _check_user(s): 17 | uid = unpack("III", s.getsockopt(SOL_SOCKET, SO_PEERCRED, 12))[1] 18 | return uid == getuid() 19 | 20 | 21 | class ConfigDarkMode(TypedDict, total=False): 22 | dark_mode: bool 23 | 24 | 25 | class Configuration(ConfigDarkMode, TypedDict): 26 | country: str 27 | language: str 28 | orientation: Literal["landscape", "portrait"] 29 | keyboardHidden: bool 30 | screenwidth: int 31 | screenheight: int 32 | fontscale: float 33 | density: float 34 | 35 | 36 | class Connection: 37 | """This represents a connection to the Termux:GUI plugin and contains all functions that don't act on any 38 | particular View, Activity or Task. """ 39 | 40 | def __init__(self): 41 | """When a connection can't be established, a RuntimeError is raised.""" 42 | 43 | self.__main_lock = RLock() 44 | self.__event_lock = RLock() 45 | 46 | adrMain = ''.join(choice(ascii_letters + digits) for i in range(50)) 47 | adrEvent = ''.join(choice(ascii_letters + digits) for i in range(50)) 48 | mainss = socket(AF_UNIX, SOCK_STREAM) 49 | eventss = socket(AF_UNIX, SOCK_STREAM) 50 | with mainss, eventss: 51 | mainss.bind('\0' + adrMain) 52 | eventss.bind('\0' + adrEvent) 53 | mainss.listen(1) 54 | eventss.listen(1) 55 | mainss.settimeout(5) 56 | eventss.settimeout(5) 57 | run(["termux-am", "broadcast", "-n", "com.termux.gui/.GUIReceiver", "--es", "mainSocket", adrMain, "--es", 58 | "eventSocket", adrEvent], stdout=DEVNULL, stderr=DEVNULL) 59 | try: 60 | main = mainss.accept()[0] 61 | try: 62 | event = eventss.accept()[0] 63 | try: 64 | # check for the termux uid to see if it is really the plugin that has connected 65 | if not _check_user(main) or not _check_user(event): 66 | main.close() 67 | event.close() 68 | raise RuntimeError("Plugin doesn't have the same UID") 69 | main.sendall(b'\x01') 70 | ret = b'' 71 | while len(ret) == 0: 72 | ret = ret + main.recv(1) 73 | if ret[0] != 0: 74 | main.close() 75 | event.close() 76 | raise RuntimeError("Invalid Protocol version response") 77 | self._main = main 78 | self._event = event 79 | except Exception as e: 80 | event.close() 81 | raise e 82 | except Exception as e: 83 | main.close() 84 | raise e 85 | except timeout: 86 | run(["am", "broadcast", "-n", "com.termux.gui/.GUIReceiver", "--es", "mainSocket", adrMain, "--es", 87 | "eventSocket", adrEvent], stdout=DEVNULL, stderr=DEVNULL) 88 | try: 89 | main = mainss.accept()[0] 90 | try: 91 | event = eventss.accept()[0] 92 | try: 93 | # check for the termux uid to see if it is really the plugin that has connected 94 | if not _check_user(main) or not _check_user(event): 95 | main.close() 96 | event.close() 97 | raise RuntimeError("Plugin doesn't have the same UID") 98 | main.sendall(b'\x01') 99 | ret = b'' 100 | while len(ret) == 0: 101 | ret = ret + main.recv(1) 102 | if ret[0] != 0: 103 | main.close() 104 | event.close() 105 | raise RuntimeError("Invalid Protocol version response") 106 | self._main = main 107 | self._event = event 108 | except Exception as e: 109 | event.close() 110 | raise e 111 | except Exception as e: 112 | main.close() 113 | raise e 114 | except timeout: 115 | raise RuntimeError("Could not connect to Termux:GUI. Is the plugin installed?") 116 | 117 | def events(self) -> Iterator[Event]: 118 | """Waits for events. Use this with "for in" to iterate over incoming events and block while waiting.""" 119 | with self.__event_lock: 120 | while True: 121 | yield Event(tgmsg.read_msg(self._event)) 122 | 123 | def checkevent(self) -> Optional[Event]: 124 | """If there is at least one event to be read, returns it. 125 | If there is no event, returns None. 126 | You can use this to e.g. check for events between drawing a frame instead of 127 | having to use a separate thread and blocking it.""" 128 | with self.__event_lock: 129 | r, _, _ = select([self._event], [], [], 0) 130 | if len(r) != 0: 131 | return Event(tgmsg.read_msg(self._event)) 132 | 133 | def toast(self, text: str, long: bool = False): 134 | """Sends a Toast. Set long to True if you want to display the text for longer.""" 135 | self.send_msg({"method": "toast", "params": {"text": text, "long": long}}) 136 | 137 | def totermux(self): 138 | """Returns to the termux task. This is a shorthand for running "am start" to start the termux activity.""" 139 | run(["am", "start", "-n", "com.termux/.app.TermuxActivity"], stdout=DEVNULL, stderr=DEVNULL) 140 | 141 | def close(self): 142 | """Closes the connection. 143 | 144 | The connection is automatically closed when the program exits, but it's good practice to close the connection 145 | yourself when you don't need it anymore or use a "with" statement.""" 146 | self._main.close() 147 | self._event.close() 148 | 149 | def __enter__(self): 150 | return self 151 | 152 | def __exit__(self, type, value, traceback): 153 | self.close() 154 | return False 155 | 156 | def send_msg(self, msg: Union[str, dict]): 157 | """Send a message to the main socket. 158 | You should only need to call this yourself if you want to use methods not yet implemented in the library.""" 159 | with self.__main_lock: 160 | if type(msg) is dict: 161 | msg = dumps(msg) 162 | tgmsg.send_msg(self._main, msg) 163 | 164 | def __read_msg(self) -> Any: 165 | """Read a message from the main socket. 166 | You should only need to call this yourself if you want to use methods not yet implemented in the library.""" 167 | return tgmsg.read_msg(self._main) 168 | 169 | def send_read_msg(self, msg: Union[str, dict]): 170 | """Send a message to the main socket and read a message afterwards. 171 | You should only need to call this yourself if you want to use methods not yet implemented in the library.""" 172 | with self.__main_lock: 173 | self.send_msg(msg) 174 | return self.__read_msg() 175 | 176 | def turnscreenon(self): 177 | """Turns the screen on.""" 178 | self.send_msg({"method": "turnScreenOn", "params": {}}) 179 | 180 | def islocked(self) -> bool: 181 | """Returns whether the device is locked.""" 182 | return self.send_read_msg({"method": "isLocked", "params": {}}) 183 | 184 | def getversion(self) -> bool: 185 | """Returns the version code of the plugin app. 186 | Can be used to disable features only present in a newer version or prompting the user to update.""" 187 | return self.send_read_msg({"method": "getVersion", "params": {}}) 188 | -------------------------------------------------------------------------------- /docs/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const _ready = (callback) => { 14 | if (document.readyState !== "loading") { 15 | callback(); 16 | } else { 17 | document.addEventListener("DOMContentLoaded", callback); 18 | } 19 | }; 20 | 21 | /** 22 | * highlight a given string on a node by wrapping it in 23 | * span elements with the given class name. 24 | */ 25 | const _highlight = (node, addItems, text, className) => { 26 | if (node.nodeType === Node.TEXT_NODE) { 27 | const val = node.nodeValue; 28 | const parent = node.parentNode; 29 | const pos = val.toLowerCase().indexOf(text); 30 | if ( 31 | pos >= 0 && 32 | !parent.classList.contains(className) && 33 | !parent.classList.contains("nohighlight") 34 | ) { 35 | let span; 36 | 37 | const closestNode = parent.closest("body, svg, foreignObject"); 38 | const isInSVG = closestNode && closestNode.matches("svg"); 39 | if (isInSVG) { 40 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 41 | } else { 42 | span = document.createElement("span"); 43 | span.classList.add(className); 44 | } 45 | 46 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 47 | parent.insertBefore( 48 | span, 49 | parent.insertBefore( 50 | document.createTextNode(val.substr(pos + text.length)), 51 | node.nextSibling 52 | ) 53 | ); 54 | node.nodeValue = val.substr(0, pos); 55 | 56 | if (isInSVG) { 57 | const rect = document.createElementNS( 58 | "http://www.w3.org/2000/svg", 59 | "rect" 60 | ); 61 | const bbox = parent.getBBox(); 62 | rect.x.baseVal.value = bbox.x; 63 | rect.y.baseVal.value = bbox.y; 64 | rect.width.baseVal.value = bbox.width; 65 | rect.height.baseVal.value = bbox.height; 66 | rect.setAttribute("class", className); 67 | addItems.push({ parent: parent, target: rect }); 68 | } 69 | } 70 | } else if (node.matches && !node.matches("button, select, textarea")) { 71 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 72 | } 73 | }; 74 | const _highlightText = (thisNode, text, className) => { 75 | let addItems = []; 76 | _highlight(thisNode, addItems, text, className); 77 | addItems.forEach((obj) => 78 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 79 | ); 80 | }; 81 | 82 | /** 83 | * Small JavaScript module for the documentation. 84 | */ 85 | const Documentation = { 86 | init: () => { 87 | Documentation.highlightSearchWords(); 88 | Documentation.initDomainIndexTable(); 89 | Documentation.initOnKeyListeners(); 90 | }, 91 | 92 | /** 93 | * i18n support 94 | */ 95 | TRANSLATIONS: {}, 96 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 97 | LOCALE: "unknown", 98 | 99 | // gettext and ngettext don't access this so that the functions 100 | // can safely bound to a different name (_ = Documentation.gettext) 101 | gettext: (string) => { 102 | const translated = Documentation.TRANSLATIONS[string]; 103 | switch (typeof translated) { 104 | case "undefined": 105 | return string; // no translation 106 | case "string": 107 | return translated; // translation exists 108 | default: 109 | return translated[0]; // (singular, plural) translation tuple exists 110 | } 111 | }, 112 | 113 | ngettext: (singular, plural, n) => { 114 | const translated = Documentation.TRANSLATIONS[singular]; 115 | if (typeof translated !== "undefined") 116 | return translated[Documentation.PLURAL_EXPR(n)]; 117 | return n === 1 ? singular : plural; 118 | }, 119 | 120 | addTranslations: (catalog) => { 121 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 122 | Documentation.PLURAL_EXPR = new Function( 123 | "n", 124 | `return (${catalog.plural_expr})` 125 | ); 126 | Documentation.LOCALE = catalog.locale; 127 | }, 128 | 129 | /** 130 | * highlight the search words provided in the url in the text 131 | */ 132 | highlightSearchWords: () => { 133 | const highlight = 134 | new URLSearchParams(window.location.search).get("highlight") || ""; 135 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 136 | if (terms.length === 0) return; // nothing to do 137 | 138 | // There should never be more than one element matching "div.body" 139 | const divBody = document.querySelectorAll("div.body"); 140 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 141 | window.setTimeout(() => { 142 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 143 | }, 10); 144 | 145 | const searchBox = document.getElementById("searchbox"); 146 | if (searchBox === null) return; 147 | searchBox.appendChild( 148 | document 149 | .createRange() 150 | .createContextualFragment( 151 | '" 155 | ) 156 | ); 157 | }, 158 | 159 | /** 160 | * helper function to hide the search marks again 161 | */ 162 | hideSearchWords: () => { 163 | document 164 | .querySelectorAll("#searchbox .highlight-link") 165 | .forEach((el) => el.remove()); 166 | document 167 | .querySelectorAll("span.highlighted") 168 | .forEach((el) => el.classList.remove("highlighted")); 169 | const url = new URL(window.location); 170 | url.searchParams.delete("highlight"); 171 | window.history.replaceState({}, "", url); 172 | }, 173 | 174 | /** 175 | * helper function to focus on search bar 176 | */ 177 | focusSearchBar: () => { 178 | document.querySelectorAll("input[name=q]")[0]?.focus(); 179 | }, 180 | 181 | /** 182 | * Initialise the domain index toggle buttons 183 | */ 184 | initDomainIndexTable: () => { 185 | const toggler = (el) => { 186 | const idNumber = el.id.substr(7); 187 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 188 | if (el.src.substr(-9) === "minus.png") { 189 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 190 | toggledRows.forEach((el) => (el.style.display = "none")); 191 | } else { 192 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 193 | toggledRows.forEach((el) => (el.style.display = "")); 194 | } 195 | }; 196 | 197 | const togglerElements = document.querySelectorAll("img.toggler"); 198 | togglerElements.forEach((el) => 199 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 200 | ); 201 | togglerElements.forEach((el) => (el.style.display = "")); 202 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 203 | }, 204 | 205 | initOnKeyListeners: () => { 206 | // only install a listener if it is really needed 207 | if ( 208 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 209 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 210 | ) 211 | return; 212 | 213 | const blacklistedElements = new Set([ 214 | "TEXTAREA", 215 | "INPUT", 216 | "SELECT", 217 | "BUTTON", 218 | ]); 219 | document.addEventListener("keydown", (event) => { 220 | if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements 221 | if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys 222 | 223 | if (!event.shiftKey) { 224 | switch (event.key) { 225 | case "ArrowLeft": 226 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 227 | 228 | const prevLink = document.querySelector('link[rel="prev"]'); 229 | if (prevLink && prevLink.href) { 230 | window.location.href = prevLink.href; 231 | event.preventDefault(); 232 | } 233 | break; 234 | case "ArrowRight": 235 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 236 | 237 | const nextLink = document.querySelector('link[rel="next"]'); 238 | if (nextLink && nextLink.href) { 239 | window.location.href = nextLink.href; 240 | event.preventDefault(); 241 | } 242 | break; 243 | case "Escape": 244 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 245 | Documentation.hideSearchWords(); 246 | event.preventDefault(); 247 | } 248 | } 249 | 250 | // some keyboard layouts may need Shift to get / 251 | switch (event.key) { 252 | case "/": 253 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 254 | Documentation.focusSearchBar(); 255 | event.preventDefault(); 256 | } 257 | }); 258 | }, 259 | }; 260 | 261 | // quick alias for translations 262 | const _ = Documentation.gettext; 263 | 264 | _ready(Documentation.init); 265 | -------------------------------------------------------------------------------- /docs/_static/alabaster.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: Georgia, serif; 7 | font-size: 17px; 8 | background-color: #fff; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | 15 | div.document { 16 | width: 940px; 17 | margin: 30px auto 0 auto; 18 | } 19 | 20 | div.documentwrapper { 21 | float: left; 22 | width: 100%; 23 | } 24 | 25 | div.bodywrapper { 26 | margin: 0 0 0 220px; 27 | } 28 | 29 | div.sphinxsidebar { 30 | width: 220px; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #fff; 41 | color: #3E4349; 42 | padding: 0 30px 0 30px; 43 | } 44 | 45 | div.body > .section { 46 | text-align: left; 47 | } 48 | 49 | div.footer { 50 | width: 940px; 51 | margin: 20px auto 30px auto; 52 | font-size: 14px; 53 | color: #888; 54 | text-align: right; 55 | } 56 | 57 | div.footer a { 58 | color: #888; 59 | } 60 | 61 | p.caption { 62 | font-family: inherit; 63 | font-size: inherit; 64 | } 65 | 66 | 67 | div.relations { 68 | display: none; 69 | } 70 | 71 | 72 | div.sphinxsidebar a { 73 | color: #444; 74 | text-decoration: none; 75 | border-bottom: 1px dotted #999; 76 | } 77 | 78 | div.sphinxsidebar a:hover { 79 | border-bottom: 1px solid #999; 80 | } 81 | 82 | div.sphinxsidebarwrapper { 83 | padding: 18px 10px; 84 | } 85 | 86 | div.sphinxsidebarwrapper p.logo { 87 | padding: 0; 88 | margin: -10px 0 0 0px; 89 | text-align: center; 90 | } 91 | 92 | div.sphinxsidebarwrapper h1.logo { 93 | margin-top: -10px; 94 | text-align: center; 95 | margin-bottom: 5px; 96 | text-align: left; 97 | } 98 | 99 | div.sphinxsidebarwrapper h1.logo-name { 100 | margin-top: 0px; 101 | } 102 | 103 | div.sphinxsidebarwrapper p.blurb { 104 | margin-top: 0; 105 | font-style: normal; 106 | } 107 | 108 | div.sphinxsidebar h3, 109 | div.sphinxsidebar h4 { 110 | font-family: Georgia, serif; 111 | color: #444; 112 | font-size: 24px; 113 | font-weight: normal; 114 | margin: 0 0 5px 0; 115 | padding: 0; 116 | } 117 | 118 | div.sphinxsidebar h4 { 119 | font-size: 20px; 120 | } 121 | 122 | div.sphinxsidebar h3 a { 123 | color: #444; 124 | } 125 | 126 | div.sphinxsidebar p.logo a, 127 | div.sphinxsidebar h3 a, 128 | div.sphinxsidebar p.logo a:hover, 129 | div.sphinxsidebar h3 a:hover { 130 | border: none; 131 | } 132 | 133 | div.sphinxsidebar p { 134 | color: #555; 135 | margin: 10px 0; 136 | } 137 | 138 | div.sphinxsidebar ul { 139 | margin: 10px 0; 140 | padding: 0; 141 | color: #000; 142 | } 143 | 144 | div.sphinxsidebar ul li.toctree-l1 > a { 145 | font-size: 120%; 146 | } 147 | 148 | div.sphinxsidebar ul li.toctree-l2 > a { 149 | font-size: 110%; 150 | } 151 | 152 | div.sphinxsidebar input { 153 | border: 1px solid #CCC; 154 | font-family: Georgia, serif; 155 | font-size: 1em; 156 | } 157 | 158 | div.sphinxsidebar hr { 159 | border: none; 160 | height: 1px; 161 | color: #AAA; 162 | background: #AAA; 163 | 164 | text-align: left; 165 | margin-left: 0; 166 | width: 50%; 167 | } 168 | 169 | div.sphinxsidebar .badge { 170 | border-bottom: none; 171 | } 172 | 173 | div.sphinxsidebar .badge:hover { 174 | border-bottom: none; 175 | } 176 | 177 | /* To address an issue with donation coming after search */ 178 | div.sphinxsidebar h3.donation { 179 | margin-top: 10px; 180 | } 181 | 182 | /* -- body styles ----------------------------------------------------------- */ 183 | 184 | a { 185 | color: #004B6B; 186 | text-decoration: underline; 187 | } 188 | 189 | a:hover { 190 | color: #6D4100; 191 | text-decoration: underline; 192 | } 193 | 194 | div.body h1, 195 | div.body h2, 196 | div.body h3, 197 | div.body h4, 198 | div.body h5, 199 | div.body h6 { 200 | font-family: Georgia, serif; 201 | font-weight: normal; 202 | margin: 30px 0px 10px 0px; 203 | padding: 0; 204 | } 205 | 206 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 207 | div.body h2 { font-size: 180%; } 208 | div.body h3 { font-size: 150%; } 209 | div.body h4 { font-size: 130%; } 210 | div.body h5 { font-size: 100%; } 211 | div.body h6 { font-size: 100%; } 212 | 213 | a.headerlink { 214 | color: #DDD; 215 | padding: 0 4px; 216 | text-decoration: none; 217 | } 218 | 219 | a.headerlink:hover { 220 | color: #444; 221 | background: #EAEAEA; 222 | } 223 | 224 | div.body p, div.body dd, div.body li { 225 | line-height: 1.4em; 226 | } 227 | 228 | div.admonition { 229 | margin: 20px 0px; 230 | padding: 10px 30px; 231 | background-color: #EEE; 232 | border: 1px solid #CCC; 233 | } 234 | 235 | div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { 236 | background-color: #FBFBFB; 237 | border-bottom: 1px solid #fafafa; 238 | } 239 | 240 | div.admonition p.admonition-title { 241 | font-family: Georgia, serif; 242 | font-weight: normal; 243 | font-size: 24px; 244 | margin: 0 0 10px 0; 245 | padding: 0; 246 | line-height: 1; 247 | } 248 | 249 | div.admonition p.last { 250 | margin-bottom: 0; 251 | } 252 | 253 | div.highlight { 254 | background-color: #fff; 255 | } 256 | 257 | dt:target, .highlight { 258 | background: #FAF3E8; 259 | } 260 | 261 | div.warning { 262 | background-color: #FCC; 263 | border: 1px solid #FAA; 264 | } 265 | 266 | div.danger { 267 | background-color: #FCC; 268 | border: 1px solid #FAA; 269 | -moz-box-shadow: 2px 2px 4px #D52C2C; 270 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 271 | box-shadow: 2px 2px 4px #D52C2C; 272 | } 273 | 274 | div.error { 275 | background-color: #FCC; 276 | border: 1px solid #FAA; 277 | -moz-box-shadow: 2px 2px 4px #D52C2C; 278 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 279 | box-shadow: 2px 2px 4px #D52C2C; 280 | } 281 | 282 | div.caution { 283 | background-color: #FCC; 284 | border: 1px solid #FAA; 285 | } 286 | 287 | div.attention { 288 | background-color: #FCC; 289 | border: 1px solid #FAA; 290 | } 291 | 292 | div.important { 293 | background-color: #EEE; 294 | border: 1px solid #CCC; 295 | } 296 | 297 | div.note { 298 | background-color: #EEE; 299 | border: 1px solid #CCC; 300 | } 301 | 302 | div.tip { 303 | background-color: #EEE; 304 | border: 1px solid #CCC; 305 | } 306 | 307 | div.hint { 308 | background-color: #EEE; 309 | border: 1px solid #CCC; 310 | } 311 | 312 | div.seealso { 313 | background-color: #EEE; 314 | border: 1px solid #CCC; 315 | } 316 | 317 | div.topic { 318 | background-color: #EEE; 319 | } 320 | 321 | p.admonition-title { 322 | display: inline; 323 | } 324 | 325 | p.admonition-title:after { 326 | content: ":"; 327 | } 328 | 329 | pre, tt, code { 330 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 331 | font-size: 0.9em; 332 | } 333 | 334 | .hll { 335 | background-color: #FFC; 336 | margin: 0 -12px; 337 | padding: 0 12px; 338 | display: block; 339 | } 340 | 341 | img.screenshot { 342 | } 343 | 344 | tt.descname, tt.descclassname, code.descname, code.descclassname { 345 | font-size: 0.95em; 346 | } 347 | 348 | tt.descname, code.descname { 349 | padding-right: 0.08em; 350 | } 351 | 352 | img.screenshot { 353 | -moz-box-shadow: 2px 2px 4px #EEE; 354 | -webkit-box-shadow: 2px 2px 4px #EEE; 355 | box-shadow: 2px 2px 4px #EEE; 356 | } 357 | 358 | table.docutils { 359 | border: 1px solid #888; 360 | -moz-box-shadow: 2px 2px 4px #EEE; 361 | -webkit-box-shadow: 2px 2px 4px #EEE; 362 | box-shadow: 2px 2px 4px #EEE; 363 | } 364 | 365 | table.docutils td, table.docutils th { 366 | border: 1px solid #888; 367 | padding: 0.25em 0.7em; 368 | } 369 | 370 | table.field-list, table.footnote { 371 | border: none; 372 | -moz-box-shadow: none; 373 | -webkit-box-shadow: none; 374 | box-shadow: none; 375 | } 376 | 377 | table.footnote { 378 | margin: 15px 0; 379 | width: 100%; 380 | border: 1px solid #EEE; 381 | background: #FDFDFD; 382 | font-size: 0.9em; 383 | } 384 | 385 | table.footnote + table.footnote { 386 | margin-top: -15px; 387 | border-top: none; 388 | } 389 | 390 | table.field-list th { 391 | padding: 0 0.8em 0 0; 392 | } 393 | 394 | table.field-list td { 395 | padding: 0; 396 | } 397 | 398 | table.field-list p { 399 | margin-bottom: 0.8em; 400 | } 401 | 402 | /* Cloned from 403 | * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 404 | */ 405 | .field-name { 406 | -moz-hyphens: manual; 407 | -ms-hyphens: manual; 408 | -webkit-hyphens: manual; 409 | hyphens: manual; 410 | } 411 | 412 | table.footnote td.label { 413 | width: .1px; 414 | padding: 0.3em 0 0.3em 0.5em; 415 | } 416 | 417 | table.footnote td { 418 | padding: 0.3em 0.5em; 419 | } 420 | 421 | dl { 422 | margin: 0; 423 | padding: 0; 424 | } 425 | 426 | dl dd { 427 | margin-left: 30px; 428 | } 429 | 430 | blockquote { 431 | margin: 0 0 0 30px; 432 | padding: 0; 433 | } 434 | 435 | ul, ol { 436 | /* Matches the 30px from the narrow-screen "li > ul" selector below */ 437 | margin: 10px 0 10px 30px; 438 | padding: 0; 439 | } 440 | 441 | pre { 442 | background: #EEE; 443 | padding: 7px 30px; 444 | margin: 15px 0px; 445 | line-height: 1.3em; 446 | } 447 | 448 | div.viewcode-block:target { 449 | background: #ffd; 450 | } 451 | 452 | dl pre, blockquote pre, li pre { 453 | margin-left: 0; 454 | padding-left: 30px; 455 | } 456 | 457 | tt, code { 458 | background-color: #ecf0f3; 459 | color: #222; 460 | /* padding: 1px 2px; */ 461 | } 462 | 463 | tt.xref, code.xref, a tt { 464 | background-color: #FBFBFB; 465 | border-bottom: 1px solid #fff; 466 | } 467 | 468 | a.reference { 469 | text-decoration: none; 470 | border-bottom: 1px dotted #004B6B; 471 | } 472 | 473 | /* Don't put an underline on images */ 474 | a.image-reference, a.image-reference:hover { 475 | border-bottom: none; 476 | } 477 | 478 | a.reference:hover { 479 | border-bottom: 1px solid #6D4100; 480 | } 481 | 482 | a.footnote-reference { 483 | text-decoration: none; 484 | font-size: 0.7em; 485 | vertical-align: top; 486 | border-bottom: 1px dotted #004B6B; 487 | } 488 | 489 | a.footnote-reference:hover { 490 | border-bottom: 1px solid #6D4100; 491 | } 492 | 493 | a:hover tt, a:hover code { 494 | background: #EEE; 495 | } 496 | 497 | 498 | @media screen and (max-width: 870px) { 499 | 500 | div.sphinxsidebar { 501 | display: none; 502 | } 503 | 504 | div.document { 505 | width: 100%; 506 | 507 | } 508 | 509 | div.documentwrapper { 510 | margin-left: 0; 511 | margin-top: 0; 512 | margin-right: 0; 513 | margin-bottom: 0; 514 | } 515 | 516 | div.bodywrapper { 517 | margin-top: 0; 518 | margin-right: 0; 519 | margin-bottom: 0; 520 | margin-left: 0; 521 | } 522 | 523 | ul { 524 | margin-left: 0; 525 | } 526 | 527 | li > ul { 528 | /* Matches the 30px from the "ul, ol" selector above */ 529 | margin-left: 30px; 530 | } 531 | 532 | .document { 533 | width: auto; 534 | } 535 | 536 | .footer { 537 | width: auto; 538 | } 539 | 540 | .bodywrapper { 541 | margin: 0; 542 | } 543 | 544 | .footer { 545 | width: auto; 546 | } 547 | 548 | .github { 549 | display: none; 550 | } 551 | 552 | 553 | 554 | } 555 | 556 | 557 | 558 | @media screen and (max-width: 875px) { 559 | 560 | body { 561 | margin: 0; 562 | padding: 20px 30px; 563 | } 564 | 565 | div.documentwrapper { 566 | float: none; 567 | background: #fff; 568 | } 569 | 570 | div.sphinxsidebar { 571 | display: block; 572 | float: none; 573 | width: 102.5%; 574 | margin: 50px -30px -20px -30px; 575 | padding: 10px 20px; 576 | background: #333; 577 | color: #FFF; 578 | } 579 | 580 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 581 | div.sphinxsidebar h3 a { 582 | color: #fff; 583 | } 584 | 585 | div.sphinxsidebar a { 586 | color: #AAA; 587 | } 588 | 589 | div.sphinxsidebar p.logo { 590 | display: none; 591 | } 592 | 593 | div.document { 594 | width: 100%; 595 | margin: 0; 596 | } 597 | 598 | div.footer { 599 | display: none; 600 | } 601 | 602 | div.bodywrapper { 603 | margin: 0; 604 | } 605 | 606 | div.body { 607 | min-height: 0; 608 | padding: 0; 609 | } 610 | 611 | .rtd_doc_footer { 612 | display: none; 613 | } 614 | 615 | .document { 616 | width: auto; 617 | } 618 | 619 | .footer { 620 | width: auto; 621 | } 622 | 623 | .footer { 624 | width: auto; 625 | } 626 | 627 | .github { 628 | display: none; 629 | } 630 | } 631 | 632 | 633 | /* misc. */ 634 | 635 | .revsys-inline { 636 | display: none!important; 637 | } 638 | 639 | /* Make nested-list/multi-paragraph items look better in Releases changelog 640 | * pages. Without this, docutils' magical list fuckery causes inconsistent 641 | * formatting between different release sub-lists. 642 | */ 643 | div#changelog > div.section > ul > li > p:only-child { 644 | margin-bottom: 0; 645 | } 646 | 647 | /* Hide fugly table cell borders in ..bibliography:: directive output */ 648 | table.docutils.citation, table.docutils.citation td, table.docutils.citation th { 649 | border: none; 650 | /* Below needed in some edge cases; if not applied, bottom shadows appear */ 651 | -moz-box-shadow: none; 652 | -webkit-box-shadow: none; 653 | box-shadow: none; 654 | } 655 | 656 | 657 | /* relbar */ 658 | 659 | .related { 660 | line-height: 30px; 661 | width: 100%; 662 | font-size: 0.9rem; 663 | } 664 | 665 | .related.top { 666 | border-bottom: 1px solid #EEE; 667 | margin-bottom: 20px; 668 | } 669 | 670 | .related.bottom { 671 | border-top: 1px solid #EEE; 672 | } 673 | 674 | .related ul { 675 | padding: 0; 676 | margin: 0; 677 | list-style: none; 678 | } 679 | 680 | .related li { 681 | display: inline; 682 | } 683 | 684 | nav#rellinks { 685 | float: right; 686 | } 687 | 688 | nav#rellinks li+li:before { 689 | content: "|"; 690 | } 691 | 692 | nav#breadcrumbs li+li:before { 693 | content: "\00BB"; 694 | } 695 | 696 | /* Hide certain items when printing */ 697 | @media print { 698 | div.related { 699 | display: none; 700 | } 701 | } -------------------------------------------------------------------------------- /docs/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({"docnames": ["index", "termuxgui", "termuxgui.oo"], "filenames": ["index.rst", "termuxgui.rst", "termuxgui.oo.rst"], "titles": ["termuxgui Python Documentation", "termuxgui package", "termuxgui.oo package"], "terms": {"packag": 0, "modul": 0, "subpackag": 0, "oo": [0, 1], "index": [0, 1], "search": 0, "page": 0, "thi": [1, 2], "i": 1, "librari": 1, "interact": 1, "termux": [1, 2], "gui": [1, 2], "plugin": [1, 2], "from": [1, 2], "python": 1, "you": [1, 2], "don": [1, 2], "t": [1, 2], "need": 1, "includ": 1, "all": [1, 2], "submodul": 1, "class": [1, 2], "ar": 1, "automat": 1, "scope": 1, "upon": 1, "import": 1, "activ": [1, 2], "connect": [1, 2], "tid": 1, "option": [1, 2], "str": [1, 2], "none": [1, 2], "dialog": [1, 2], "bool": [1, 2], "pip": [1, 2], "fals": [1, 2], "overlai": 1, "lockscreen": [1, 2], "canceloutsid": 1, "true": [1, 2], "intercept": [1, 2], "base": [1, 2], "object": 1, "repres": [1, 2], "an": [1, 2], "android": 1, "us": [1, 2], "c": [1, 2], "access": 1, "task": [1, 2], "finish": [1, 2], "getconfigur": 1, "configur": [1, 2], "read": 1, "current": 1, "hidesoftkeyboard": 1, "hide": 1, "soft": 1, "keyboard": 1, "ha": [1, 2], "focu": 1, "interceptbackbutton": 1, "set": 1, "whether": 1, "back": [1, 2], "button": [1, 2], "press": 1, "send": 1, "event": [1, 2], "instead": 1, "keepscreenon": 1, "should": 1, "keep": 1, "screen": 1, "while": 1, "show": 1, "movetoback": 1, "move": 1, "": 1, "recent": 1, "requestunlock": 1, "request": 1, "unlock": 1, "If": 1, "just": 1, "protect": 1, "swipe": 1, "immedi": 1, "pin": 1, "password": 1, "pattern": 1, "bring": 1, "up": 1, "ui": 1, "let": 1, "user": 1, "sendoverlayev": 1, "want": [1, 2], "receiv": 1, "setinputmod": 1, "mode": 1, "liter": [1, 2], "resiz": 1, "pan": 1, "input": 1, "setorient": 1, "orient": 1, "behind": 1, "fullsensor": 1, "fullus": 1, "landscap": 1, "lock": 1, "nosensor": 1, "portrait": 1, "reverselandscap": 1, "reverseportrait": 1, "sensorlandscap": 1, "sensorportrait": 1, "userlandscap": 1, "userportrait": 1, "prefer": 1, "setpipmod": 1, "goe": 1, "out": 1, "pictur": 1, "when": [1, 2], "exit": 1, "put": 1, "list": 1, "shown": 1, "setpipmodeauto": 1, "enter": 1, "leav": 1, "setpipparam": 1, "num": 1, "int": [1, 2], "den": 1, "paramet": 1, "onli": [1, 2], "aspect": 1, "ration": 1, "setposit": 1, "x": 1, "y": 1, "posit": 1, "settaskdescript": 1, "label": 1, "img": 1, "icon": 1, "png": 1, "jpeg": 1, "imag": 1, "base64": 1, "encod": 1, "string": 1, "setthem": 1, "statusbarcolor": 1, "colorprimari": 1, "windowbackground": 1, "textcolor": 1, "coloracc": 1, "theme": 1, "buffer": 1, "w": 1, "h": 1, "format": 1, "argb888": 1, "raw": 1, "avail": 1, "8": 1, "1": [1, 2], "consum": 1, "much": 1, "memori": 1, "so": 1, "sure": 1, "close": 1, "ani": [1, 2], "more": 1, "get": 1, "The": 1, "share": 1, "can": [1, 2], "like": 1, "byte": 1, "file": 1, "also": 1, "directli": 1, "b": 1, "mem": 1, "A": 1, "runtimeerror": 1, "rais": 1, "creat": [1, 2], "blit": 1, "underli": 1, "To": 1, "updat": 1, "imageview": [1, 2], "thei": 1, "have": 1, "refresh": 1, "afterward": 1, "remov": [1, 2], "free": 1, "text": [1, 2], "parent": [1, 2], "view": [1, 2], "allcap": [1, 2], "visibl": [1, 2], "0": [1, 2], "2": [1, 2], "textview": [1, 2], "checkbox": [1, 2], "check": [1, 2], "compoundbutton": 1, "id": [1, 2], "doesn": 1, "correspond": 1, "particular": [1, 2], "provid": 1, "common": 1, "method": 1, "handleev": 1, "e": 1, "track": 1, "pass": 1, "everi": 1, "here": [1, 2], "setcheck": 1, "explicitli": 1, "statu": 1, "contain": [1, 2], "function": [1, 2], "act": [1, 2], "checkev": 1, "least": 1, "one": [1, 2], "return": [1, 2], "g": 1, "between": 1, "draw": 1, "frame": 1, "separ": 1, "thread": 1, "block": 1, "program": 1, "good": 1, "practic": 1, "yourself": 1, "anymor": 1, "statement": 1, "iter": 1, "wait": 1, "over": 1, "incom": 1, "getvers": 1, "version": 1, "code": 1, "app": 1, "disabl": 1, "featur": 1, "present": 1, "newer": 1, "prompt": 1, "islock": 1, "devic": 1, "read_msg": [], "messag": 1, "main": 1, "socket": 1, "call": 1, "yet": 1, "implement": 1, "send_msg": 1, "msg": 1, "union": 1, "dict": 1, "send_read_msg": 1, "toast": 1, "long": 1, "displai": 1, "longer": 1, "totermux": 1, "shorthand": 1, "run": 1, "am": 1, "start": 1, "turnscreenon": 1, "turn": 1, "edittext": [1, 2], "singlelin": [1, 2], "line": [1, 2], "blockinput": [1, 2], "inputtyp": [1, 2], "textmultilin": [1, 2], "phone": [1, 2], "date": [1, 2], "time": [1, 2], "datetim": [1, 2], "number": [1, 2], "numberdecim": [1, 2], "numberpassword": [1, 2], "numbersign": [1, 2], "numberdecimalsign": [1, 2], "textemailaddress": [1, 2], "textpassword": [1, 2], "showcursor": 1, "cursor": 1, "ev": 1, "variabl": 1, "type": [1, 2], "eventtyp": 1, "valu": [1, 2], "dictionari": 1, "airplan": 1, "click": 1, "config": [1, 2], "destroi": 1, "focuschang": 1, "itemselect": 1, "kei": 1, "local": 1, "longclick": 1, "notif": 1, "notificationdismiss": 1, "notificationact": 1, "overlayscal": 1, "overlaytouch": 1, "paus": 1, "pipchang": 1, "remoteclick": 1, "resum": 1, "screen_off": 1, "screenon": 1, "screen_on": 1, "select": 1, "stop": 1, "timezon": 1, "touch": 1, "touch_cancel": 1, "cancel": 1, "touch_down": 1, "down": 1, "touch_mov": 1, "touch_pointer_down": 1, "pointer_down": 1, "touch_pointer_up": 1, "pointer_up": 1, "touch_up": 1, "userleavehint": 1, "webviewconsolemessag": 1, "webviewdestroi": 1, "webviewerror": 1, "webviewhttperror": 1, "webviewnavig": 1, "webviewprogress": 1, "framelayout": [1, 2], "viewgroup": [1, 2], "gridlayout": [1, 2], "row": [1, 2], "col": [1, 2], "horizontalscrollview": [1, 2], "fillviewport": [1, 2], "snap": [1, 2], "nobar": [1, 2], "getscrollposit": 1, "scroll": 1, "setscrollposit": 1, "smooth": 1, "specifi": [1, 2], "scoll": 1, "redraw": 1, "after": 1, "setbuff": 1, "make": 1, "sourc": 1, "setimag": 1, "linearlayout": [1, 2], "vertic": [1, 2], "nestedscrollview": [1, 2], "channel": 1, "3": [1, 2], "4": [1, 2], "createchannel": 1, "notifi": 1, "setact": 1, "action": 1, "setalertonc": 1, "alertonc": 1, "than": 1, "onc": [1, 2], "alert": 1, "setcont": 1, "custom": 1, "layout": [1, 2], "setexpandedlayout": 1, "remoteview": 1, "expand": 1, "titl": 1, "largetext": 1, "largeimag": 1, "sethudlayout": 1, "hud": 1, "seticon": 1, "setlargeimag": 1, "larg": 1, "setlargeimageasthumbnail": 1, "asthumbnail": 1, "thumbnail": 1, "collaps": 1, "setlargetext": 1, "setlayout": 1, "setongo": 1, "ongo": 1, "dismiss": 1, "setshowtimestamp": 1, "timestamp": 1, "settimestamp": 1, "unix": 1, "millisecond": 1, "settitl": 1, "progressbar": [1, 2], "setprogress": 1, "progress": 1, "integ": 1, "100": 1, "inclus": 1, "radiobutton": [1, 2], "radiogroup": [1, 2], "insid": [1, 2], "emit": [1, 2], "chang": [1, 2], "remot": 1, "widget": 1, "addbutton": 1, "could": 1, "further": 1, "manipul": 1, "addframelayout": 1, "addimageview": 1, "addlinearlayout": 1, "addprogressbar": 1, "addtextview": 1, "delet": [1, 2], "setbackgroundcolor": 1, "color": 1, "background": 1, "setpad": 1, "left": 1, "top": 1, "right": 1, "bottom": 1, "pad": 1, "max": 1, "maximum": 1, "settext": 1, "settextcolor": 1, "settexts": 1, "size": 1, "px": 1, "pixel": 1, "dip": 1, "setvis": 1, "vi": 1, "gone": 1, "hidden": 1, "still": 1, "take": 1, "space": [1, 2], "do": 1, "updatewidget": 1, "wid": 1, "empti": [1, 2], "your": [1, 2], "spinner": [1, 2], "setlist": 1, "item": 1, "swiperefreshlayout": [1, 2], "setrefresh": 1, "anim": 1, "switch": [1, 2], "tablayout": [1, 2], "selecttab": 1, "tab": 1, "It": 1, "bringtofront": 1, "front": 1, "might": 1, "requir": 1, "other": 1, "permiss": 1, "selectabletext": [1, 2], "clickablelink": [1, 2], "gettext": 1, "sendtextev": 1, "ot": 1, "setgrav": 1, "horizont": 1, "graviti": 1, "center": 1, "invert": 1, "same": 1, "togglebutton": [1, 2], "gener": [1, 2], "constant": 1, "match_par": 1, "special": 1, "wrap_cont": 1, "forcesoft": 1, "open": 1, "getdimens": 1, "width": 1, "height": 1, "handl": 1, "subclass": 1, "overrid": 1, "sendclickev": 1, "sent": 1, "sendfocuschangeev": 1, "sendlongclickev": 1, "sendtouchev": 1, "setclick": 1, "clickabl": 1, "setdimens": 1, "dimens": 1, "setwidth": 1, "setheight": 1, "setgridlayoutparam": 1, "rowsiz": 1, "colsiz": 1, "alignmentrow": 1, "baselin": 1, "fill": 1, "alignmentcol": 1, "column": 1, "defin": 1, "how": 1, "mani": 1, "cell": 1, "its": 1, "either": 1, "describ": 1, "dp": 1, "setlinearlayoutparam": 1, "weight": 1, "setmargin": 1, "margin": 1, "dir": 1, "direct": 1, "clearchildren": [1, 2], "child": [1, 2], "webview": [1, 2], "allowcontenturi": 1, "allow": 1, "load": 1, "uri": 1, "allowjavascript": 1, "javascript": 1, "execut": 1, "deni": 1, "until": 1, "respond": 1, "wheter": 1, "enabl": 1, "allownavig": 1, "navig": 1, "differ": 1, "site": 1, "evaluatej": 1, "goback": 1, "histori": 1, "goforward": 1, "forward": 1, "loaduri": 1, "setdata": 1, "data": 1, "document": 1, "abc": 2, "abstract": 2, "enum": 2, "enumer": 2, "dialog_no_cancel_outsid": 2, "5": 2, "normal": 2, "get_typ": 2, "intercept_back": 2, "on_back": 2, "on_config": 2, "on_creat": 2, "on_destroi": 2, "on_paus": 2, "on_pipchang": 2, "on_resum": 2, "on_start": 2, "on_stop": 2, "on_userleavehint": 2, "event_loop": 2, "launch": 2, "arg": 2, "kwarg": 2, "selectitem": 1}, "objects": {"": [[1, 0, 0, "-", "termuxgui"]], "termuxgui": [[1, 1, 1, "", "Activity"], [1, 1, 1, "", "Buffer"], [1, 1, 1, "", "Button"], [1, 1, 1, "", "Checkbox"], [1, 1, 1, "", "CompoundButton"], [1, 1, 1, "", "Connection"], [1, 1, 1, "", "EditText"], [1, 1, 1, "", "Event"], [1, 1, 1, "", "FrameLayout"], [1, 1, 1, "", "GridLayout"], [1, 1, 1, "", "HorizontalScrollView"], [1, 1, 1, "", "ImageView"], [1, 1, 1, "", "LinearLayout"], [1, 1, 1, "", "NestedScrollView"], [1, 1, 1, "", "Notification"], [1, 1, 1, "", "ProgressBar"], [1, 1, 1, "", "RadioButton"], [1, 1, 1, "", "RadioGroup"], [1, 1, 1, "", "RemoteViews"], [1, 1, 1, "", "Space"], [1, 1, 1, "", "Spinner"], [1, 1, 1, "", "SwipeRefreshLayout"], [1, 1, 1, "", "Switch"], [1, 1, 1, "", "TabLayout"], [1, 1, 1, "", "Task"], [1, 1, 1, "", "TextView"], [1, 1, 1, "", "ToggleButton"], [1, 1, 1, "", "View"], [1, 1, 1, "", "ViewGroup"], [1, 1, 1, "", "WebView"], [2, 0, 0, "-", "oo"]], "termuxgui.Activity": [[1, 2, 1, "", "finish"], [1, 2, 1, "", "getconfiguration"], [1, 2, 1, "", "hidesoftkeyboard"], [1, 2, 1, "", "interceptbackbutton"], [1, 2, 1, "", "keepscreenon"], [1, 2, 1, "", "movetoback"], [1, 2, 1, "", "requestunlock"], [1, 2, 1, "", "sendoverlayevents"], [1, 2, 1, "", "setinputmode"], [1, 2, 1, "", "setorientation"], [1, 2, 1, "", "setpipmode"], [1, 2, 1, "", "setpipmodeauto"], [1, 2, 1, "", "setpipparams"], [1, 2, 1, "", "setposition"], [1, 2, 1, "", "settaskdescription"], [1, 2, 1, "", "settheme"]], "termuxgui.Buffer": [[1, 2, 1, "", "blit"], [1, 2, 1, "", "remove"]], "termuxgui.CompoundButton": [[1, 2, 1, "", "handleevent"], [1, 2, 1, "", "setchecked"]], "termuxgui.Connection": [[1, 2, 1, "", "checkevent"], [1, 2, 1, "", "close"], [1, 2, 1, "", "events"], [1, 2, 1, "", "getversion"], [1, 2, 1, "", "islocked"], [1, 2, 1, "", "send_msg"], [1, 2, 1, "", "send_read_msg"], [1, 2, 1, "", "toast"], [1, 2, 1, "", "totermux"], [1, 2, 1, "", "turnscreenon"]], "termuxgui.EditText": [[1, 2, 1, "", "showcursor"]], "termuxgui.Event": [[1, 3, 1, "", "airplane"], [1, 3, 1, "", "back"], [1, 3, 1, "", "click"], [1, 3, 1, "", "config"], [1, 3, 1, "", "create"], [1, 3, 1, "", "destroy"], [1, 3, 1, "", "focusChange"], [1, 3, 1, "", "itemselected"], [1, 3, 1, "", "key"], [1, 3, 1, "", "locale"], [1, 3, 1, "", "longClick"], [1, 3, 1, "", "notification"], [1, 3, 1, "", "notificationDismissed"], [1, 3, 1, "", "notificationaction"], [1, 3, 1, "", "overlayScale"], [1, 3, 1, "", "overlaytouch"], [1, 3, 1, "", "pause"], [1, 3, 1, "", "pipchanged"], [1, 3, 1, "", "refresh"], [1, 3, 1, "", "remoteclick"], [1, 3, 1, "", "resume"], [1, 3, 1, "", "screen_off"], [1, 3, 1, "", "screenon"], [1, 3, 1, "", "selected"], [1, 3, 1, "", "start"], [1, 3, 1, "", "stop"], [1, 3, 1, "", "text"], [1, 3, 1, "", "timezone"], [1, 3, 1, "", "touch"], [1, 3, 1, "", "touch_cancel"], [1, 3, 1, "", "touch_down"], [1, 3, 1, "", "touch_move"], [1, 3, 1, "", "touch_pointer_down"], [1, 3, 1, "", "touch_pointer_up"], [1, 3, 1, "", "touch_up"], [1, 3, 1, "", "userleavehint"], [1, 3, 1, "", "webviewConsoleMessage"], [1, 3, 1, "", "webviewDestroyed"], [1, 3, 1, "", "webviewError"], [1, 3, 1, "", "webviewHTTPError"], [1, 3, 1, "", "webviewNavigation"], [1, 3, 1, "", "webviewProgress"]], "termuxgui.HorizontalScrollView": [[1, 2, 1, "", "getscrollposition"], [1, 2, 1, "", "setscrollposition"]], "termuxgui.ImageView": [[1, 2, 1, "", "refresh"], [1, 2, 1, "", "setbuffer"], [1, 2, 1, "", "setimage"]], "termuxgui.NestedScrollView": [[1, 2, 1, "", "getscrollposition"], [1, 2, 1, "", "setscrollposition"]], "termuxgui.Notification": [[1, 2, 1, "", "cancel"], [1, 2, 1, "", "createchannel"], [1, 2, 1, "", "notify"], [1, 2, 1, "", "setactions"], [1, 2, 1, "", "setalertonce"], [1, 2, 1, "", "setcontent"], [1, 2, 1, "", "setexpandedlayout"], [1, 2, 1, "", "sethudlayout"], [1, 2, 1, "", "seticon"], [1, 2, 1, "", "setlargeimage"], [1, 2, 1, "", "setlargeimageasthumbnail"], [1, 2, 1, "", "setlargetext"], [1, 2, 1, "", "setlayout"], [1, 2, 1, "", "setongoing"], [1, 2, 1, "", "setshowtimestamp"], [1, 2, 1, "", "settimestamp"], [1, 2, 1, "", "settitle"]], "termuxgui.ProgressBar": [[1, 2, 1, "", "setprogress"]], "termuxgui.RemoteViews": [[1, 2, 1, "", "addButton"], [1, 2, 1, "", "addFrameLayout"], [1, 2, 1, "", "addImageView"], [1, 2, 1, "", "addLinearLayout"], [1, 2, 1, "", "addProgressBar"], [1, 2, 1, "", "addTextView"], [1, 2, 1, "", "delete"], [1, 2, 1, "", "setBackgroundColor"], [1, 2, 1, "", "setImage"], [1, 2, 1, "", "setPadding"], [1, 2, 1, "", "setProgress"], [1, 2, 1, "", "setText"], [1, 2, 1, "", "setTextColor"], [1, 2, 1, "", "setTextSize"], [1, 2, 1, "", "setVisibility"], [1, 2, 1, "", "updateWidget"]], "termuxgui.Spinner": [[1, 2, 1, "", "selectitem"], [1, 2, 1, "", "setlist"]], "termuxgui.SwipeRefreshLayout": [[1, 2, 1, "", "setrefreshing"]], "termuxgui.TabLayout": [[1, 2, 1, "", "selecttab"], [1, 2, 1, "", "setlist"]], "termuxgui.Task": [[1, 2, 1, "", "bringtofront"], [1, 2, 1, "", "finish"]], "termuxgui.TextView": [[1, 2, 1, "", "gettext"], [1, 2, 1, "", "sendtextevent"], [1, 2, 1, "", "setgravity"], [1, 2, 1, "", "settext"], [1, 2, 1, "", "settextcolor"], [1, 2, 1, "", "settextsize"]], "termuxgui.View": [[1, 3, 1, "", "GONE"], [1, 3, 1, "", "HIDDEN"], [1, 3, 1, "", "MATCH_PARENT"], [1, 3, 1, "", "VISIBLE"], [1, 3, 1, "", "WRAP_CONTENT"], [1, 2, 1, "", "delete"], [1, 2, 1, "", "focus"], [1, 2, 1, "", "getdimensions"], [1, 2, 1, "", "handleevent"], [1, 2, 1, "", "sendclickevent"], [1, 2, 1, "", "sendfocuschangeevent"], [1, 2, 1, "", "sendlongclickevent"], [1, 2, 1, "", "sendtouchevent"], [1, 2, 1, "", "setbackgroundcolor"], [1, 2, 1, "", "setclickable"], [1, 2, 1, "", "setdimensions"], [1, 2, 1, "", "setgridlayoutparams"], [1, 2, 1, "", "setheight"], [1, 2, 1, "", "setlinearlayoutparams"], [1, 2, 1, "", "setmargin"], [1, 2, 1, "", "setvisibility"], [1, 2, 1, "", "setwidth"]], "termuxgui.ViewGroup": [[1, 2, 1, "", "clearchildren"]], "termuxgui.WebView": [[1, 2, 1, "", "allowcontenturi"], [1, 2, 1, "", "allowjavascript"], [1, 2, 1, "", "allownavigation"], [1, 2, 1, "", "evaluatejs"], [1, 2, 1, "", "goback"], [1, 2, 1, "", "goforward"], [1, 2, 1, "", "loaduri"], [1, 2, 1, "", "setdata"]], "termuxgui.oo": [[2, 1, 1, "", "Activity"], [2, 1, 1, "", "Button"], [2, 1, 1, "", "Checkbox"], [2, 1, 1, "", "Connection"], [2, 1, 1, "", "EditText"], [2, 1, 1, "", "FrameLayout"], [2, 1, 1, "", "GridLayout"], [2, 1, 1, "", "HorizontalScrollView"], [2, 1, 1, "", "ImageView"], [2, 1, 1, "", "LinearLayout"], [2, 1, 1, "", "NestedScrollView"], [2, 1, 1, "", "ProgressBar"], [2, 1, 1, "", "RadioButton"], [2, 1, 1, "", "RadioGroup"], [2, 1, 1, "", "Space"], [2, 1, 1, "", "Spinner"], [2, 1, 1, "", "SwipeRefreshLayout"], [2, 1, 1, "", "Switch"], [2, 1, 1, "", "TabLayout"], [2, 1, 1, "", "TextView"], [2, 1, 1, "", "ToggleButton"], [2, 1, 1, "", "View"], [2, 1, 1, "", "ViewGroup"], [2, 1, 1, "", "WebView"]], "termuxgui.oo.Activity": [[2, 1, 1, "", "Type"], [2, 2, 1, "", "get_type"], [2, 2, 1, "", "intercept_back"], [2, 2, 1, "", "on_back"], [2, 2, 1, "", "on_config"], [2, 2, 1, "", "on_create"], [2, 2, 1, "", "on_destroy"], [2, 2, 1, "", "on_pause"], [2, 2, 1, "", "on_pipchanged"], [2, 2, 1, "", "on_resume"], [2, 2, 1, "", "on_start"], [2, 2, 1, "", "on_stop"], [2, 2, 1, "", "on_userleavehint"]], "termuxgui.oo.Activity.Type": [[2, 3, 1, "", "DIALOG"], [2, 3, 1, "", "DIALOG_NO_CANCEL_OUTSIDE"], [2, 3, 1, "", "LOCKSCREEN"], [2, 3, 1, "", "NORMAL"], [2, 3, 1, "", "PIP"]], "termuxgui.oo.Connection": [[2, 2, 1, "", "event_loop"], [2, 2, 1, "", "launch"]], "termuxgui.oo.View": [[2, 2, 1, "", "delete"]], "termuxgui.oo.ViewGroup": [[2, 2, 1, "", "clearchildren"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:attribute"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "attribute", "Python attribute"]}, "titleterms": {"welcom": [], "termuxgui": [0, 1, 2], "": [], "document": 0, "content": [0, 1, 2], "indic": 0, "tabl": 0, "packag": [1, 2], "modul": [1, 2], "subpackag": 1, "oo": 2, "python": 0}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 56}}) --------------------------------------------------------------------------------