Python.use(better, Tkinter=”GoF”) # Observer

記事一覧

Python.use(better, Tkinter=”GoF") # Python で学ぶ デザインパターンの世界
Observer 《Python3.1》

《著》森こねこ・小粒ちゃん+∞《監修》小泉ひよ子とタマゴ倶楽部
α版♪1993/11/25 ● β版♪1995/11/22 ● 第1版♪2006/10/28

概要

Tkinter アプリケーションとして、Python で実現した事例を紹介します。

Tkinter によるオブジェクト指向プログラミングへの扉を開きます。
※ Tcl/Tk で作成した例題を、Tkinter で再構成しました。

事例

次のコードを実行すると、ウィンドウが現われます。

>>> observer()





from tkinter import *
from tkinter.filedialog import FileDialog

## ---------------------------------------- Observer::Subject
class Subject(object):
    def attach_(self, observer): pass
    def detach_(self, observer): pass
    def notify_(self): pass

## ---------------------------------------- Observer::Observer
class Observer(object):
    def update_(self): pass

## ---------------------------------------- Observer::ConcreteSubject
class ConcreteSubject(Subject):
    pass

## ---------------------------------------- Observer::ConcreteSubject
class ColorSubject(Subject):
    def __init__(self):
        self.value = StringVar()
        self.observers = []

    def background(self, value=None):
        if value is None:
            return self.value.get()
        else:
            self.value.set(value)
            self.notify_()

    def attach_(self, observer):
        self.observers.append(observer)

    def notify_(self):
        for e in self.observers:
            e.update_()

## ---------------------------------------- Observer::ConcreteObserver
class ConcreteObserver(Observer):
    def _subject(self, subject):
        self.subject = subject
        self.subject.attach_(self)

## ----------------------------------------
class TIPS(ConcreteObserver):
    def __init__(self, master):
        self._subject(ColorSubject())

        self.master = master
        self.canvas = self.create_Canvas(master)
        self.create_Menubutton(master)
        self.create_CommandButton(master)

    ## ----------------------------------------
    def update_(self):
        bg = self.subject.value.get()
        self.canvas.config(bg=bg)

    ## ----------------------------------------
    def create_Canvas(self, master):
        widget = Canvas(
            master=master,
            width=100, height=100, relief=RIDGE, borderwidth=5,
            )
        widget.pack(side=LEFT)
        return widget

    def create_Menubutton(self, master):
        widget = Menubutton(
            master=master,
            text='File',
            )
        widget.pack(side=TOP)

        menu = Menu(widget)
        widget['menu'] = menu

        for text, command in [
            ("Open..."       , self.fileCommand),
            ("Canvas & Scale", self.openCommand),
            ("Exit"          , self.exitCommand),
        ]:
            CommandMenu(
                menu=menu,
                text=text,
                command=command,
                )

    def create_CommandButton(self, master):
        frame = Frame(master)
        frame.pack(side=TOP)
        
        s = "aliceblue", "beige", "lavender",
        for e in s:
            widget = CommandButton(
                master=frame,
                text=e,
                variable=self.subject.value,
                command=self.notify_,
                )
            widget.pack(anchor=W)

    ## ----------------------------------------
    def fileCommand(self):
        dir_or_file = "./_flags"
        s = FileDialog(self.master).go(dir_or_file)
        self.image = PhotoImage(file=s)

        s    = s.split("/")[-1]
        text = s.split(".")[0]

        dx = self.master["padx"]
        dx = int(str(dx))
        x, y, dy = 50+dx, 80, 8

        self.canvas.create_image(
            x, y   , anchor=S, image=self.image)
        self.canvas.create_text(
            x, y+dy, anchor=N, text=text)
                
    def openCommand(self):
        CanvasScale(self.subject)

    def exitCommand(self):
        self.master.destroy()

    ## ----------------------------------------
    def notify_(self):
        for e in self.subject.observers:
            e.update_()

## ---------------------------------------- Observer::ConcreteSubject
class CanvasScale(ConcreteObserver):
    items = "red", "green", "blue",

    def __init__(self, subject):
        self._subject(subject)

        top = Toplevel()
        top.title("Canvas & Scale")
        top.config(padx=12, pady=4)

        self.values = self.create_values()
        self.canvas = self.create_Canvas(master=top)
        self.scales = self.create_Scale(master=top)

    def create_values(self):
        return dict((e,IntVar()) for e in self.items)

    def create_Canvas(self, master):
        widget = Canvas(
            master=master,
            width=100, height=100, relief=RIDGE, borderwidth=5,
            )
        widget.pack(side=LEFT)
        return widget

    def create_Scale(self, master):
        frame = Frame(master=master)
        frame.pack(side=RIGHT)

        s = {}
        for e in self.items:
            widget = Scale(
                master=frame,
                from_=0, to=255, orient=HORIZONTAL, length=255,
                variable=self.values[e],
                command=self,
                )
            widget.pack()
            s[e] = widget
        return s

    ## ----------------------------------------
    def update_(self):
        bg = self.subject.background()
        self.canvas.config(bg=bg)

        rgb = [e>>8 for e in self.canvas.winfo_rgb(bg)]
        for key, value in zip(self.items, rgb):
            self[key] = value

    ## ----------------------------------------
    def __getitem__(self, key):
        return self.values[key].get()

    def __setitem__(self, key, value):
        self.values[key].set(value)
        
    def __call__(self, event):
        value = [self[e] for e in self.items]
        bg = ("#"+"%02X"*len(value))%tuple(value)

        self.canvas.config(bg=bg)
        self.subject.background(value=bg)

## ----------------------------------------
class CommandHolder(object):
    pass

class CommandButton(CommandHolder, Radiobutton):
    def __init__(self, master, text, variable, command):
        super().__init__(
            master=master,
            text=text,
            value=text,
            variable=variable,
            command=command,
            )

class CommandMenu(CommandHolder):
    def __init__(self, menu, text, command):
        menu.add_radiobutton(
            label=text,
            command=command,
            )

## ========================================
def observer():
    root = Tk()
    root.title("Observer")
    root.config(padx=8, pady=4)

    TIPS(master=root)    

    root.mainloop()

TOP

関連記事

Last updated♪2009/09/15