零基础入门学习Python(35)--图形用户界面入门:EasyGui

知识点

EasyGui学习文档【超详细中文版】

1. 建议不要在IDLE上运行EasyGui

EasyGui是运行在TKinter上并拥有自身的事件循环,而IDLE也是Tkinter写的一个应用程序并也拥有自身的事件循环。因此当两者同时运行的时候,有可能会发生冲突,且带来不可预测的结果。因此如果你发现你的EasyGui程序有这样的问题,请尝试在IDLE外去运行你的程序。

2. 一个简单的例子

在EasyGui中,所有的GUI互动均是通过简单的函数调用,下边一个简单的例子告诉你EasyGui确实很Easy!

import easygui as g
import sys

while 1:
        g.msgbox("嗨,欢迎进入第一个界面小游戏^_^")

        msg ="请问你希望在鱼C工作室学习到什么知识呢?"
        title = "小游戏互动"
        choices = ["谈恋爱", "编程", "OOXX", "琴棋书画"]

        choice = g.choicebox(msg, title, choices)

        # note that we convert choice to string, in case
        # the user cancelled the choice, and we got None.
        g.msgbox("你的选择是: " + str(choice), "结果")

        msg = "你希望重新开始小游戏吗?"
        title = "请选择"

        if g.ccbox(msg, title):     # show a Continue/Cancel dialog
                pass  # user chose Continue
        else:
                sys.exit(0)     # user chose Cancel

3. EasyGui各种功能演示

要运行EasyGui的演示程序,在命令行调用EasyGui是这样的:

python easygui.py

或者你可以从IDE工具上调用:

import easygui as g
g.egdemo()

成功调用后你将可以尝试EasyGui拥有的各种功能,并将你选择的结果打印至控制台。

4. 导入EasyGui

为了使用EasyGui这个模块,你应该先导入它。最简单的导入语句是:

import easygui

如果你使用上面这种形式导入的话,那么你使用EasyGui的函数的时候,必须在函数的前面加上前缀easygui,像这样:

easygui.msgbox(...)

另一种选择是导入整个EasyGui包:

from easygui import *

这使得我们更容易调用EasyGui的函数,你可以直接这样编写代码:

msgbox(...)

第三种方案是使用类似下边的import语句:

import easygui as g

这样让你保持EasyGui的命名空间,同时减少你的打字数量。导入之后你就可以这么调用EasyGui的函数:

g.msgbox(...)

5. 使用EasyGui

一旦你的模块导入EasyGui,GUI操作就是一个简单的调用EasyGui函数的几个参数的问题了。
例如,使用EasyGui来实现著名的你好,世界!程序是这样的:

import easygui as g
g.msgbox("Hello,World!")

6. EasyGui函数的默认参数

对于所有函数而言,前两个参数是消息和标题。按照这个规律,在某种情况下,这可能不是最有利于用户的安排(例如,对话框在获取目录和文件名的时候忽略消息参数),但是我觉得保持这种一致性贯穿于所有的窗口部件是一种更为重要的考虑!

绝大部分的EasyGui函数都有默认参数,几乎所有的部件都会显示一个消息和标题。标题默认是空字符串,消息通常有一个简单的默认值。

这使得你可以尽量少的去设置参数,比如msgbox()函数标题部分的参数是可选的,所以你调用msgbox()的时候可以只指定一个消息参数,例如:

import easygui as g
g.msgbox("Hello,World!")


当然你也可以指定标题参数和消息参数,例如:

import easygui as g
g.msgbox('Hello World!','小甲鱼编程')


在各类按钮组件里,默认的消息是Shall I continue?,所以你可以不带任何参数的去调用它们。这里我们演示不带任何参数的去调用ccbox(),当选择cancel关闭窗口的时候返回一个布尔类型的值:

import easygui as g
while True:
    if g.ccbox():
        pass         # user chose to continue
    else:
        exit()     # user chose to cancel

7. 使用关键字参数调用EasyGui函数

调用EasyGui函数还可以使用关键字参数
现在假设你需要使用一个按钮组件,但你不想指定标题参数(第二个参数),你仍可以使用关键字参数的方法指定choices参数(第三个参数),像这样:

import easygui as g
xz = ['棒','真棒','超级棒']
replay = g.choicebox('我棒不棒?',choices=xz)

8. 使用按钮组建

根据需求,EasyGui在buttonbox()上建立了一系列的函数供调用。

