《Python编程》笔记(十二)

来源:互联网 发布:灰原穷 知乎 编辑:程序博客网 时间:2024/06/06 11:35

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 wdef 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):        passdef 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