-
-
Notifications
You must be signed in to change notification settings - Fork 0
Apps
Applications (Apps) are essentially just Python modules which implement the method register(). A Python module is no more than a folder containing an empty init.py and the Python script with the functionality and the register() method. Typically, an application inherits from the Kapp class, providing it some basic functionality and services. Applications can have a GUI, but there is no need to. They can be listed in the Launcher, but there is also no need for that. Almost everything in kapps is implemented as apps. That includes Installer, Uninstaller, Launcher, Notifications, etc. All these can be replaced by others, if required.
Lets learn how to develop apps by looking at a very simple app, the Quit app. This app has a shortcut in the Launcher, but no own GUI. When started, it simply publishes the Quit() command, leading to the termination of kapps by the core.
The Quit app has the following folder structure:
Quit/
├─ res/
│ ├─ icon.png
├─ __init__.py
├─ quit.py
Technically, the icon is not even required. A basic icon is always given, but lets have our app look a little nice. We thus add an icon.png file in the res/ folder. This is helpful, as there is some built-in support to serve resources from this folder. If you would like to use a different folder for resources, just overwrite the resourceCallback() in your application. We also require an empty init.py to declare this a Python module and of course the quit.py for the functionality.
Looking into Quit.py, we find:
from core.kapp import Kapp
from core.commands import Quit, Launcher
from core.httpResponse import HTTPResponse
class QuitApp(Kapp):
name = "Quit"
def homeCallback(self, kcommand):
self.publish(Quit())
return self.publish(Launcher())[0]
def iconCallback(self, kcommand):
return HTTPResponse(content=self.getRes("icon.png"))
def register(appID, appPath, ctx):
return QuitApp(appID, appPath, ctx)
Lets start analyzing this from the bottom:
- First, we find the register method, instantiating a QuitApp() object, passing through some parameters and returning it.
- Above that is the QuitApp class, inheriting from the Kapp class and implementing the homeCallback() and iconCallback() methods. These are overriding the corresponding methods of Kapp to change the app start behavior, as well as the icon.
- In homeCallback() you find, that first, the Quit command is published.
- Then, the Launcher command is published and returned. This setup you might see often, effectively, this is a forwarding of commands. Thus, when starting the Quit app, it will return straight back to the Launcher, after publishing the Quit command
- In QuitApp, you also find the IconCallback() being overriden. This is called whenever the icon of the app is required. While in most cases, this callback might serve a file from the res/ folder, in some cases, different icons might be served, e.g., to show an update in the app (see e.g., Notifications app).
- Also note, how the getRes() method is used to retrieve the icon from the res/ folder. This is a service method in Kapp and allows to work independent of the path where the application is stored. Paths will be handled by kapps.
A GUI app is basically identical to a normal app, just that it returns some HTML to be shown to the user, e.g., in the homeCallback(). Take a look at the folder structure of TestApp:
TestApp/
├─ res/
│ ├─ icon.png
│ ├─ testApp.html
├─ __init__.py
├─ testApp.py
This is a common pattern. In testApp.html, you will find an HTML template to be used and potentially altered by testApp.py. Lets take a look at testApp.html:
<html>
<head>
<link rel="stylesheet" href="/core/res/base.css">
</head>
<body>
<table>
<tbody>
<tr>
<td><a href="/"><h1>X</h1></a></td>
<td><h1 align="center">TestApp</h1></td>
<td></td>
</tr>
</tbody>
</table>
<h2 align="center">This is some TestApp content</h2>
$CONTENT$
</body>
</html>
Note the "$CONTENT$". This will be replaced later by testApp.py. Also note the stylesheet from /core/res/base.css. This is open to use by any application and is supposed to give them a somewhat unified look. See also GUI Design Guidelines. Also note the link in the first table cell linking to "/", this is a shortcut that is caught by kapps and redirected to the Launcher command.
Now lets look at testApp.py:
from core.kapp import Kapp
from core.commands import Notify, Launcher
from core.httpResponse import HTTPResponse
class TestApp(Kapp):
name = "TestApp"
def homeCallback(self, kcommand):
self.publish(Notify().setParam("title", "Test").setParam(
"message", "TestApp started"))
content = "<a href=\"" + Launcher().toURL() + "\">Quit</a>"
return HTTPResponse(content=self.getRes("testApp.html").replace("$CONTENT$", content))
def iconCallback(self, kcommand):
return HTTPResponse(content=self.getRes("icon.png"))
def register(appID, appPath, ctx):
return TestApp(appID, appPath, ctx)
While you notice that the basic structure is identical to the above Quit app, there are some differences:
- In homeCallback(), a Notification is published. Notify is a system command, that can be used by any application to send a notification. A Notifications app for viewing these is provided. However, any app can subscribe to these notifications.
- The above testApp.html is loaded by getRes() in homeCallback and the "$CONTENT$" string is replaced with a link to the Launcher command. This is rather powerful, as it allows to call commands from straight from the GUI, e.g., to start other apps (such as the Launcher in this case), or to pass control and information to other parts of the app.
The following settings are possible for your app.
You may hide your app from the Launcher, by overriding needsLauncherEntry(), like this:
def needsLauncherEntry(self):
return False
Of course you can manually copy the folder to the kapps/apps/ directory, but kapps also comes with a built-in downloader and installer for applications. This section will show the requirements to use it.
Apps are distributed as *.zip files. The zip files needs to contain at least the app folder, as well as a manifest.kapp file describing the application, e.g.:
quit.zip
├─ quit/
│ ├─ res/
│ │ ├─ icon.png
│ ├─ __init__.py
│ ├─ quit.py
├─ manifest.kapp
The manifest file is currently rudimentary and will likely be extended in future. For the example above, it would look like this:
{
"app": {
"name": "Quit",
"folder": "quit"
}
}
It is important that the correct folder containing the app is specified, as there might be multiple folders (e.g., documentation, tests, ...) in the *.zip file. Only the given folder is copied to the apps/ directory and loaded.
To make distribution even easier and as no binaries are required, you may serve your *.zip file directly from the repository, e.g., Github. On GitHub, the adress is along the lines of "https://codeload.github.com/PhilippMundhenk/kapps-quit/zip/main" where PhilippMundhenk is the repository owner and kapps-quit is the repository name. This will download the main branch.
Finally, to not have to take care of any infrastructure, a basic infrastructure for downloading and installing applications is already in place, all based on GitHub. The Installer app reads the file list in the GitHub (kapps-list)[https://github.com/PhilippMundhenk/kapps-list] repository. You may enter your application there via a pull request. It will then be shown in the Installer app. The format of the file is as follows:
{
"apps": [{
"name": "Alarm",
"package": "https://codeload.github.com/PhilippMundhenk/kapps-alarm/zip/main",
"website": "https://github.com/PhilippMundhenk/kapps-alarm"
},
{
"name": "Quit",
"package": "https://codeload.github.com/PhilippMundhenk/kapps-quit/zip/main",
"website": "https://github.com/PhilippMundhenk/kapps-quit"
}]
}
Simply add another entry with your application name, its URL and website, if existing.
You can then install your app with a simple button press in the Installer app.
kapps Documentation, license: MIT, website: https://github.com/PhilippMundhenk/kapps