8.1 msgbox()

>>> help(g.msgbox)
Help on function msgbox in module easygui:

msgbox(msg='(Your message goes here)', title=' ', ok_button='OK', image=None, root=None)
    Display a messagebox

重写OK按钮最简单的方法是使用关键字参数:

import easygui as g
g.msgbox('我一定要学会Python', ok_button='加油')

8.2 ccbox()

>>> help(g.ccbox)
Help on function ccbox in module easygui:

ccbox(msg='Shall I continue?', title=' ', choices=('Continue', 'Cancel'), image=None)
    Display a msgbox with choices of Continue and Cancel.

    The default is "Continue".

    The returned value is calculated this way::
        if the first choice ("Continue") is chosen, or if the dialog is cancelled:
            return 1
        else:
            return 0

    If invoked without a msg argument, displays a generic request for a confirmation
    that the user wishes to continue.  So it can be used this way::

        if ccbox():
            pass # continue
        else:
            sys.exit(0)  # exit the program

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed

ccbox()提供一个选择:Continue或者Cancel,并相应的返回1(选中Continue)或者0(选中Cancel)。

【注意】:ccbox()是返回整形的1或0,不是布尔类型的True或False。但你仍然可以这么些:

import sys
import easygui as g
if g.ccbox('要再来一次吗?',choices=('要啊要啊^_^', '算了吧T_T')):
    g.msgbox('不给玩了,再玩就玩坏了……')
else:
    sys.exit(0)

8.3 ynbox()

>>> help(g.ynbox)
Help on function ynbox in module easygui:

ynbox(msg='Shall I continue?', title=' ', choices=('Yes', 'No'), image=None)
    Display a msgbox with choices of Yes and No.

    The default is "Yes".

    The returned value is calculated this way::
        if the first choice ("Yes") is chosen, or if the dialog is cancelled:
            return 1
        else:
            return 0

    If invoked without a msg argument, displays a generic request for a confirmation
    that the user wishes to continue.  So it can be used this way::
        if ynbox(): pass # continue
        else: sys.exit(0)  # exit the program

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed

同8.2

8.4 buttonbox()

>>> help(g.buttonbox)
Help on function buttonbox in module easygui:

buttonbox(msg='', title=' ', choices=('Button1', 'Button2', 'Button3'), image=None, root=None)
    Display a msg, a title, and a set of buttons.
    The buttons are defined by the members of the choices list.
    Return the text of the button that the user selected.

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed

可以使用buttonbox()定义自己的一组按钮,buttonbox()会显示一组你定义好的按钮。

当用户点击任意一个按钮的时候,buttonbox()返回按钮的文本内容。如果用户取消或者关闭窗口,那么会返回默认选项(第一个选项)。请看示例:

import easygui as g
g.buttonbox(choices=('草莓','西瓜','芒果'))

8.5 indexbox()

>>> help(g.indexbox)
Help on function indexbox in module easygui:

indexbox(msg='Shall I continue?', title=' ', choices=('Yes', 'No'), image=None)
    Display a buttonbox with the specified choices.
    Return the index of the choice selected.

基本上跟上边一样,区别就是当用户选择第一个按钮的时候返回序号0,选择第二个按钮的时候返回序号1.

8.6 boolbox()

>>> help(g.boolbox)
Help on function boolbox in module easygui:

boolbox(msg='Shall I continue?', title=' ', choices=('Yes', 'No'), image=None)
    Display a boolean msgbox.

    The default is the first choice.

    The returned value is calculated this way::
        if the first choice is chosen, or if the dialog is cancelled:
            returns 1
        else:
            returns 0

如果第一个按钮被选中返回1,否则返回0.

9. 如何在buttonbox里显示图片

当你调用一个buttonbox函数(例如:msgbox(),ynbox(),indexbox()等)的时候,你还可以为关键字参数image赋值,这是设置一个.gif格式的图像(注意仅支持GIF格式)

import easygui as g
g.buttonbox('大家说我长得帅吗?', image='1.gif', choices=('帅', '不帅', '你大爷'))

10. 为用户提供一系列选项

10.1 choicebox()

>>> help(g.choicebox)
Help on function choicebox in module easygui:

choicebox(msg='Pick something.', title=' ', choices=())
    Present the user with a list of choices.
    return the choice that he selects.
    return None if he cancels the selection selection.

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed

