Skip to main content

Create desktop applications with Flask/Django/FastAPI!

Project description

Flaskwebgui

Downloads PyPI

Create desktop applications with Flask/FastAPI/Django!

Table of Contents generated with DocToc

Install

pip install flaskwebgui

Usage with Flask

Let's say we have the following flask application:

#main.py

from flask import Flask
from flask import render_template
from flaskwebgui import FlaskUI # import FlaskUI

app = Flask(__name__)


@app.route("/")
def hello():
    return render_template('index.html')

@app.route("/home", methods=['GET'])
def home():
    return render_template('some_page.html')


if __name__ == "__main__":
  # If you are debugging you can do that in the browser:
  # app.run()
  # If you want to view the flaskwebgui window:
  FlaskUI(app=app, server="flask").run()

Install waitress for more performance.

Usage with Flask-SocketIO

Let's say we have the following SocketIO application:

#main.py
from flask import Flask, render_template
from flask_socketio import SocketIO
from flaskwebgui import FlaskUI


app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

@app.route("/")
def hello():
    return render_template('index.html')

@app.route("/home", methods=['GET'])
def home():
    return render_template('some_page.html')


if __name__ == '__main__':
    # socketio.run(app) for development
    FlaskUI(
        app=app,
        socketio=socketio,
        server="flask_socketio",
        width=800,
        height=600,
    ).run()

App will be served by flask_socketio.

Usage with FastAPI

Pretty much the same, below you have the main.py file:

#main.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi import FastAPI
from flaskwebgui import FlaskUI

app = FastAPI()

# Mounting default static files
app.mount("/public", StaticFiles(directory="dist/"))
templates = Jinja2Templates(directory="dist")


@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})


@app.get("/home", response_class=HTMLResponse)
async def home(request: Request):
    return templates.TemplateResponse("some_page.html", {"request": request})


if __name__ == "__main__":

    FlaskUI(app=app, server="fastapi").run()

FastApi will be served by uvicorn.

Usage with Django

Install waitress and whitenoise to make it work nicely.

In the settings.py file you need to do the following.

Configure static/media files:

STATIC_URL = "static/"
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

if not os.path.exists(MEDIA_ROOT):
    os.makedirs(MEDIA_ROOT)

Add whitenoise to MIDDLEWARE (to handle static files):

MIDDLEWARE = [
    "whitenoise.middleware.WhiteNoiseMiddleware",
    ...
]

Next to manage.py file create a gui.py file where you need to import application from project's wsgi.py file.

├── project_name
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── gui.py # this
├── manage.py

In gui.py file add below code.

#gui.py
from flaskwebgui import FlaskUI
from djangodesktop.wsgi import application as app

if __name__ == "__main__":
    FlaskUI(app=app, server="django").run()

Next start the application with:

python gui.py

Close application using a route

You can close the application using the close_application from flaskwebgui.

from flaskwebgui import FlaskUI, close_application

# Any python webframework routing here

@app.route("/close", methods=["GET"])
def close_window():
    close_application()

And somewhere a link:

<a href="/close" class="exit" role="button"> CLOSE </a>

Prevent users from opening browser console

Add below js script to your index.html file to prevent users from opening the browser console. You can extend it to prevent other browser specific features.

Native like features are pretty hard to implement because we have access only to javascript.

See issue 135.

<script>

    // Prevent F12 key
    document.onkeydown = function (e) {
        if (e.key === "F12") {
            e.preventDefault();
        }
    };

    // Prevent right-click
    document.addEventListener("contextmenu", function (e) {
        e.preventDefault();
    });

</script>

Configurations

Default FlaskUI class parameters:

  • server: Union[str, Callable[[Any], None]]: function which receives server_kwargs to start server (see examples folder);
  • server_kwargs: dict = None: kwargs which will be passed down to server function;
  • app: Any = None: wsgi or asgi app;
  • port: int = None: specify port if not a free port will set;
  • width: int = None: width of the window;
  • height: int = None: height of the window;
  • fullscreen: bool = True: start app in fullscreen (maximized);
  • on_startup: Callable = None: function to before starting the browser and webserver;
  • on_shutdown: Callable = None: function to after the browser and webserver shutdown;
  • extra_flags: List[str] = None: list of additional flags for the browser command;
  • browser_path: str = None: set path to chrome executable or let the defaults do that;
  • browser_command: List[str] = None: command line with starts chrome in app mode (example of browser command: ["/path/to/browser-executable", "--user-data-dir=/path/to/profile", "--new-window", "--no-default-browser-check", "--allow-insecure-localhost", "--no-first-run", "--disable-sync", "--window-size=800,600", "--app=http://127.0.0.1:46899"]);
  • socketio: Any = None: socketio instance in case of flask_socketio;
  • app_mode: bool = True: by defaults stats in app mode (browser without address bar) if false will start the browser in guest mode (with address bar);
  • browser_pid: int = None: when the app starts browser_pid will be filled with the pid of the process opened with subprocess.Popen;
  • auto_close: bool = True: by default if browser is closed the server will close as well, setting this to False will leave the server opened;

