2025年3月24日 星期一 甲辰(龙)年 月廿三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

python --pywebview、flaskwebgui开发桌面程序

时间:08-16来源:作者:点击数:68
CDSY,CDSY.XYZ

python --pywebview、flaskwebgui开发桌面程序

pywebview

装包

  • pip install pywebview==3.7.2

官文

  • https://pywebview.flowrl.com/guide/interdomain.html#invoke-python-from-javascript

案例

  • https://pywebview.flowrl.com/examples/fullscreen.html

实操

  • import webview
  • from flask import Flask, render_template, jsonify, request
  • import json
  • from functools import wraps
  • app = Flask(__name__, template_folder='./static', static_folder='./static', static_url_path='')
  • def verify_token(function):
  • @wraps(function)
  • def wrapper(*args, **kwargs):
  • data = json.loads(request.data)
  • token = data.get('token')
  • if token == webview.token:
  • return function(*args, **kwargs)
  • else:
  • raise Exception('Authentication error')
  • return wrapper
  • @app.route('/')
  • def index():
  • return render_template('index.html')
  • @app.route('/backstage/api/login/', methods=['POST'])
  • def login():
  • print(request.data)
  • data = json.loads(request.data)
  • user = data.get('username')
  • pwd = data.get('password')
  • if user != 'test' or pwd != 'test':
  • print({'code': '4013', 'msg': '用户名或密码错误'}, jsonify({'code': '4013', 'msg': '用户名或密码错误'}))
  • return jsonify({'code': '4013', 'msg': '用户名或密码错误'})
  • groups = {"首页": [], "业务菜单": ["3D模型", "画图展示", "业务3"], "系统设置": ["用户管理", "系统日志"]}
  • roles = {"首页": ["读"], "3D模型": ["读", "写"], "业务2": ["读", "写"], "业务3": ["读", "写"],
  • "用户管理": ["读", "写"], "系统日志": ["读", "写"]}
  • return jsonify({'code': '0', 'data': {'groups': groups, 'roles': roles}, 'msg': 'ok'})
  • @app.route('/get_usr_info', methods=['GET'])
  • @verify_token
  • def get_usr_info():
  • return jsonify({'code': '0', 'data': []})
  • if __name__ == '__main__':
  • chinese = {
  • 'global.quitConfirmation': u'确定关闭?',
  • }
  • window = webview.create_window(
  • title='云收单',
  • url=app,
  • width=900,
  • height=620,
  • # frameless=True,
  • # easy_drag=True,
  • # hidden=True,
  • transparent=True,
  • )
  • webview.start(localization=chinese, debug=True, http_server=True)
  • 注意: 如果要修改flask运行端口为固定则去源码找serving.py 53行修改为指定端口即可;
  • 注意2: 如新版本出错
  • RuntimeError: cannot call null pointer pointer from cdata 'int(*)(void *, int)'
  • pip install pythonnet==2.5.2

控制窗口事件

  • import json
  • import webview
  • from flask import Flask, render_template
  • from flask_cors import CORS
  • import uuid
  • app = Flask(__name__, template_folder='./static', static_folder='./static', static_url_path='')
  • CORS(app, supports_credentials=True)
  • class Windows(object):
  • '''窗口事件'''
  • _window = None
  • @property
  • def w(self):
  • return self._window
  • @w.setter
  • def w(self, value):
  • self._window = value
  • def minimize(self):
  • print('调用最小化窗口')
  • self._window.minimize()
  • def destroy(self):
  • print('销毁窗口')
  • self._window.destroy()
  • @app.route('/')
  • def index():
  • return render_template('index.html')
  • @app.route('/api_minimize', methods=['GET', ])
  • def api_minimize():
  • _w.minimize()
  • return json.dumps({'code': 0})
  • @app.route('/kill', methods=('GET',))
  • def kill():
  • _w.destroy()
  • window = webview.create_window(
  • title='问卷侠题库系统',
  • url=app,
  • width=1360,
  • height=800,
  • text_select=True,
  • resizable=False,
  • frameless=True,
  • confirm_close=True
  • )
  • _w = Windows()
  • _w.w = window
  • webview.start(debug=True, http_server=True)

flaskwebgui

装包: pip install flaskwebgui

github:

  • https://github.com/ClimenteA/flaskwebgui/tree/master/examples/

实例:

  • from flask import Flask
  • from flask import render_template
  • from flaskwebgui import FlaskUI
  • app = Flask(__name__, static_folder='./static', template_folder='./static', static_url_path='')
  • ui = FlaskUI(app, width=500, height=500)
  • @app.route("/")
  • def hello():
  • return render_template('index.html')
  • @app.route("/home", methods=['GET'])
  • def home():
  • return render_template('index.html')
  • if __name__ == "__main__":
  • ui.run()

结构:

在这里插入图片描述

打包多进程失效或异常问题

重复启动主进程问题

  • if __name__ == '__main__':
  • multiprocessing.freeze_support()