按钮组建方便提供用户一个简单的按钮选项,但如果有很多选项,或者选项的内容特别长的话,更好的策略是为它们提供一个可选择的列表。

choicebox()为用户提供了一个可选择的列表,使用序列(元祖或列表)作为选项,这些选项显示前会按照不区分大小写的方法排好序。

另外还可以使用键盘来选择其中一个选项(比较纠结,但一点儿都不重要):
- 例如当按下键盘上g键,将会选中第一个以g开头的选项。再次按下g键,则会选中下一个以g开头的选项。在选中最后一个以g开头的选项时候,再次按下g键将重新回到在列表的开头的第一个以g开头的选项。
- 如果选项中没有以g开头的,则会选中字符排序在g之前f的那个字符开头的选项
- 如果选项中没有字符排序在g之前,那么在列表中第一个元素将会被选中。

综合我们之前的学习文件功能,举个例子:

import easygui as g

target = ['.mp4','.avi','.rmvb','.mkv','.torrent']
vedio_list = []

import os
def serach_file(start_dir,target):
    os.chdir(start_dir)

    for each_file in os.listdir(os.curdir):
        ext = os.path.splitext(each_file)[1]
        if ext in target:
            vedio_list.append(os.getcwd() + os.sep +each_file + os.linesep)
        if os.path.isdir(each_file):
            serach_file(each_file,target)
            os.chdir(os.pardir)
start_dir = input('请输入需要查找的目录:')
program_dir = os.getcwd()
serach_file(start_dir,target)

f = open(program_dir + os.sep + 'vedioList.txt','w')
f.writelines(vedio_list)
f.close()

g.choicebox(msg='在 【%s】 系列路径下工搜索满足条件的文件如下' % start_dir,choices=vedio_list)

10.2 multchoicebox()

>>> help(g.multchoicebox)
Help on function multchoicebox in module easygui:

multchoicebox(msg='Pick as many items as you like.', title=' ', choices=(), **kwargs)
    Present the user with a list of choices.
    allow him to select multiple items and return them in a list.
    if the user doesn't choose anything from the list, return the empty list.
    return None if he cancelled selection.

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg choices: a list or tuple of the choices to be displayed

multchoicebox()函数也是提供一个可选择的列表,与choicebox()不同的是,multchoicebox()支持用户选择0个,1个或者同时选择多个选项。

multchoicebox()函数也是使用序列(元祖或列表)作为选项,这些选项显示前会按照不区分大小写的方法排好序。

... #与上例一致
g.multchoicebox(msg='在 【%s】 系列路径下工搜索满足条件的文件如下' % start_dir,choices=vedio_list)

11. 让用户输入消息

11.1 enterbox()

Help on function enterbox in module easygui:

enterbox(msg='Enter something.', title=' ', default='', strip=True, image=None, root=None)
    Show a box in which a user can enter some text.

    You may optionally specify some default text, which will appear in the
    enterbox when it is displayed.

    Returns the text that the user entered, or None if he cancels the operation.

    By default, enterbox strips its result (i.e. removes leading and trailing
    whitespace).  (If you want it not to strip, use keyword argument: strip=False.)
    This makes it easier to test the results of the call::

        reply = enterbox(....)
        if reply:
            ...
        else:
            ...

enterbox()为用户提供一个简单的输入框,返回值为用户输入的字符串。默认返回的值会自动去除首位空格,如果需要保留首位空格,请设置参数strip = False

import easygui as g
print(g.enterbox('请输入一句你最想对小甲鱼说的话:'))

===输出结果===
D:\untitled\venv\Scripts\python.exe D:/untitled/Python_learn/test1.py
我想死你了

11.2 integerbox()

>>> help(g.integerbox)
Help on function integerbox in module easygui:

integerbox(msg='', title=' ', default='', lowerbound=0, upperbound=99, image=None, root=None, **invalidKeywordArguments)
    Show a box in which a user can enter an integer.

    In addition to arguments for msg and title, this function accepts
    integer arguments for "default", "lowerbound", and "upperbound".

    The default argument may be None.

    When the user enters some text, the text is checked to verify that it
    can be converted to an integer between the lowerbound and upperbound.

    If it can be, the integer (not the text) is returned.

    If it cannot, then an error msg is displayed, and the integerbox is
    redisplayed.

    If the user cancels the operation, None is returned.

    NOTE that the "argLowerBound" and "argUpperBound" arguments are no longer
    supported.  They have been replaced by "upperbound" and "lowerbound".

