程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Develop beautiful GUIs with Python

編輯:Python

之前,我有一篇文章《Python GUI美化小技巧》,which mentions two beautificationsGUI的方式,嗯,I am not satisfied with the two ways.

Tkinter Designer需要學習FigmaHow to use this design software,The beautificationGUIThe principle is to fill with pictures,從而讓TkinterHave a more beautiful style,抱歉,I don't want to learn yetFigma.

And another scheme in the text:TTk BootstrapActually a bigger hole,If you are only developing single windowGUI軟件,TTkBootstrap挺好的,but when you have opened a window,When closing and opening another new window on demand,TTKBoostrap就是一個大坑,The new window will lose the corresponding style.

我希望Pythonecologically similarElectron的庫,讓我們可以利用HTML、CSS、JS來構建前端頁面,And the back-end logic usesPython來實現,This makes it easy to build beautifulGUI了.

Friends who have my WeChat friends should know,Last two weeks I released Video Sync Assistant,It can help you to sync the video to6different media platforms,This software is usedPyWebView框架開發,軟件界面如下:

PyWebView可以讓你通過HTML、CSS、JS來構建GUI的前端頁面,使用Pythonto implement the business logic in the software,再配合使用PyinstallerImplement software packaging,to build a beautifulGUI應用了.

遺憾的是PyWebView資料比較少,Github的star數也不多,I actually do it,有些WindowsPlatform cannot be opened,因精力有限,I didn't go to the bottom of why it couldn't be opened..

This article briefly introducesPyWebView的使用方法,compared to official documentation,It will highlight the pits encountered and the corresponding solutions,It can be regarded as a technical review of the video synchronization assistant project.(嗯,The monetization phase failed,所以).

簡單使用

light readingPyWebView的文檔,It's harder to get started,A better way is to goPyWebView的githubLieutenant will pull down the source code,其中有example目錄,Provides an example project,The core logic to understand is,前端JS寫的邏輯,how to go withPython互通.

PyWebView提供了js apiand interface request these two methods,先看js api.

JS api

在實例化webview時,將定義好的Api類實例作為js_api的參數傳入:

class Api():
    def addItem(self, title):
        print('Added item %s' % title)
    def removeItem(self, item):
        print('Removed item %s' % item)
    def editItem(self, item):
        print('Edited item %s' % item)
    def toggleItem(self, item):
        print('Toggled item %s' % item)
    def toggleFullscreen(self):
        webview.windows[0].toggle_fullscreen()
    def load_page(self):
        file_url = 'file:///C:/Users/admin/workplace/github/pywebview/examples/todos/assets/hello.html'
        webview.windows[0].load_url(file_url)
if __name__ == '__main__':
    api = Api()
    # js_api 獲得 api 實例
    webview.create_window('Todos magnificos', 'assets/index.html', js_api=api, min_size=(600, 450))
    webview.start(debug=True)

前端通過window.pywebview.api來使用Api類中提供的方法,比如調用addItem方法:

window.pywebview.api.addItem(title)

Because our project starts,開啟了debug模式,所以可以按F12打開浏覽器的DevTools,在控制台中,Can be easily printed outwindow.pywebview中各種屬性,其中的api屬性包含了Python中Api類提供的所有方法.

HTTP服務

JS apiFor some of the more complex requirements is not too good,此時可以使用HTTP服務的形式,具體而言,利用Flask開啟一個web服務,Front-end operation throughHTTPrequest to call theWeb服務.

首先,基於Flask構建服務實例:

from flask import Flask
server = Flask(__name__, static_folder=gui_dir, template_folder=gui_dir)

然後再實例化webview:

# 傳入server實例,開啟HTTP服務模式
window = webview.create_window('My first pywebview application', server)
webview.start(debug=True)

前端JS通過Ajax的形式,請求Flask提供的接口,so that the backend executes the relevant logic,如果沒有使用Vue、React等框架時,I am used to usingJquery提供的ajax方法,And encapsulate one more layer on the basis of this method.

function doAjax(url, method, data, success, fail) {
    if (success === undefined) {
        success = function (data) {
            console.log('[success] ', data)
        }
    }
    if (fail === undefined) {
        fail = function (err) {
            console.log('[fail]', err)
        }
    }
    if (method === 'POST') {
        data = JSON.stringify(data)
    }
    $.ajax({
        url: url,
        type: method,
        cache: false,
        dataType: "json",
        headers: {
            "token": window.token
        },
        data: data,
        contentType: "application/json; charset=utf-8",
        success: success,
        fail: fail
    })
}

When you need to call back-end logic,You can directly request the interface:

function get_video_settings_data(video_id) {
    doAjax("/video_settings/data", "GET", {video_id: video_id}, get_video_settings_data_handler);
}

The corresponding back-end interface is the same as the ordinaryFlaskno difference in interface:

@server.route('/video_settings/data', methods=['GET'])
def get_video_settings_data():
    video_id = request.args.get('video_id')
    video_id = video_id.split('-')[-1]
    data = VideoTaskSettingsModel.get_settings(video_id=video_id)
    if not data:
        video_settings = {}
    else:
        video_settings = data[0]
    return jsonify({'status': 'ok', 'video_settings': video_settings})

PyWebViewIn order to avoid malicious requests for the interface,Its official example suggests that when requesting,headerZhongdu bring oneToken,Used to identify the current request,這個Token PyWebViewwill help us generate a goodwindow.pywebview.token中.

