《Python编程》笔记(十二)

GUI编写技巧

  • 技巧:

    • 在“混合类”中提供常见的GUI操作
    • 从数据结构模板中创建菜单和工具栏
    • 为命令行工具添加GUI接口
    • 将输入和输出流重定向到GUI组件
    • 重新加载运行中的GUI回调处理程序
    • 封装和自动化顶层窗口的接口
    • 使用线程和队列避免GUI中的阻塞
    • 根据需要从非GUI程序中弹出GUI窗口
    • 用套接字和管道将GUI作为单独的程序实现添加
  • 组件生成器函数:可以尽可能地将繁琐的组件设置放在函数中封装,使用时只需要调用函数即可完成设置。实际上这种方法可以用OOP来代替,那样可复用性更高!下面的代码演示了组件生成器函数的使用方法,完整的代码参见这里

def frame(master, side=TOP, **kw):
    """
    Generate a Frame widget with custom configurations.
    """
    w = Frame(master)
    w.pack(side=side, expand=YES, fill=BOTH)
    if kw:
        w.config(**kw)
    return w

def main():
    app = Tk()
    f = frame(app)
        app.mainloop()
  • 混合类:在一个类中使用常用的方法,在任何需要的地方继承。混合类通常可以用来封装有用的工具,在需要的时候使用。下面给出一个简单的示例用法,完整示例参见这里
class MixinDemo:
    def info_box(self, title, text, **options):
        return showinfo(title, text, **options)

    def question(self, title, text, **options):
        return askyesno(title, text, **options)

    def quit(self):
        ans = self.question('Quit?', 'Are you sure you want to quit?')
        if ans:
            exit()

class Test(Frame, MixinDemo):
    def __init__(self, master=None, cnf={}, **kw):
        super(Test, self).__init__(master, cnf, **kw)
        self.pack()
        Button(self, text='Info Box', command=lambda: self.info_box('info', 'test info')).grid(row=0, column=0)
        Button(self, text='Question', command=lambda: self.question('question', 'test question')).grid(row=1, column=0)
        Button(self, text='Quit', command=self.quit).grid(row=2, column=0)

  • 菜单栏和状态栏实际上是比较常用的,但是在tkinter中创建它们却比较繁琐,重复性工作很多,所以可以在类中封装生成的方法,通过读取并解析固定格式的配置来自动生成。
  • 重定向流到组件:这里需要使用的技巧就是实现两个类似文件的类(即,分别实现write, read等接口),然后将命令行中的sys.stdin, sys.stdout, sys.stderr重定向到要显示的组件(如Text组件)即可。
class GuiConsole(Frame):
    def __init__(self, master=None, cnf={}, **kw):
        super(GuiConsole, self).__init__(master, cnf, **kw)
        self.pack(fill=BOTH, expand=YES)
        self.console = ScrolledText(self, font=('Source Code Pro', 12, 'normal'))
        self.console.pack(side=TOP, fill=BOTH, expand=YES, padx=5, pady=5)
        self.console.focus()
        self.console.mark_set(INSERT, '1.0')

    def clear(self):
        self.console.delete('1.0', END)

    def write(self, text):
        text = '{}'.format(text)
        self.console.insert(INSERT, text)
        self.console.mark_set(INSERT, INSERT+'+{}c'.format(len(text)))

    def writeline(self, text):
        self.write('{}\n'.format(text))

    def writelines(self, lines):
        for line in lines:
            self.writeline(line)

    def read(self):
        pass

def main():
    import sys
    app = Tk()
    c = GuiConsole(app)
    save_streams = sys.stdin, sys.stdout
    sys.stdout = c
    print('hello, world')
    sys.stdin, sys.stdout = save_streams
    app.mainloop()

  • 动态重载回调处理器(仅作了解,需要时候深入):

    • 即在GUI运行时修改GUI,这块很好理解,自己领悟!
    • importlib.reload函数允许动态修改并且重载程序的模块,无须停止程序。这个特性特别适合开发需要很长事件才能重新启动的程序。对于连接到数据库或网络服务器的程序、初始化大型对象的程序、执行长期运行服务的程序,或者需要经过较多步骤才能重新触发回调的程序,都可以作为reload的主要对象。它可以在开发周期中节省大量时间,并且使系统更加灵活
    • 相关的测试代码参见ch10/reload.py, ch10/actions.py,可以在测试中看到修改actions.py中的打印消息后,也会在按下按钮时打印最新修改后的结果。
  • GUI应用中,长期运行的操作必须在并行的线程中运行,这样才可以避免GUI因自我更新或响应新的用户请求而遭遇阻塞。长期运行的操作:时间密集型函数调用、服务器下载、阻塞输入/输出调用等。

  • 多线程中,只使用主线程来更行显示,其他并行线程负责产生数据供主线程消费,这种是典型的生产者/消费者模型。
  • 来看一个简单的示例,学习如何在GUI程序中使用多线程的生产者消费者模型。完整的代码参见ch10/queue-thread-gui.py,以及封装在类中的示例ch10/queue-thread-gui-class.py,以下是关键代码:
def producer(id_):
    for i in range(4):
        time.sleep(0.1)
        data.put('[producer: id is {}, count is {}]\n'.format(id_, i))


def consumer(out):
    try:
        d = data.get(block=False)
    except queue.Empty:
        # out.writeline('No data in queue now.')
        pass
    else:
        out.write(d)

    out.after(100, lambda: consumer(out))

  • 在队列中放置回调:通过将函数或其他任何可调用对象放置在队列中,生产者线程可以非常直接地告知GUI如何处理消息。所以,我们可以构建调用队列,等待主线程处理。相关的实例参见ch10/threadtools

  • 可以通过进程间通信机制为非GUi脚本添加图形界面,比如利用管道、套接字、文件或者其他方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值