Source

lyne / lyne / gui / treewidget.py

Full commit
import time

import Tkinter
import ttk

from lyne.lib.tree import Tree
from lyne.lib.node import ConstantNode, CountingNode, OperatorNode
from lyne.lib.stoppablethread import StoppableThread

from lyne.gui.plugwidget import PlugWidget
from lyne.gui.headerbarwidget import HeaderbarWidget

(IN, OUT) = range(0, 2)

class TreeItemWidget(Tkinter.Frame):
    def __init__(self, canvas):
        Tkinter.Frame.__init__(self, canvas, borderwidth=2, 
                               relief=Tkinter.RAISED)

        self._tree = None # a reference to the underlying data model item
        self._widget = None

        self._lines_in = []
        self._lines_out = []

        self._animation_thread = None

        self._create_widgets()

    def _create_widgets(self):
        self._headerbar = HeaderbarWidget(self)
        self._headerbar.pack(fill=Tkinter.X)

        Tkinter.Widget.bind(self._headerbar.handle, '<Button-1>',
                            self._set_initial_xy)
        Tkinter.Widget.bind(self._headerbar.handle, '<B1-Motion>',
                            self._handle_item_drag)
        Tkinter.Widget.bind(self._headerbar.sound_button, '<Button-1>',
                            self._toggle_noise)
        Tkinter.Widget.bind(self._headerbar.quit_button, '<Button-1>',
                            self.delete)

        self._plug = PlugWidget(self)
        self._plug.pack(fill=Tkinter.X)

        Tkinter.Widget.bind(self._plug, '<Button-1>', self._notify_canvas)
   
    def _toggle_noise(self, event):
        self.dgw.master.toggle_noise(self)

    def set_active(self):
        self.config(background='#FF0000')
        
    def set_inactive(self):
        self.config(background='#D9D9D9')

    def start_animation(self):
        if self._animation_thread == None or not self._animation_thread.isAlive():
            self._animation_thread = StoppableThread(
                target=self._animate)
            self._animation_thread.start()

    def stop_animation(self):
        if self._animation_thread:
            self._animation_thread.stop()

    def _animate(self):
        self.config(background='#00FF00')
        time.sleep(0.25)
        self.config(background='#FF0000')
        time.sleep(0.75)

    def _notify_canvas(self, event):
        self.dgw.handle_item_plug_click(self._w_id)

    def delete(self, event=None):
        # delete this widget from the canvas
        self.master.delete(self._w_id)

        # delete the reference to this item 
        # from the app's widget reference dict
        del self.dgw._widgets[self._w_id]

        # delete attached lines / references to widgets
        for widget in self._lines_in:
            widget._lines_out.remove(self)
            self.dgw._lines[(widget, self)].delete()
            del self.dgw._lines[(widget, self)]
        for widget in self._lines_out:
            widget._lines_in.remove(self)
            self.dgw._lines[(self, widget)].delete()
            del self.dgw._lines[(self, widget)]

        # if this item was a counting widget, then
        # tell that the app
        if isinstance(self._tree.node, CountingNode):
            self.dgw.master._countingnode = None

        # finally, update the underlying data model
        self._tree.delete()

    def _set_initial_xy(self, event):
        self.initial_x = event.x
        self.initial_y = event.y

    def _handle_item_drag(self, event):
        self.dgw.move_item(
            self._w_id, 
            event.x-self.initial_x, 
            event.y-self.initial_y
        )
        # do not save the event's x and y coords in our last.x and y coords
        # because  when we move the item on the canvas we also move the
        # coordinate system.
        # x and y coordinates are relative to the handle
        # and not to the canvas!

    def set_window_id(self, w_id):
        self._w_id = w_id

    def attach_parentwidget(self, widget):
        self._lines_in.append(widget)

    def attach_subwidget(self, widget):
        self._lines_out.append(widget)

    def has_inputs(self):
        ''' Override this method if necessary.
        '''
        return False

    def has_output(self):
        ''' Override this method if necessary.
        '''
        return False

class TreeItemConstantWidget(TreeItemWidget):
    def __init__(self, canvas, tree=None):
        TreeItemWidget.__init__(self, canvas)
        self._tree = Tree(ConstantNode()) if not tree else tree
        self._widget.insert(0, str(self._tree.node._value))

    def _create_widgets(self):
        TreeItemWidget._create_widgets(self)
        self._widget = Tkinter.Entry(
            self, 
            width=5, 
            validate="key",
            validatecommand=(self.register(self._text_entered), '%W', '%P')
        )
        self._widget.pack(fill=Tkinter.X)

    def _text_entered(self, widget, value):
        try:
            value_int = int(value if len(value)>0 else 0)
        except ValueError:
            return False
        self._tree.node.value = value_int
        return True

    def has_output(self):
        return True

class TreeItemCounterWidget(TreeItemWidget):
    def __init__(self, canvas, tree=None):
        TreeItemWidget.__init__(self, canvas)
        self._tree = Tree(CountingNode()) if not tree else tree

    def _create_widgets(self):
        TreeItemWidget._create_widgets(self)
        self._widget = Tkinter.Label(self, text='count', background='white', 
                                     width=5)
        self._widget.pack(fill=Tkinter.X)

    def has_output(self):
        return True

class TreeItemOperatorWidget(TreeItemWidget):
    def __init__(self, canvas, tree=None):
        TreeItemWidget.__init__(self, canvas)
        self._tree = Tree(OperatorNode()) if not tree else tree
        try:
            idx = OperatorNode.allowed_operators.index(
                self._tree.node._operator)
        except ValueError, msg:
            raise ValueError, 'Invalid operator found: %r\n[%s]' % (
                self._tree.node._operator, msg)
        self._widget.current(idx)

    def _create_widgets(self):
        TreeItemWidget._create_widgets(self)
        self._widget = ttk.Combobox(self, values=OperatorNode.allowed_operators,
                                state='readonly', width=4)
        self._widget.pack(fill=Tkinter.X)
        Tkinter.Widget.bind(self._widget, '<<ComboboxSelected>>',
                            self._combobox_selected)

    def _combobox_selected(self, event):
        assert isinstance(event.widget, ttk.Combobox), 'Expected widget ' \
            'of type Combobox, not %r' % (event.widget,)
        current_selection = event.widget.current()
        new_operator = OperatorNode.allowed_operators[current_selection]
        self._tree.node.operator = new_operator

    def has_inputs(self):
        return True

    def has_output(self):
        return True