integerbox()为用户提供一个简单的输入框,用户只能输入范围内的(lowerbound为最小值,upperbound为最大值)整形数值,否则会要求用户重新输入。

习题1

实现数字小游戏图

import easygui as g
import random
g.msgbox("嗨,欢迎进入第一个界面小游戏^_^")
times = 5
secret = random.randint(1,10)
msg = "不妨猜一下小甲鱼现在心里想的是哪个数字(1~10):"
title = "数字小游戏"

while times > 0:
    guess = g.integerbox(msg,title, lowerbound=1, upperbound=10)
    times = times - 1
    if guess == secret:
        g.msgbox("卧槽,你是小甲鱼心里的蛔虫吗?!",title=title)
        g.msgbox("哼,猜中了也没有奖励!",title=title)
        break
    else:
        if guess > secret:
            g.msgbox("哥,大了大了~~~",title=title)
        else:
            g.msgbox("嘿,小了,小了~~~",title=title)
        if times > 0:
            g.msgbox("你还有【%s】次机会,再试一次吧:" % times,title=title)
        else:
            g.msgbox("机会用光咯T_T",title=title)
g.msgbox("游戏结束,不玩拉o(^▽^)o",title=title)






11.3 multenterbox()

>>> help(g.multenterbox)
Help on function multenterbox in module easygui:

multenterbox(msg='Fill in values for the fields.', title=' ', fields=(), values=())
    Show screen with multiple data entry fields.

    If there are fewer values than names, the list of values is padded with
    empty strings until the number of values is the same as the number of names.

    If there are more values than names, the list of values
    is truncated so that there are as many values as names.

    Returns a list of the values of the fields,
    or None if the user cancels the operation.

    Here is some example code, that shows how values returned from
    multenterbox can be checked for validity before they are accepted::
        ----------------------------------------------------------------------
        msg = "Enter your personal information"
        title = "Credit Card Application"
        fieldNames = ["Name","Street Address","City","State","ZipCode"]
        fieldValues = []  # we start with blanks for the values
        fieldValues = multenterbox(msg,title, fieldNames)

        # make sure that none of the fields was left blank
        while 1:
            if fieldValues == None: break
            errmsg = ""
            for i in range(len(fieldNames)):
                if fieldValues[i].strip() == "":
                    errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
            if errmsg == "":
                break # no problems found
            fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)

        writeln("Reply was: %s" % str(fieldValues))
        ----------------------------------------------------------------------

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg fields: a list of fieldnames.
    @arg values:  a list of field values

multenterbox()为用户提供多个简单的输入框,要注意以下几点:
- 如果用户输入的值比较少的话,则返回列表中的值用空字符串填充用户输入的选项。
- 如果用户输入的值比选项多的话,则返回列表中的值将截断为选项的数量。
- 如果用户取消操作,则返回的为列表的值或者None值

习题2

实现一个用于登记用户账号信息的界面(如果是带*号的是必填项,要求一定要有输入并且不能是空格):

import easygui as g
msg = '【*真实姓名为必填项】\n【*手机号码为必填项】\n【*E-mail为必填项】'
title = "账号中心"
fieldNames = ["用户名", "*真实姓名", "固定电话", "*手机号码", "QQ","*E-mail"]
fieldValues = []  # we start with blanks for the values
fieldValues = g.multenterbox(msg, title, fieldNames)

# 确保必填项字段不为空
while 1:
    if fieldValues == None:
        break
    errmsg = ""
    for i in range(len(fieldNames)):
        option = fieldNames[i].strip()
        if fieldValues[i].strip() == "" and option[0] == '*':
            errmsg += ('"%s" 是必填字段.\n\n' % fieldNames[i])
    if errmsg == "":
        break  # no problems found
    fieldValues = g.multenterbox(errmsg, title, fieldNames, fieldValues)

print("用户资料如下:%s" % str(fieldValues))

实现如下图:

12. 让用户输入密码

有时候我们需要让用户输入密码,就是用户输入的东西看上去都是**********

12.1 passwordbox()

>>> help(g.passwordbox)
Help on function passwordbox in module easygui:

passwordbox(msg='Enter your password.', title=' ', default='', image=None, root=None)
    Show a box in which a user can enter a password.
    The text is masked with asterisks, so the password is not displayed.
    Returns the text that the user entered, or None if he cancels the operation.