import webview
def verify_token(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        token = request.headers.get('token')
        # Judging front-end deliverytoken是否與webview.token一致,so as to verify whether the request is legitimate
        if token == webview.token:
            return function(*args, **kwargs)
        else:
            raise Exception('Authentication error')
    return wrapper
@server.route('/video/task_list')
@verify_token
def video_task_list():
    '''
    Video Configuration List
    :return:
    '''
    video_tasks = VideoTaskModel.get_all_video_task()
    response = {
        'status': 'ok',
        'video_tasks': video_tasks
    }
    return jsonify(response)

簡易路由

While developing the Video Sync Assistant,為了方便,沒有使用Vue+PyWebView的形式,i.e. I cannot use something likevue-routerthings to achieve the effect of page changes,how to do it?

Video synchronization assistant front-end utilizationFlask的模板語法+Jquery實現主要邏輯,後端就是Flask+JS Api,沒錯,PyWebViewAllows you to open at the same timeHTTP服務與JS Api支持.

To achieve page changes,I write these in the sidebarbase.html中,然後通過FlaskEach page template inheritance's ability to avoid get a sidebar.

when we want to visit different pages,發現PyWebViewyou are not allowed to passrender_templatemethod to get new page,PyWebViewThere are only two ways to achieve page update,一是使用load_url方法,提供一個url,webviewwill load a new page,另一種是使用load_html方法,This method will be loadedhtml文件.

遺憾的是,load_html方法加載html時,不會執行html中引入的JS,所以我們只能通過load_url方法去做.

回顧一下PyWebView HTTP服務的使用,我們利用Flask構建了web實例,但WebThe ports that the instance listens on after it starts up. These are set byPyWebView隨機分配的,我們無法獲得,而load_urlmethod needs to be completeurl才能獲取.

為了解決這個問題,只能在PyWebView剛啟動時,將url記錄起來,Then use the recordedurl拼接出完整的URL,實現頁面的切換.

PyWebView啟動後,如果開啟了HTTP服務,will access the root path by default,Let's record it hereurl:

@server.route('/')
def video_task_template():
    """
    視頻任務
    """
    set_base_url(request.base_url)
    # 省略...

配合JS Api,來調用load_url方法:

class JSApi():
    def load_page(self, page_name):
        url = urljoin(get_base_url(), page_name)
        webview.windows[0].load_url(url)

When the user clicks on the sidebar,使用JSApi提供的load_page方法:

window.token = '{
{ token }}';
function view_video_task() {
  window.pywebview.api.load_page('/')
}
function view_user() {
  window.pywebview.api.load_page('/user_template')
}

Flaskmid-to-interface,通過render_template返回頁面數據:

@server.route('/user_template')
def user_template():
    """
    用戶列表
    """
    set_base_url(request.base_url)
    return render_template('user.html', token=webview.token)

通過這種方式,利用了PyWebView的load_url方法,New page loaded successfully,實現頁面的切換,頁面中的JS也可以正常執行.

video element display

Users need to add their own videos to the sensorless synchronization assistant,會發現,Video element cannot be displayed,其原因在於,The video may be anywhere on the user's system,當前Flask構建的webService because of security sandbox,Unable to read an element anywhere in the system and return.

我們看到Flask實例化時,需要傳輸static_folder,Can we visit in normalstatic_folder中的js、css文件,template_folder也是一個道理.

frontend_dir = os.path.join(root_path, 'gui', 'frontend')
server = Flask(__name__, static_folder=frontend_dir, template_folder=frontend_dir)

我們是否可以為Flask添加多個static,從而實現讓FlaskCan access the effects of any element in the system??嗯,可以的,利用Flask提供的send_from_directory方法.

from flask import request, send_from_directory
@server.route('/cdn')
def custom_static():
    file_dir_path = request.args.get('file_dir_path')
    filename = request.args.get('filename')
    return send_from_directory(file_dir_path, filename)

Front-end access is in the form of:

<div class="video-source-div"> 
  <video class="video-source" controls=""> 
    <source id="video-srouce-5" src="cdn?file_dir_path=C:\Users\admin\Videos\myvideos&filename=Insensitive assistant development reflection.mp4" metadata="" preload="auto" type="video/mp4"></source> 
  </video> 
</div>

JSUnable to start newTab

In the business logic of the senseless synchronization assistant,There are newTabpage requirements,Common solutions online are:

window.open('https://www.baidu.com', '_blank')

正常而言,This will start a newTab,但ChromeIn order to avoid malicious website opening newTab,The default is to not allow pureJS開啟新Tab的操作的.

怎麼解決?翻了大半天Chrome的文檔,only know maintenancewindow.openIneffective and solutions.

訪問ChromeSettings for pop-up windows in:chrome://settings/content/popups

Then change the default modification to【Website can send the popup window and using redirects】則可.

結尾

PyWebViewIt will use the browser kernel that comes with the system to give youGUIProvide the front-end display program,而ElectronIs packed into oneChromium,所以PyWebViewPacking will be smaller,but the stability,個人體驗下來,比ElectronSome of the poor.

此外,PyinstallerPackaging is also a big pit,後續有空,再將PyinstallerVarious practical skills to make up.

我是二兩,下篇文章見.


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved