thepoy 发表于 2020-11-13 13:58

简单轻量的GUI编程--PyWebview

本帖最后由 thepoy 于 2020-11-22 21:45 编辑

有新项目了,正在写,见下面更新
主流的GUI以windows平台为主,其次是macOS, 最后才轮得到linux,正好,pywebview在linux上写代码并不舒服,所以可以说这个库/包是windows和macOS专用的,或者说更适合在这两个平台上开发。
正如其名,这是一个以HTML展示,web项目在后端处理基本的软件逻辑的GUI库,比PyQT轻量得多得多。

web建议后端用restful-api(flask、fastapi等轻量框架)实现,前端用ajax传递数据。

本次分享以windows为例。
在windows上,PyWebview是以edge内核开发的,如果想调试,需要在微软应用商店安装edge开发调试软件--Microsoft Edge DevTools。
先上样图(很像一个浏览器):



### 11-1317.29更新
项目结构如下:
```shell
├── backend   # 后端处理
│   ├── __init__.py
│   ├── main# 功能函数
│   └── web   # web函数
├── manage.py# 看名字就知道这是什么了
├── static   # 前端静态文件
├── templates   # 前端HTML模板
```

pywebview官方文档只能看api,不能看示例,示例不算是一个合格的项目。


一个PyWebview项目最关键的就是`manage.py`文件,我写的项目的内容如下:
```python
import logging
import webview

from io import StringIO
from contextlib import redirect_stdout

from backend.web import create_flask_app

logger = logging.getLogger(__name__)

if __name__ == '__main__':
    stream = StringIO()
    with redirect_stdout(stream):
      window = webview.create_window(
            "图片批处理工具 1.0.0",
            create_flask_app(),
            width=920,
            resizable=False,
            text_select=False,
            background_color="#000"
      )

      webview.start(debug=True)

```
`manage.py`是整个项目的调用文件,这个文件中的关键代码是:
```python
window = webview.create_window(
                "图片批处理工具 1.0.0",
                create_flask_app(),
                width=920,
                resizable=False,
                text_select=False,
                background_color="#000"
)

webview.start(debug=True)
```
**api文档:**
创建一个新的 pywebview 窗口。首次调用此函数时将启动应用程序并占用程序主线程。您必须在单独的线程中执行您的程序逻辑。后续调用 create_window 将返回一个唯一窗口 uid,该窗口可用于引用 API 函数中的特定窗口。单窗口应用程序无需理会 uid,在函数调用中可以直接忽略它。
- `title` - 窗口标题
- `url` - 要加载的 URL。如果 URL 没有协议前缀,则将其解析为相对于应用程序入口点的路径。
- `js_api` - 将 js_api 类对象公开给当前 pywebview 窗口的 DOM 。JavaScript 可以通过 window.pywebview.api 对象来对 js_api 进行函数调用。回调函数可以接受一个参数的基本类型或对象。对象类型在 JavaScript 和 Python 间转换。函数在单独的非安全的线程中执行。
- `width` - 窗口宽度。默认值为 800px。
- `height` - 窗口高度。默认值为 600px。
- `resizable` - 窗口大小是否可以调整。默认为 True。
- `fullscreen` - 窗口全屏模式, 默认值为 False。
- `frameless` - 窗口无边框模式,默认值为 False。
- `min_size` - 窗口最小尺寸,元组类型 (width, height), 默认值为 (200x100)。
- `strings` - 窗口本地化语言配置,字符串的字典类型。默认值在 localization.py 中定义。
- `confirm_quit` - 窗口退出时是否显示确认对话框,默认值为False。
- `background_color` - 窗口的 WebView 在 loaded 之前的背景颜色。指定为十六进制颜色。默认为 white。
- `debug` - 启用调试模式,默认值为 False。
- `text_select` - 启用 WebView 的文档文本选择,默认值为 False。如需单独指定元素的文本选择,可以使用 CSS 的 user-select 属性。



`manage.py`文件需要一个最基本的web项目作为连接前端和后端的过度/中间人,我用的是flask,我的flask项目结构为:

```shell
web
├── __init__.py
├── settings.py# flask配置文件
└── views
    ├── __init__.py
    └── first.py# 如果视图函数复杂的话,可能需要分成若干个文件
```