passwordbox()跟enterbox()样式一样,不同的是用户输入的内容用*显示,返回用户输入的字符串:

import easygui as g
print(g.passwordbox('请输入密码:'))

===输出结果===
D:\untitled\venv\Scripts\python.exe D:/untitled/Python_learn/test1.py
123

12.2 multpasswordbox()

>>> help(g.multpasswordbox)
Help on function multpasswordbox in module easygui:

multpasswordbox(msg='Fill in values for the fields.', title=' ', fields=(), values=())
    Same interface as multenterbox.  But in multpassword box,
    the last of the fields is assumed to be a password, and
    is masked with asterisks.

    Example
    =======

    Here is some example code, that shows how values returned from
    multpasswordbox can be checked for validity before they are accepted::
        msg = "Enter logon information"
        title = "Demo of multpasswordbox"
        fieldNames = ["Server ID", "User ID", "Password"]
        fieldValues = []  # we start with blanks for the values
        fieldValues = multpasswordbox(msg,title, fieldNames)

        # make sure that none of the fields was left blank
        while 1:
            if fieldValues == None: break
            errmsg = ""
            for i in range(len(fieldNames)):
                if fieldValues[i].strip() == "":
                    errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
                if errmsg == "": break # no problems found
            fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)

        writeln("Reply was: %s" % str(fieldValues))

multpasswordbox()跟multenterbox()使用相同的接口,但当它显示的时候,最后一个输入框显示为密码的形式*

from easygui import *
msg = "验证登陆信息"
title = "密码函数multpasswordbox示例"
fieldNames = ["服务器 ID", "用户 ID", "密码"]
fieldValues = []  # we start with blanks for the values
fieldValues = multpasswordbox(msg, title, fieldNames)
print("输入的信息是: %s" % str(fieldValues))

13. 显示文本

EasyGui还提供函数用于显示文本。

13.1 textbox()

>>> help(g.textbox)
Help on function textbox in module easygui:

textbox(msg='', title=' ', text='', codebox=0)
    Display some text in a proportional font with line wrapping at word breaks.
    This function is suitable for displaying general written text.

    The text parameter should be a string, or a list or tuple of lines to be
    displayed in the textbox.

textbox()函数默认会以比例字体(参数codebox = 1设置为等宽字体)来显示文本内容(会自动换行哦),这个函数适合用于显示一般的书面文字。

【注意】:text参数(第三个参数)可以是字符串,列表或者元祖类型。

13.2 codebox()

Help on function codebox in module easygui:

codebox(msg='', title=' ', text='')
    Display some text in a monospaced font, with no line wrapping.
    This function is suitable for displaying code and text that is
    formatted using spaces.

    The text parameter should be a string, or a list or tuple of lines to be
    displayed in the textbox.

codebox()以等宽字体显示文本内容,相当于testbox(codebox = 1)

14. 目录与文件

GUI编程中一个常见的场景是要求用户输入目录及文件名,EasyGui提供了一些基本函数让用户来浏览文件系统,选择一个目录或文件。

14.1 diropenbox()

Help on function diropenbox in module easygui:

diropenbox(msg=None, title=None, default=None)
    A dialog to get a directory name.
    Note that the msg argument, if specified, is ignored.

    Returns the name of a directory, or None if user chose to cancel.

    If the "default" argument specifies a directory name, and that
    directory exists, then the dialog box will start with that directory.

diropenbox()函数用于提供一个对话框,返回用户选择的目录名(带完整路径),如果用户选择Cancel则返回None。
default参数用于设置默认的打开目录(请确保设置的目录已存在。)

>>> g.diropenbox()

===输出结果===
'D:\\Dict'


g.diropenbox(default='d:\\Bin procedure')

14.2 fileopenbox()

>>> help(g.fileopenbox)
Help on function fileopenbox in module easygui:

