五十二、GUI布局tkinter完善Python小项目

Python之王

共 5643字,需浏览 12分钟

 · 2020-12-05


「@Author:Runsen」

本次 Python 小项目主要功能:调用电脑摄像头实现拍照,并使用百度 API 接口实现图像识别。

上次完成了API的封装,这次完成GUI的布局。具体成品如下所示。

拍照保存图片采用的是opencv中的imwrite方法,具体的示例查看上上篇文章。

Tkinter 布局逻辑中最推荐使用的Grid布局。实现机制是将Widget逻辑上分割成表格,在指定的位置放置想要的Widget就可以了。

Grid布局参数说明

参数作用
column 指定组件插入的列(0 表示第 1 列)
默认值是 0
columnspan 指定用多少列(跨列)显示该组件
row 指定组件插入的行(0 表示第 1 行)
rowspan 指定用多少行(跨行)显示该组件
in_将该组件放到该选项指定的组件中
指定的组件必须是该组件的父组件
ipadx  水平方向上的内边距
ipady  垂直方向上的内边距
padx 水平方向上的外边距
pady 垂直方向上的外边距
sticky 控制组件在 grid 分配的空间中的位置
可以使用 "n", "e", "s", "w" 以及它们的组合来定位(ewsn代表东西南北,上北下南左西右东)
使用加号(+)表示拉长填充,例如 "n" + "s" 表示将组件垂直拉长填充网格,"n" + "s" + "w" + "e" 表示填充整个网格
不指定该值则居中显示选项 含义

具体main.py代码如下。

"""
@Author:Runsen
@WeChat:RunsenLiu
@微信公众号:Python之王
@CSDN:https://blog.csdn.net/weixin_44510615
@Github:https://github.com/MaoliRUNsen
@Date:2020/11/29
"""

import time
import cv2 as cv  # pip install opencv-python
import tkinter as tk
from tkinter import ttk  # 下拉框依赖库
from tkinter import scrolledtext  # 滚动文本框依赖库
from tkinter import N,E,S,W
# 引入Baidu_API类 (上次文章)
from baidu_api import Baidu_API

# 拍照
def take_a_photo():
    # 调用笔记本内置摄像头,所以参数为0,如果有其他的摄像头可以调整参数为1,2
    cap = cv.VideoCapture(0)
    img_path = str(int(time.time())) + '.jpg'
    while True:
        # 从摄像头读取图片
        sucess, img = cap.read()
        # 转为灰度图片
        # gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)#
        # 显示摄像头
        cv.imshow('----------please enter "s" to take a picture----------', img)
        # 保持画面的持续,无限期等待输入
        k = cv.waitKey(1)
        if k == 27:
            # 通过esc键退出摄像
            cv.destroyAllWindows()
            break
        elif k == ord("s"):
            # 通过s键保存图片,并退出。
            cv.imwrite(img_path, img)
            cv.destroyAllWindows()
            break
    # 关闭摄像头
    cap.release()
    # 打印日志
    scr.insert(tk.END, '[{}]拍摄成功...\n'.format(time.strftime('%Y-%m-%d %H:%M:%S')))
    # 返回图像
    return img_path

# ----------图形界面各个组件功能的设计----------
# 清除窗口日志
def clear_the_window():
    scr.delete(1.0, tk.END)

# 退出软件
def exit():
    win.quit()

