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

記事一覧

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

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

概要

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

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

事例

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

>>> strategy()



## ----------------------------------------
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

## ----------------------------------------
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)

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

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

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

    ## ----------------------------------------
    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),
            ("Scale"  , self.openScale  ),
            ("Entry"  , self.openEntry  ),
            ("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 openScale(self):
        self.context.setScaleStrategy()
        self.context.open_()

    def openEntry(self):
        self.context.setEntryStrategy()
        self.context.open_()

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

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

## ---------------------------------------- Strategy::Context
class Context(object):

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

    ## ----------------------------------------
    def setScaleStrategy(self):
        self.strategy = ScaleStrategy(self.subject)

    def setEntryStrategy(self):
        self.strategy = EntryStrategy(self.subject)

    def open_(self):
        self.strategy.create_()

## ---------------------------------------- Strategy::Strategy
class Strategy(object):
    def create_(self): pass

## ---------------------------------------- Strategy::ConcreteStrategy
class ConcreteStrategy(Strategy):
    items = "red", "green", "blue",

    def __init__(self, subject):
        self._subject(subject)
        self.values = self.create_values()
        self.top = self.create_Toplevel()

    def _subject(self, subject):
        self.subject = subject
        self.subject.attach_(self)

    def create_values(self):
        return dict*1 for e in self.items)

    def create_Toplevel(self):
        widget = Toplevel()
        widget.title("Entry")
        widget.config(padx=50, pady=4)
        return widget

    ## ----------------------------------------
    def update_(self):
        bg = self.subject.background()
        rgb = [e>>8 for e in self.top.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.subject.background(value=bg)

## ---------------------------------------- Observer::ConcreteSubject
class ScaleStrategy(ConcreteStrategy):

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

## ---------------------------------------- Observer::ConcreteSubject
class EntryStrategy(ConcreteStrategy):

    def create_(self):
        for e in self.items:
            widget = Entry(
                master=self.top,
                width=5,
                textvariable=self.values[e],
                )
            widget.bind('', self)
            widget.pack(pady=2)

## ----------------------------------------
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 strategy():
    root = Tk()
    root.title("strategy")
    root.config(padx=8, pady=4)

    TIPS(master=root)    

    root.mainloop()

TOP

関連記事

Last updated♪2009/09/20

*1:e,IntVar(