fileopenbox(msg=None, title=None, default='*', filetypes=None)
    A dialog to get a file name.

    About the "default" argument
    ============================
        The "default" argument specifies a filepath that (normally)
        contains one or more wildcards.
        fileopenbox will display only files that match the default filepath.
        If omitted, defaults to "*" (all files in the current directory).

        WINDOWS EXAMPLE::
            ...default="c:/myjunk/*.py"
        will open in directory c:\myjunk\ and show all Python files.

        WINDOWS EXAMPLE::
            ...default="c:/myjunk/test*.py"
        will open in directory c:\myjunk\ and show all Python files
        whose names begin with "test".


        Note that on Windows, fileopenbox automatically changes the path
        separator to the Windows path separator (backslash).

    About the "filetypes" argument
    ==============================
        If specified, it should contain a list of items,
        where each item is either::
            - a string containing a filemask          # e.g. "*.txt"
            - a list of strings, where all of the strings except the last one
                are filemasks (each beginning with "*.",
                such as "*.txt" for text files, "*.py" for Python files, etc.).
                and the last string contains a filetype description

        EXAMPLE::
            filetypes = ["*.css", ["*.htm", "*.html", "HTML files"]  ]

    NOTE THAT
    =========

        If the filetypes list does not contain ("All files","*"),
        it will be added.

        If the filetypes list does not contain a filemask that includes
        the extension of the "default" argument, it will be added.
        For example, if     default="*abc.py"
        and no filetypes argument was specified, then
        "*.py" will automatically be added to the filetypes argument.

    @rtype: string or None
    @return: the name of a file, or None if user chose to cancel

    @arg msg: the msg to be displayed.
    @arg title: the window title
    @arg default: filepath with wildcards
    @arg filetypes: filemasks that a user can choose, e.g. "*.txt"

fileopenbox()函数用于提供一个对话框,返回用户选择的文件名(带完整路径),如果用户选择Cancel则返回None

关于default参数的设置方法:
- default参数指定一个默认路径,通常包含一个或多个通配符。
- 如果设置了default参数,fileopenbox()显示默认的文件路径和格式。
- default默认的参数是*,即匹配所有格式的文件。