# 下拉框选项选择
def select_ttk(event):
    global numberChosen
    # 颜值评分
    if numberChosen.current() == 1:
        # 获取图像
        img_path = take_a_photo()

        try:
            # 向API发送图像并获取信息
            score, age, gender, race = Baidu_API().face_detect(img_path=img_path)

            # 打印日志
            scr.insert(tk.END, '[{}]年龄「{}」性别「{}」人种「{}」\n'.format(time.strftime('%Y-%m-%d %H:%M:%S'), age, gender, race))
            scr.insert(tk.END, '[{}]颜值评分为:{}/100 分\n'.format(time.strftime('%Y-%m-%d %H:%M:%S'), score))
        except:
            scr.insert(tk.END, '[{}]{}'.format(time.strftime(time.strftime('%Y-%m-%d %H:%M:%S')),
                                               Baidu_API().face_detect(img_path=img_path)))
    # 手势识别
    if numberChosen.current() == 2:
        scr.insert(tk.END, '[{}]请将您的手势放置摄像头前...\n'.format(time.strftime('%Y-%m-%d %H:%M:%S')))
        time.sleep(0.1)
        img_path = take_a_photo()
        try:
            classname_en, classname_zh = Baidu_API().gesture(img_path=img_path)
            scr.insert(tk.END,
                       '[{}]手势大意:{}({})\n'.format(time.strftime('%Y-%m-%d %H:%M:%S'), classname_zh, classname_en))
        except:
            scr.insert(tk.END,
                       '[{}]{}\n'.format(time.strftime('%Y-%m-%d %H:%M:%S'), Baidu_API().gesture(img_path=img_path)))
    # 智能人脸抠图
    if numberChosen.current() == 3:
        scr.insert(tk.END, '智能人脸抠图\n'.format(time.strftime('%Y-%m-%d %H:%M:%S')))
        img_path = take_a_photo()
        out_path = str(int(time.time())) + '.jpg'
        try:
            Baidu_API().body_seg(img_path=img_path, out_path=out_path)
            scr.insert(tk.END, '完成智能人脸抠图')
        except:
            scr.insert(tk.END, '[{}]{}\n'.format(time.strftime('%Y-%m-%d %H:%M:%S'),
                                                 Baidu_API().body_seg(img_path=img_path, out_path=None)))


# -------------创建窗口--------------
win = tk.Tk()
win.title('客官先关注微信公众号:Python之王!')
win.geometry('600x300')

# ------------窗口组件设计-----------
# grid中的参数:column, columnspan, in, ipadx, ipady, padx, pady, row, rowspan,sticky

# 下拉框组件
number = tk.StringVar
numberChosen = ttk.Combobox(win, textvariable=number)
numberChosen['value'] = ('please select''给我的颜值打个分吧!''识别一下我的手势''智能人脸抠图')

numberChosen.current(0)  # 设置默认值为第一个,即默认下拉框中的内容

numberChosen.grid(row=1, column=1, rowspan=1, sticky=N + E + S + W)
# 下拉框触发动作 (绑定点击事件)
numberChosen.bind('<>', select_ttk)

# 清除按钮组件
tk.Button(win, cnf={'text''clear''command': clear_the_window}).grid(row=1, column=2, ipadx=1, sticky=N + E + S + W)

# 退出按钮组件
tk.Button(win, cnf={'text''exit''command': exit}).grid(row=1, column=3, ipadx=1, sticky=N + E + S + W)

# 滚动文本框组件
scr = scrolledtext.ScrolledText(win)
scr.grid(row=2, column=1, columnspan=3, rowspan=1)

# 使窗口一直显示
win.mainloop()

最后使用Pyinstaller打包即可。

Java 一次编译到处运行,Python没有这么好本事,Python有一个pyinstaller可以打包exe,在window平台下运行,这也是Python非常不好的方面,而且打包出来的占用内存非常的大

安装:pip install pyinstaller。Pyinstaller具体参数如下所示。

参数含 义
-F只生成一个exe文件
–distpath指定生成的exe存放的目录
–workpath指定编译中临时文件存放的目录
-i创建一个目录包含:exe文件、依赖文件
–icon=指定exe图标
-p指定exe依赖的包、模块
-d编译为debug模式,获取运行中的日志信息
-clean清理编译时临时文件
-c使用控制台
-w使用窗口
-version-file添加exe版本信息

注意点:有的时候在代码最后面加上input(),这样打开exe不会一散而过。由于上面代码本身就是窗口一直显示,无需加上input()。

在打包时候,并没有提示错误,可以顺利打包成 exe 文件。但是在运行打包好的软件时,会提示找不到模块,本人遇到的是找不到第三方模块,例如 cv2  。这时候需要在打包时指定 -p 参数,后面跟上 python 目录下的第三方库模板目录路径 site-packages ,再打包就成功了。

cd 到代码的目录执行 pyinstaller main.py -F -p F:\anaconda\Lib\site-packages

如果Pyinstaller打包报错numpy.core.multiarray failed to import,这是numpy和opencv的不兼容,可以降低numpy的版本。

具体代码在微信公众号:Python之王。回复:tkinter

本文已收录 GitHub,传送门~[1] ,里面更有大厂面试完整考点,欢迎 Star。



Reference

[1]

传送门~: https://github.com/MaoliRUNsen/runsenlearnpy100


更多的文章

点击下面小程序


- END -


浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报