In recent months I really came to like Flask as a framework for creating websites and web applications. The usage of Jinja2 templates, HTML5 and Javascript to create good-looking and responsive user interfaces along with the Python language in the backend comes by naturally and on some point I asked myself why not to use Flask to create Desktop applications. The idea to use web technology for creating desktop applications is quite old and for other programming languages there exist some nice frameworks (e.g. Githubs Electron). However for Python there are not so many players around. So I decided to use the PyQt5 WebView widget. I have some experience with PyQt5 so this approach seemed the most natural to me. I wanted the application to become a stand alone executable. So I used cx_freeze here.
This is just a simple Hello World app including the named components Flask, QWebView and cx_freeze. The Flask application is started in a separate thread. I tested the code on Python3.5 and Python3.6. For Python 3.6 Flask makes use of the asyncio module. cx_freeze couldn't find all modules so I had to help by including the missing ones directly in the application.
app.py
import platform import threading from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import * from PyQt5.QtWebKitWidgets import QWebView from flask import Flask # Python 3.6 only # Needed imports for cx_freeze to include all necessary # modules if int(platform.python_version_tuple()[1]) >= 6: import asyncio import asyncio.base_futures import asyncio.base_tasks import asyncio.compat import asyncio.base_subprocess import asyncio.proactor_events import asyncio.constants import asyncio.selector_events import asyncio.windows_utils import asyncio.windows_events import jinja2.asyncsupport import jinja2.ext app = Flask(__name__) @app.route('/') def index(): return 'Hello World' thread = threading.Thread(target=app.run, args=['localhost', 5000]) thread.daemon = True thread.start() qt_app = QApplication([]) w = QWebView() w.load(QUrl('http://localhost:5000')) w.show() qt_app.exec_()
setup.py
from cx_Freeze import setup, Executable # Dependencies are automatically detected, but it might need # fine tuning. # When using flask-sqlalchemy add sqlite3.dll to include_files buildOptions = dict( packages = [], excludes = [], include_files=[r'c:\miniconda\Library\plugins\platforms'] ) import sys base = 'Win32GUI' if sys.platform=='win32' else None # base = 'Console' executables = [ Executable('app.py', base=base) ] setup(name='Test', version = '1.0', description = '', options = dict(build_exe = buildOptions), executables = executables)
As one can see this example was created on Windows. The include_files section in the setup.py is important as cx_freeze likes to miss the qt platform plugins which results in the application not working. At the end we get a nice executable shows our GUI which we can now create by using all nice features Flask gives us at hand. Lately I used the beautiful Flask-Admin to create a simple CRUD application in very few lines, which was really fun.