欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python GUI:Tkinter——07

程序员文章站 2024-02-04 22:25:58
...


上一章,我们用介绍了多线程,大家可以看一下:Python GUI:Tkinter——06 本章我们将要介绍 文件浏览弹窗,以及如何将我们的 .py 文件,打包成 exe,以便所有人使用它!!!

搭建 GUI

我们在 tab2 页面添加如下代码,首先打开 tab2.py 文件,并修改:

import tkinter as tk
from tkinter import ttk
import shutil
from tkinter import messagebox as mBox
class Tab2():       
    """开发页面2"""
    def __init__(self,tab):
        self._createWidget(tab)
    def _createWidget(self,tab):
        self.muniao = ttk.LabelFrame(tab,text='我是frame')
        self.muniao.grid(column=0,row=0)
        """之后,所有的空间的 Master 就是 frame muniao了"""
        self.dFlag = tk.IntVar()
        self.eFlag = tk.IntVar()
        self.fFlag = tk.IntVar()
        self.dCheck = tk.Checkbutton(self.muniao,variable=self.dFlag,text='A',state='disabled')
        self.eCheck = tk.Checkbutton(self.muniao,variable=self.eFlag,text='B')
        self.eCheck.select()
        self.fCheck = tk.Checkbutton(self.muniao,variable=self.fFlag,text='C')

        
        self.mngFilesFrame = ttk.LabelFrame(tab,text='Manage Files: ')
        self.mngFilesFrame.grid(column=0,row=1,sticky='W',padx=10,pady=5)
        
        self.but = ttk.Button(self.mngFilesFrame,text='Browse to File...'
                    ,command=self._getFileName)
        self.but.grid(column=0,row=0,sticky='W')
        
        self.copyBut = ttk.Button(self.mngFilesFrame,text='Copy to File...'
                                  ,command=self._copy)
        self.copyBut.grid(column=0,row=1,sticky='W')
        
        self.fileEntry = ttk.Entry(self.mngFilesFrame,width=40)
        self.fileEntry.grid(column=1,row=0,sticky='W')
        self.copyEntry = ttk.Entry(self.mngFilesFrame,width=40)
        self.copyEntry.grid(column=1,row=1,sticky='W')
        
        for child in self.mngFilesFrame.winfo_children():
            child.grid_configure(padx=6,pady=6)
            
    def _getFileName(self):
        print('hello from getFileName')
    
    def _copy(self):
        src = self.fileEntry.get()
        dst = self.copyEntry.get()
        try:
            shutil.copy(src,dst)
            mBox.showinfo(' ','成功复制文件')
        except FileNotFoundError:
            mBox.showerror('错误','未找到文件\n'
                           +str(FileNotFoundError))
        except Exception:
            mBox.showerror('错误','无法复制文件'
                           +str(Exception))

Python GUI:Tkinter——07
点击 copy to file,就可以复制文件了

改善

大家是不是觉得每次都要输入一大串的文件名很麻烦呀?于是,就有了 文件浏览弹窗。这是一个很常见的弹窗,任何带有 save to 的软件都会有的。那么怎么做呢?现在就来实现!

获取文件名和文件路径

Python GUI:Tkinter——07
我们要实现一个这样的功能,点击 browse to File 弹出对话框后,用户选择一个文件,就会自动在旁边的 Entry 中填入 完整的文件路径。之后,在下面的 Entry 中填入文件路径,就如同上面的图片所显示的那样。

首先要做的,就是根据完整的文件路径,获得文件路径了。假设有完整的文件路径:fName,于是要想获得文件名、路径,可以这样做:

from os import path
fFile = fName.split('/')[-1]    #文件名
fPath = path.dirname(fName)    #路径

弹窗

修改代码即可:

......
    def _getFileName(self):
        print('hello from getFileName')
        fDir = path.dirname(__file__)   #即 .py 文件所在路径
        fName = fd.askopenfilename(parent=self.mngFilesFrame,initialdir=fDir)
        #  initialdir 参数为打开弹窗时,默认的文件夹位置。
        fPath = path.dirname(fName)
        self.fileEntry.delete(0,tk.END)   #删除原来的
        self.fileEntry.insert(0,fName)   #在0的位置插入
        self.copyEntry.delete(0,tk.END)   #删除原来的
        self.copyEntry.insert(0,fPath)   #在0的位置插入
......

效果如下:
Python GUI:Tkinter——07
选择一个文件后,就会出现上上张图那样的效果。

打包成 exe

综上,我们已经差不多把 GUI 开发完了,那么如何将他打包为 exe 文件呢?一个可以用的工具是:pyinstaller

记住,这里我用的是工具一词,而不是所谓的库、模块。也就是说,pyinstaller 是一个独立于 Python 的工具,而不是一个能够被 .py 文件 import 的模块。那么如下下载它呢?请到官网一叙:
http://www.pyinstaller.org/