例如:
1. default = ‘c:/fishc/*.py’即显示c:\fishc文件夹下所有的Python文件
2. default = ‘c:/fishc/test*.py’即显示c:\fishc文件夹下所有的名字以test开头的Python文件

关于filetypes参数的设置方法:
- 可以是包含文件掩码的字符串列表。例如:filetypes = ['*.txt']
- 可以是字符串列表,列表的最后一项字符串是文件类型的描述,例如:filetypes = ['*.css',['*.htm','*.html','*.HTML files']]

>>> g.fileopenbox(default = 'E:\software\*.exe')
'E:\\software\\pycharm-professional-2018.1.1.exe'


>>> g.fileopenbox(filetypes = ['*.py',['*.py','*.python file']])
'D:\\untitled\\Python_learn\\将一个数分解成列表.py'

习题3:
提供一个文件夹浏览框,让用户选择需要打开的文件,打开并显示文件内容:

import easygui as g
import os

file_path = g.fileopenbox(default="*.txt")

with open(file_path) as f:
    title = os.path.basename(file_path)
    msg = "文件【%s】的内容如下:" % title
    text = f.read()
    g.textbox(msg, title, text)


14.3 filesavebox()

>>> help(g.filesavebox)
Help on function filesavebox in module easygui:

filesavebox(msg=None, title=None, default='', filetypes=None)
    A file to get the name of a file to save.
    Returns the name of a file, or None if user chose to cancel.

    The "default" argument should contain a filename (i.e. the
    current name of the file to be saved).  It may also be empty,
    or contain a filemask that includes wildcards.

    The "filetypes" argument works like the "filetypes" argument to
    fileopenbox.

filesavebox()函数提供一个对话框,让用于选择的文件需要保存的路径(带完整路劲),如果用户选择Cancel则返回None。

default参数应该包含一个文件名(例如当前需要保存的文件名),当然你也可以设置为空的,或者包含一个文件格式掩码的通配符。

filetypes参数类似于fileopenbox()函数的filetypes参数。

>>> g.filesavebox(default='test.txt')


'C:\\Users\\ThinkPad\\Desktop\\test.txt'

习题4

习题3的基础上增强功能:当用户点击OK按钮的时候,比较当前文件是否修改过,如果修改过,则提示覆盖保存,放弃保存另存为...并实现相应功能。

(提示:解决这道题可能需要点耐心,因为你有可能被一个小问题卡住,但请坚持,自己想办法找到这个小问题并解决它!)

import easygui as g
import os

file_path = g.fileopenbox(default='*.txt')

with open(file_path) as old_file:
    title = os.path.basename(file_path)
    msg = '文件【%s】的内容如下:' % title
    text = old_file.read()
    text_after = g.textbox(msg,title,text)

if text != text_after[:-1]: #textbox的返回值会在末尾追加一个换行符    
    #buttonbox(msg='', title=' ', choices=('Button1', 'Button2', 'Button3'), image=None, root=None)
    choice = g.buttonbox('检测到文件内容发生改变,请选择以下操作:','警告',('覆盖保持','放弃保存','另存为...'))
    if choice == '覆盖保存':
        with open(file_path,'w') as old_file:
            old_file.write(text_after[:-1])
    if choice == '放弃保存':
        pass
    if choice == '另存为...':
        another_path = g.filesavebox(default='.txt')
        if os.path.splitext(another_path)[1] != '.txt':
            another_path += '.txt'
        with open(another_path,'w') as new_file:
            new_file.write(text_after[:-1])

15. 记住用户的设置

15.1 EgStore

>>> help(g.EgStore)

Help on class EgStore in module easygui:

class EgStore(builtins.object)
 |  A class to support persistent storage.
 |  
 |  You can use EgStore to support the storage and retrieval
 |  of user settings for an EasyGui application.
 |  
 |  
 |  # Example A
 |  #-----------------------------------------------------------------------
 |  # define a class named Settings as a subclass of EgStore
 |  #-----------------------------------------------------------------------
 |  class Settings(EgStore):
 |  ::
 |      def __init__(self, filename):  # filename is required
 |          #-------------------------------------------------
 |          # Specify default/initial values for variables that
 |          # this particular application wants to remember.
 |          #-------------------------------------------------
 |          self.userId = ""
 |          self.targetServer = ""
 |  
 |          #-------------------------------------------------
 |          # For subclasses of EgStore, these must be
 |          # the last two statements in  __init__
 |          #-------------------------------------------------
 |          self.filename = filename  # this is required
 |          self.restore()            # restore values from the storage file if possible
 |  
 |  
 |  
 |  # Example B
 |  #-----------------------------------------------------------------------
 |  # create settings, a persistent Settings object
 |  #-----------------------------------------------------------------------
 |  settingsFile = "myApp_settings.txt"
 |  settings = Settings(settingsFile)
 |  
 |  user    = "obama_barak"
 |  server  = "whitehouse1"
 |  settings.userId = user
 |  settings.targetServer = server
 |  settings.store()    # persist the settings
 |  
 |  # run code that gets a new value for userId, and persist the settings
 |  user    = "biden_joe"
 |  settings.userId = user
 |  settings.store()
 |  
 |  
 |  # Example C
 |  #-----------------------------------------------------------------------
 |  # recover the Settings instance, change an attribute, and store it again.
 |  #-----------------------------------------------------------------------
 |  settings = Settings(settingsFile)
 |  settings.userId = "vanrossum_g"
 |  settings.store()
 |  
 |  Methods defined here:
 |  
 |  __init__(self, filename)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __str__(self)
 |      return my contents as a string in an easy-to-read format.
 |  
 |  kill(self)
 |      Delete my persistent file (i.e. pickle file), if it exists.
 |  
 |  restore(self)
 |      Set the values of whatever attributes are recoverable
 |      from the pickle file.
 |      
 |      Populate the attributes (the __dict__) of the EgStore object
 |      from     the attributes (the __dict__) of the pickled object.
 |      
 |      If the pickled object has attributes that have been initialized
 |      in the EgStore object, then those attributes of the EgStore object
 |      will be replaced by the values of the corresponding attributes
 |      in the pickled object.
 |      
 |      If the pickled object is missing some attributes that have
 |      been initialized in the EgStore object, then those attributes
 |      of the EgStore object will retain the values that they were
 |      initialized with.
 |      
 |      If the pickled object has some attributes that were not
 |      initialized in the EgStore object, then those attributes
 |      will be ignored.
 |      
 |      IN SUMMARY:
 |      
 |      After the recover() operation, the EgStore object will have all,
 |      and only, the attributes that it had when it was initialized.
 |      
 |      Where possible, those attributes will have values recovered
 |      from the pickled object.
 |  
 |  store(self)
 |      Save the attributes of the EgStore object to a pickle file.
 |      Note that if the directory for the pickle file does not already exist,
 |      the store operation will fail.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

GUI编程中一个常见的场景就是要求用户设置一下参数,然后保存下来,以便下次用户使用你的程序的时候可以记住他的设置。

为了实现对用户的设置进行存储和恢复这一过程,EasyGui提供了一个EgStore的类。为了记住某些设置,你的应用程序必须定义一个类(暂时称之为“设置”类,尽管你随意的使用你想要的名称设置它)继承自EgStore类。

然后你的应用程序必须创建一个该类的对象(暂时称之为“设置”对象)

设置类的构造函数(__init__方法)必须初始化所有的你想要它记住的那些值。

一旦你这样做了,你就可以在“设置”对象中通过设定值去实例化变量,从而简单地记住设置。之后使用setting.store()方法在硬盘上持久化设置对象。

下面是创建一个“设置”类的例子:

#-----------------------------------------------------------------------
# create "settings", a persistent Settings object
# Note that the "filename" argument is required.
# The directory for the persistent file must already exist.
#-----------------------------------------------------------------------
settingsFilename = os.path.join("C:", "FishCApp", "settings.txt")  # Windows example
settings = Settings(settingsFilename)

下面是使用“设置”对象的例子:

# we initialize the "user" and "server" variables
# In a real application, we'd probably have the user enter them via enterbox
user    = "奥巴马"
server  = "白宫"

# we save the variables as attributes of the "settings" object
settings.userId = user
settings.targetServer = server
settings.store()    # persist the settings

# run code that gets a new value for userId
# then persist the settings with the new value
user    = "小甲鱼"
settings.userId = user
settings.store()

16. 捕获异常

Help on function exceptionbox in module easygui:

exceptionbox(msg=None, title=None)
    Display a box that gives information about
    an exception that has just been raised.

    The caller may optionally pass in a title for the window, or a
    msg to accompany the error information.

    Note that you do not need to (and cannot) pass an exception object
    as an argument.  The latest exception will automatically be used.

使用EasyGui编写GUI程序,有时候难免会产生异常。当然这取决于你如何运行你的应用程序,当你的应用程序崩溃的时候,堆栈追踪可能会被抛出,或者被写入到stdout标准输出函数中。

EasyGui通过exceptionbox()函数提供了更好的方式去处理异常,异常出现的时候,exceptionbox()会显示堆栈追踪在一个codebox()中并且允许你做进一步的处理。

exceptionbox()很容易使用,请看下例:


import easygui as g

try:
    print('i love fishc.com')
    int('FishC') #这里会产生异常
except:
    g.exceptionbox()

习题5:

写一个程序统计你当前代码量的总和,并显示离十万行代码量还有多远?

  • 要求1:递归搜索各个文件夹
  • 要求2:显示各个类型的源文件和源代码数量
  • 要求3:显示总行数与百分比

截图1:

截图2:

import easygui as g
import os

def show_result(start_dir):
    lines = 0
    total = 0
    text = ""

    for i in source_list:
        lines = source_list[i]
        total += lines
        text += "【%s】源文件 %d 个,源代码 %d 行\n" % (i, file_list[i], lines)
    title = '统计结果'
    msg = '您目前共累积编写了 %d 行代码,完成进度:%.2f %%\n离 10 万行代码还差 %d 行,请继续努力!' % (total, total/1000, 100000-total)
    g.textbox(msg, title, text)

def calc_code(file_name):
    lines = 0
    with open(file_name) as f:
        print('正在分析文件:%s ...' % file_name)
        try:
            for each_line in f:
                lines += 1
        except UnicodeDecodeError:
            pass # 不可避免会遇到格式不兼容的文件,这里忽略掉......
    return lines

def search_file(start_dir) :
    os.chdir(start_dir)

    for each_file in os.listdir(os.curdir) :
        ext = os.path.splitext(each_file)[1]
        if ext in target :
            lines = calc_code(each_file) # 统计行数
            # 还记得异常的用法吗?如果字典中不存,抛出 KeyError,则添加字典键
            # 统计文件数
            try:
                file_list[ext] += 1
            except KeyError:
                file_list[ext] = 1
            # 统计源代码行数
            try:
                source_list[ext] += lines
            except KeyError:
                source_list[ext] = lines

        if os.path.isdir(each_file) :
            search_file(each_file) # 递归调用
            os.chdir(os.pardir) # 递归调用后切记返回上一层目录

target = ['.c', '.cpp', '.py', '.cc', '.java', '.pas', '.asm']
file_list = {}
source_list = {}

g.msgbox("请打开您存放所有代码的文件夹......", "统计代码量")
path = g.diropenbox("请选择您的代码库:")

search_file(path)
show_result(path)

猜你喜欢

转载自blog.csdn.net/wanbin6470398/article/details/80420896
今日推荐