每个人可能都有自己的flask项目结构,所以这方面就不再赘述了。



现在有了web了,接下来就是主角出场了——真正的后端功能函数,我给它起的名字是main,其结构为:

```shell
main
├── exceptions.py
├── filename_handler.py# 处理文件名
├── __init__.py
├── spider_handler.py# 处理爬虫
└── XXX_handler.py# 处理XXX
```

其实叫handlers或controllers可能更合适,只是叫main更能凸显它的重要性。

这里的函数就看自己怎么写了,这是整个项目的中心功能所在。

以`spider_handler.py`为例,只是一个简单地爬取一个网页内所有图片的爬虫,其内容为:

```python
class Spider:
    def __init__(self, url: str, output: str, cookie: str = ''):
      self.url: str = url
      self.output: str = output
      self.headers: dict = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
      }
      if cookie:
            self.header['Cookie'] = cookie

    def __parse_image_urls(self, html: str) -> List:
      soup = BeautifulSoup(html, 'lxml')
      img_tags = soup.find_all('img')
      return ) for img_tag in img_tags]

    def __save_all_image_file(self) -> int:
      html = self.__get_html_response()
      if html:
            srcs = self.__parse_image_urls(html)
            count = 0
            for src in srcs:
                img_response = requests.get(src, headers=self.headers)
                if img_response.status_code == 200:
                  img_file = img_response.content
                  with open(f"{self.output}/spider_img_{ts}{suffix}", 'wb') as f:
                        f.write(img_file)
                        count += 1
                  except UnidentifiedImageError:
                        continue
            return count

    def __get_html_response(self) -> str:
      response = requests.get(self.url, headers=self.headers)
      if response.status_code == 200:
            return response.text
      else:
            return ''

    def get_all_images(self) -> dict:
      try:
            count = self.__save_all_image_file()
            return {"status": 'ok', 'count': count}
      except Exception as e:
            return {'status': 'error', 'error': str(e)}

```

作为一个handler函数,一定要有返回值,方便在flask视图函数里调用并直接返回结果。

视图函数里的相关内容为:

```python
from flask import Blueprint, jsonify, request
from backend.main.spider_handler import Spider


first = Blueprint("first_bp", __name__)

@first.route("/spider", methods=['POST'])
def spider_images_from_url():
    data = request.json

    url = data['url']
    output = data['output']

    spider = Spider(url, output)
    response = spider.get_all_images()
    return jsonify(response)
```

如第一张图的内容所见,当传递完url和output后,spider就开始运行了,这里有兴趣的人可以在前端展示进度,需要配合js完成。
### 11-1810:09更新
正在写一个小项目,PC微信文件清理工具,进度有些慢,因为正在熟悉FastAPI,这个项目也是用来练习FastAPI的练手之作。

https://github.com/thep0y/WFCleanUpTool
### 11-18 18:05更新
Linux上的完成度示意图:


### 11-22 18:18更新
大概能返回文件夹的大小了


### 11-22 21:43更新
已完成初步的文件删除功能,会尽快打包发布。

细水流长 发表于 2020-11-13 14:18

Zeaf 发表于 2020-11-13 14:20

有点意思,看看,坐等楼主更新

麦米尔加弗德 发表于 2020-11-13 15:02

看起来还可以,最近我也在找gui,楼主有什么推荐的教程吗?

741956 发表于 2020-11-13 15:40

下载地址呢? 先拿出来看看啊

Cool_Breeze 发表于 2020-11-13 15:45

看看,等更新

thepoy 发表于 2020-11-13 18:20

细水流长 发表于 2020-11-13 14:18
不懂,楼主有例子吗,我和pyqt对比下

我只有一个完整的项目,不知道你要怎么对比。

thepoy 发表于 2020-11-13 18:21

麦米尔加弗德 发表于 2020-11-13 15:02
看起来还可以,最近我也在找gui,楼主有什么推荐的教程吗?

没有教程,看官方文档

zhorses 发表于 2020-11-15 14:48

python 的gui 是个恶性循环啊。。。

xu程序员 发表于 2020-11-15 15:29

挺好的一个思路啊。
万物都可以往python上转。
页: [1] 2
查看完整版本: 简单轻量的GUI编程--PyWebview