Awesome
Carta
Python library for making GUI applications on the reMarkable tablet wrapped around the "simple" application
Simulating the ReMarkable device
It can be a pain having to transfer the script & run the script from the RM each time. Especially if you want to use the device for something else at the time. To help with this, I've made the option to manually set the command to a simple
x64 binary that can be run on linux which will write to fb.pnm
- This is the framebuffer file that you'll need to read from.
In the misc
folder I've provided the binary and a basic script as an example. To use this, just initialize the ReMarkable object like so:
rm = ReMarkable(simple="path/to/simple/binay")
Finally, checkout remarkable-sim for events (like button clicking) support
Using Carta
Carta's purpose is to function as a Python wrapper around sas - the simple app script for making apps on the reMarkable without need to compiling code. It's written in python and is designed to be as user-friendly and easy to use as possible.
To use it, you'll need to install a few things onto your reMarkable, in an SSH session:
toltec
- an unofficial reMarkable-specific package manager See instructions on toltec's website.simple
opkg install simple
- rM2 users need
display
opkg install display
pip
- the package manager for pythonwget https://bootstrap.pypa.io/get-pip.py python get-pip.py
carta
- this librarypip install carta
After having installed all the prerequisites, you are ready to start developing.
Make a python script on the rM and run chmod +x
on the script to make it executable.
You'll also need to add a shebang
to the script (#!/opt/bin/python
at the top) so that it knows what to run it with.
Here's an example script that you can copy:
#!/opt/bin/python
from carta import ReMarkable, Widget
rm = ReMarkable()
my_button = Widget(id="but1", typ="button", value="Hello!", x="50%", y="50%")
rm.add(my_button)
rm.display()
Let's walk through it line by line,
from carta import ReMarkable, Widget
rm = ReMarkable()
This imports the necessary "parts" for the library to function. The ReMarkable
class is the main "driver" code and handles everything from interacting with simple
to display your app to managing the widgets themselves. The widget
dataclass are the actual contents themselves and there are 6 core types;
- Label
- Paragraph
- Button
- TextInput
- Text Area
- Image
Please refer to the simple
spec for more detailed information.
my_button = Widget(id="but1", typ="button", value="Hello!", x="50%", y="50%")
Here we are making a new Widget
which we have assigned to my_button
.
The following is a table outlining information about supported arguments which can be given.
Argument | Required | Use | Supported inputs | Notes |
---|---|---|---|---|
id | Yes | Used to uniquely identify a widget when being interacted with | Any string that hasn't been used before | Spaces will be converted to _ |
typ | Yes | Gives a widget specific properties so it knows what to be used as | Any from the following: label , paragraph , burtton , textinput , textarea , image | Range is a WIP |
value | Yes | What will be displayed on the widget | Any string (Image path for the image widget type) | Be careful about using new lines |
x | Yes | Used to calculate the rendering position of the widget | A percentage, keyword or an integer | Supported keywords are same (same value as previous item) and step (previous item's cord + previous items size) |
y | Yes | Used to calculate the rendering position of the widget | A percentage, keyword or an integer | Supported keywords are same (same value as previous item) and step (previous item's cord + previous items size) |
width | No (Automatically calculated) | The width of the widget | Any integer | You might have some trouble with getting the right value for this |
height | No (Automatically calculated) | The height of the widget | Any integer | You might have some trouble with getting the right value for this |
justify | No (Optional for buttons) | Where the text sits in a button | Any string from the following: center , right , left |
Finally, we want to add the widget to the actual screen.
rm.add(my_button)
rm.display()
I've made the decision to have widgets be manually added like this to make it easier to manage when making apps).
We can do this easily with add(args*)
, where args*
are any Widget
types - For example,
rm.add(my_button, label1, logo)
And then display it to the rM! The display()
function will take all the currently active Widgets
and convert them into something that simple can understand and render. The active widgets are all stored in the screen
list (in this case rm.screen
) and you can easily access and modify any of the widgets in there at any time via the lookup
function which will return the Widget
object that has the corresponding ID
print(rm.lookup("<id>"))
To remove a Widget
you can use the remove()
function and either supply it an ID
or Widget
object like so
rm.remove("<id>")
rm.remove(my_button)
In case you want to fully clear the screen, you can use the eclear()
function - however this does require the modified updated simple binary which okeh
can provide for you!
rm.eclear()
Finally, there are two other options that the ReMarkable
class has that we can modify.
- Fontsize - Changes the fontsize of the text on the screen
- Timeout - The time after
simple
quits if no input is given- In this case, carrying on the program after
rm.display()
has been run and no input has been given after a set time
- In this case, carrying on the program after
For example, if we wanted to make the fontsize
24 and timeout the program after 30
seconds we would do
rm.timeout = 32
rm.fontsize = 24
When you run the display
function, carta will wait on any input being given OR the timeout being reached. When it's done this, it will return a dict
object with the input given.
clicked = rm.display()
print(clicked) # {"<id>": True}
If it's a button
that has been pressed it will return True
as the value, if anything else has been given it will use that specific input (for example, from the keyboard) as the value.
Hope this guide was helpful, feel free to ask if you need some help (jake.sh
on discord)