Develop your app as you would normally do, add flaskwebgui at the end or for tests. flaskwebgui doesn't interfere with your way of doing an application it just helps "converting" it into a desktop app more easily with pyinstaller or pyvan.

Advanced Usage

You can plug in any python webframework you want just by providing a function to start the server in server FlaskUI parameter which will be feed server_kwargs.

Example:

# until here is the flask example from above

def start_flask(**server_kwargs):

    app = server_kwargs.pop("app", None)
    server_kwargs.pop("debug", None)

    try:
        import waitress

        waitress.serve(app, **server_kwargs)
    except:
        app.run(**server_kwargs)


if __name__ == "__main__":

    # Custom start flask

    def saybye():
        print("on_exit bye")

    FlaskUI(
        server=start_flask,
        server_kwargs={
            "app": app,
            "port": 3000,
            "threaded": True,
        },
        width=800,
        height=600,
        on_shutdown=saybye,
    ).run()

In this way any webframework can be plugged in and the webframework can be started in a more customized manner.

Here is another example with the nicegui package:

from flaskwebgui import FlaskUI
from nicegui import ui

ui.label("Hello Super NiceGUI!")
ui.button("BUTTON", on_click=lambda: ui.notify("button was pressed"))

def start_nicegui(**kwargs):
    ui.run(**kwargs)

if __name__ in {"__main__", "__mp_main__"}:
    DEBUG = False

    if DEBUG:
        ui.run()
    else:
        FlaskUI(
            server=start_nicegui,
            server_kwargs={"dark": True, "reload": False, "show": False, "port": 3000},
            width=800,
            height=600,
        ).run()

Checkout examples for more information.

Distribution

You can distribute it as a standalone desktop app with pyinstaller or pyvan. If pyinstaller failes try pyinstaller version 5.6.2.

pyinstaller -w -F  main.py

After the command finishes move your files (templates, js,css etc) to the dist folder created by pyinstaller. Or add them into the executable: pyinstaller --name your-app-name --add-data "dbsqlite:dbsqlite" --add-data "templates:templates" --add-data "static:static" --collect-all name_of_package_that_pyinstaller_did_not_found gui.py (for Windows change : with ;).

If you want your desktop application to be installed via snap or flatpack (Linux) checkout:

Made by William Moreno.

Observations

  • Parameters width, height and maybe fullscreen may not work on Mac;
  • Window control is limited to width, height, fullscreen;
  • Remember the gui is still a browser - pressing F5 will refresh the page + other browser specific things (you can hack it with js though);
  • You don't need production level setup with gunicorn etc - you just have one user to serve;
  • If you want to debug/reload features - just run it as you would normally do with app.run(**etc), uvicorn.run(**etc), python manage.py runserver etc. flaskwebgui does not provide auto-reload you already have it in the webframework you are using;

Credits

It's a combination of https://github.com/Widdershin/flask-desktop and https://github.com/ChrisKnott/Eel

It has some advantages over flask-desktop because it doesn't use PyQt5, so you won't have any issues regarding licensing and over Eel because you don't need to learn any logic other than Flask/Django/FastAPI/etc.

Submit any questions/issues you have! Fell free to fork it and improve it!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flaskwebgui-1.1.9.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flaskwebgui-1.1.9-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file flaskwebgui-1.1.9.tar.gz.

File metadata

  • Download URL: flaskwebgui-1.1.9.tar.gz
  • Upload date:
  • Size: 9.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for flaskwebgui-1.1.9.tar.gz
Algorithm Hash digest
SHA256 3a5dfea6bb58479beced817cb3c7adeffba7f4965273c1cb00f728aeb9566ce1
MD5 15a47c788f2de7f4277d98f1a95ead70
BLAKE2b-256 ba3b6a81bb31370deb3a92a9cd36c3b2f83827b3cb126815a3414c39f1a70002

See more details on using hashes here.

File details

Details for the file flaskwebgui-1.1.9-py3-none-any.whl.

File metadata

  • Download URL: flaskwebgui-1.1.9-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for flaskwebgui-1.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 f52ba839c6da1a9ed766c5c1d30508839a4a948410925b4083deff4d0b54394a
MD5 172a234ae77be4397393143ffe366c3d
BLAKE2b-256 15f81267044bbdf4f6ead3fe725a0dadbc5e9ea86fc5ddac6f82d5a0541c19d6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page