当然,也可以用 pip 来安装这个工具,对于windows 用户而言,首先需要安装 pywin32,才能够完成 pyinstaller 的安装。虽然印象中 pip 都是来安装模块的。但今天,咱们就要打破这个顽固的印象。记住:pip 也是可以用来安装工具的。

这里推荐用pip来安装,为什么?因为他帮我们把 pywin32 一站式安装好了。之后,就是 pyinstaller 的使用了。想要知道他怎么使用吗?请到官网一叙:
http://www.pyinstaller.org/documentation.html

pyinstalled 简易教程

安装完后,就可以直接在 cmd(黑乎乎的窗口)里使用 pyinstaller 工具了。比如:
pyinstall myscript.py
就可以将这个脚本文件,转换为 exe 文件了。那么,这句简单的话里,发生了什么呢?来看官方描述:

PyInstaller reads a Python script written by you. It analyzes your code to discover every other module and library
your script needs in order to execute. Then it collects copies of all those files – including the active Python interpreter!
– and puts them with your script in a single folder, or optionally in a single executable file.

也就是说,pyinstaller 会读取你的脚本文件,然后将文件中 import 的所有有关库都拷贝一份,并和你的脚本文件,以及python解释器一起,弄成一个文件夹或一个单一的可执行文件。

为了生成一个单一的可执行文件,而不是一个带有很多 dl 的文件夹,你也可以:
pyinstaller --onefile --windowed myscript.py

另外,生成的 exe 是自包含的。也就是说,使用者无需下载 Python 并安装任何有关模块。但是,有一点需要提示,如下:

Note: The output of PyInstaller is specific to the active operating system and the active version of Python. This
means that to prepare a distribution for:

  • a different OS
  • a different version of Python
  • a 32-bit or 64-bit OS
    you run PyInstaller on that OS, under that version of Python. The Python interpreter that executes PyInstaller is part
    of the bundle, and it is specific to the OS and the word size.

这段话什么意思呢?如果不懂,可以在评论区求教。

pyinstaller 是如何运行的

首先,如果用 pyinstaller xxx.py 来安装的话,会生成一个文件夹。然后,当运行 exe 文件时,就会有一个叫 bootloader 的家伙,首先在文件夹中搜啊、搜啊,把所有需要的东西都在 exe 上,然后运行。

如果是只生成一个 exe 而不是文件夹的话,运行时间会变慢!为什么,因为运行 exe 时,还是要生成一个临时文件夹。之后,还是原来的剧本,bootloader 在临时文件夹里面搜啊、搜啊…

注意的是,如果是生成一个 exe 文件的那种,建议别玩了。因为好像可以逆向最终你的临时文件夹,然后,呵呵,你懂的。

再注意,虽然用文件夹的方式生成 app 比较安全。但是,文件夹里面包含编译了的 Python 脚本 .pyc 文件。所有,亦可以进行逆向编译,还原出你的源代码!!

如何解决?请在评论区中评论,我可能会解答哦~

打包我们的 .py 文件

首先,我们之前新建的所有文件有 prac_oop.py , tab2.py , tab3.py , tooltips.py,我们把他们弄到一个文件夹中,然后运行:
pyinstaller --icon app.ico -w prac_oop.py
其中 --icon 是设置 exe 文件的图标, app.ico 必须在上述文件的同一路径下;-w 是为了避免程序在运行过程中,出现 dos(黑乎乎的)窗口。

调试

运行一下我们的 .exe 文件(在 dist 文件中,慢慢找呗),发现错误:

Traceback (most recent call last):
File “site-packages\PyInstaller\loader\rthooks\pyi_rth_pkgres.py”, line 13, in
File “c:\users\liu\appdata\local\programs\python\python37\lib\site-packages\PyInstaller\loader\pyimod03_importers.py”, line 623, in exec_module
exec(bytecode, module.dict)
File “site-packages\pkg_resources_init_.py”, line 86, in
ModuleNotFoundError: No module named ‘pkg_resources.py2_warn’
[17404] Failed to execute script pyi_rth_pkgres

我们可以打开 pkg_resources/__init__ 文件,然后修改它,如下所示:
只要注释掉其中的一条就可以了。
Python GUI:Tkinter——07
另外,由于我们的源代码中,有用到 ico 文件和一张 .jpg 的妹妹图,当时是用相对路径写的吧?所以,还要将这两个图片拷贝到相应的位置,以便我们的 exe 能够找到它 之后,就万事大吉了!!!!!!
Python GUI:Tkinter——07

总结

很开心吗?我们的课程,就快结束了。和大家一起学习的我,也感到十分荣幸、快乐! 当然,如果想要我写更多的文章,那么可以关注我一下,我将无比光荣。之后的写作,也会有更多的乐趣。

基本上,Tkinter 的教程也差不多啦,下一章,我们将介绍如何将我们的 GUI 与 Internet 相连接。敬请期待。