自定义封装桌面程序

  • import logging
  • import os
  • import signal
  • import subprocess as sps
  • import tempfile
  • import win32con
  • import win32gui
  • import win32print
  • from flask import Flask, redirect
  • from flask_cors import CORS
  • from flaskwebgui import FlaskUI
  • class BaseFlaskUI(FlaskUI):
  • def find_chrome_win(self):
  • import winreg as reg
  • reg_path = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe'
  • chrome_path = None
  • last_exception = None
  • for install_type in reg.HKEY_CURRENT_USER, reg.HKEY_LOCAL_MACHINE:
  • try:
  • reg_key = reg.OpenKey(install_type, reg_path, 0, reg.KEY_READ)
  • chrome_path = reg.QueryValue(reg_key, None)
  • reg_key.Close()
  • except WindowsError as e:
  • last_exception = e
  • else:
  • if chrome_path and len(chrome_path) > 0:
  • break
  • if not chrome_path:
  • edge_path = "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
  • return edge_path
  • logging.info(f'{chrome_path}')
  • return chrome_path
  • def get_real_resolution(self):
  • """获取真实的分辨率"""
  • hdc = win32gui.GetDC(0)
  • return win32print.GetDeviceCaps(hdc, win32con.DESKTOPHORZRES), \
  • win32print.GetDeviceCaps(hdc, win32con.DESKTOPVERTRES)
  • def open_chromium(self):
  • logging.info(f"Opening browser at {self.localhost}")
  • temp_profile_dir = os.path.join(tempfile.gettempdir(), "flaskwebgui")
  • if self.browser_path:
  • width, height = self.get_real_resolution()
  • width = (width - 1750) // 2
  • height = (height - 850) // 2
  • if self.fullscreen:
  • launch_options = ["--start-fullscreen"]
  • elif self.maximized:
  • launch_options = ["--start-maximized"]
  • else:
  • launch_options = [f"--window-size={self.width},{self.height}"]
  • options = [
  • self.find_chrome_win(),
  • f"--user-data-dir={temp_profile_dir}",
  • "--new-window",
  • "--no-first-run",
  • f"--window-position={width},{height}",
  • "--disable-desktop-notifications"
  • ] + launch_options + [f'--app={self.localhost}']
  • sps.Popen(options, stdout=sps.PIPE, stderr=sps.PIPE, stdin=sps.PIPE)
  • else:
  • import webbrowser
  • webbrowser.open_new(self.localhost)
  • app = Flask(__name__, template_folder='./static', static_folder='./static', static_url_path='')
  • CORS(app, supports_credentials=True)
  • ui = BaseFlaskUI(app, close_server_on_exit=True, width=1750, height=850, port=8001)
  • @app.route('/kill', methods=('GET',))
  • def kill():
  • print('调用用户退出')
  • handle = os.getpid()
  • print(f'pid={handle}')
  • os.kill(handle, signal.SIGABRT)
  • @app.route('/', methods=('GET',))
  • def index():
  • return redirect('http://8.140.142.128:8000/static/index.html#/login')
  • if __name__ == '__main__':
  • ui.run()
  • # app = Flask(__name__, template_folder='./static', static_folder='./static', static_url_path='')
  • #
  • #
  • # @app.route('/')
  • # def index():
  • # return redirect('http://8.140.142.128:8000/static/index.html#/login')
  • #
  • #
  • # @app.route('/kill')
  • # def kill():
  • # print('调用用户退出')
  • # handle = os.getpid()
  • # print(f'pid={handle}')
  • # os.kill(handle, signal.SIGABRT)
  • #
  • #
  • # def get_real_resolution():
  • # """获取真实的分辨率"""
  • # hdc = win32gui.GetDC(0)
  • # return win32print.GetDeviceCaps(hdc, win32con.DESKTOPHORZRES), \
  • # win32print.GetDeviceCaps(hdc, win32con.DESKTOPVERTRES)
  • #
  • #
  • # if __name__ == '__main__':
  • # chinese = {
  • # 'global.quitConfirmation': u'您确定要关闭吗?',
  • # }
  • #
  • # window = webview.create_window(
  • # title='船舶管理',
  • # url=app,
  • # width=1750,
  • # height=850,
  • # # transparent=True,
  • # text_select=True,
  • # confirm_close=True
  • # )
  • # print(get_real_resolution())
  • # window_hwnd: list = []
  • # win32gui.EnumWindows(lambda _hwd, param: param.append(_hwd), window_hwnd)
  • # status = False
  • # for hwd in window_hwnd:
  • # if win32gui.GetWindowText(hwd) == '船舶管理':
  • # width, height = get_real_resolution()
  • # print(f'激活窗口:{width};{height}')
  • # win32gui.ShowWindow(hwd, win32con.SW_MAXIMIZE)
  • # win32gui.MoveWindow(hwd, (width - 1750) // 2, (height - 850) // 2, 1750, 850, True)
  • # status = True
  • # break
  • #
  • # if not status:
  • # webview.start(localization=chinese, debug=False, http_server=True)
CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