title = 'Blt Graph demonstration'

# Import Pmw from this directory tree.
import sys
sys.path[:0] = ['../../..']

import string
import tkinter
import Pmw

# Simple random number generator.
rand = 12345
def random():
    global rand
    rand = (rand * 125) % 2796203
    return rand

class GraphDemo(Pmw.MegaToplevel):

    def __init__(self, parent=None, **kw):

        # Define the megawidget options.
        optiondefs = (
            ('size',      10,   Pmw.INITOPT),
        )
        self.defineoptions(kw, optiondefs)

        # Initialise the base class (after defining the options).
        Pmw.MegaToplevel.__init__(self, parent)

        # Create the graph.
        self.createWidgets()

        # Check keywords and initialise options.
        self.initialiseoptions()

    def createWidgets(self):
        # Create vectors for use as x and y data points.
        self._numElements = 7
        self._vectorSize = self['size']
        self._vector_x = Pmw.Blt.Vector()
        self._vector_y = []
        for y in range(self._numElements):
            self._vector_y.append(Pmw.Blt.Vector())
        for index in range(self._vectorSize):
            self._vector_x.append(index)
            for y in range(self._numElements):
                self._vector_y[y].append(random() % 100)

        interior = self.interior()

        controlFrame = tkinter.Frame(interior)
        controlFrame.pack(side = 'bottom', fill = 'x', expand = 0)

        # Create an option menu for the kind of elements to create.
        elementtype = Pmw.OptionMenu(controlFrame,
                labelpos = 'nw',
                label_text = 'Element type',
                items = ['bars', 'lines', 'mixed', 'none'],
                command = self._setelementtype,
                menubutton_width = 8,
        )
        elementtype.pack(side = 'left')

        # Create an option menu for the barmode option.
        barmode = Pmw.OptionMenu(controlFrame,
                labelpos = 'nw',
                label_text = 'Bar mode',
                items = ['normal', 'stacked', 'aligned', 'overlap'],
                command = self._setbarmode,
                menubutton_width = 8,
        )
        barmode.pack(side = 'left')

        # Create an option menu for the smooth option.
        self.smooth = Pmw.OptionMenu(controlFrame,
                labelpos = 'nw',
                label_text = 'Smooth',
                items = ['linear', 'step', 'natural', 'quadratic'],
                command = self._setsmooth,
                menubutton_width = 9,
        )
        self.smooth.pack(side = 'left')

        # Create an option menu to reverse sort the elements.
        sortelements = Pmw.OptionMenu(controlFrame,
                labelpos = 'nw',
                label_text = 'Order',
                items = ['normal', 'reverse'],
                command = self._setsortelements,
                menubutton_width = 8,
        )
        sortelements.pack(side = 'left')

        # Create an option menu for the bufferelements option.
        bufferelements = Pmw.OptionMenu(controlFrame,
                labelpos = 'nw',
                label_text = 'Buffering',
                items = ['buffered', 'unbuffered'],
                command = self._setbufferelements,
                menubutton_width = 10,
        )
        bufferelements.pack(side = 'left')

        # Create a button to add a point to the vector.
        addpoint = tkinter.Button(controlFrame, text = 'Add point',
                command = Pmw.busycallback(self._addpoint))
        addpoint.pack(side = 'left', fill = 'x', expand = 0)

        # Create a button to close the window
        close = tkinter.Button(controlFrame, text = 'Close',
                command = Pmw.busycallback(self.destroy))
        close.pack(side = 'left', fill = 'x', expand = 0)

        # Create the graph and its elements.
        self._graph = Pmw.Blt.Graph(interior)
        self._graph.pack(expand = 1, fill = 'both')
        self._graph.yaxis_configure(command=self.yaxisCommand)
        elementtype.invoke('mixed')
        bufferelements.invoke('buffered')

    def yaxisCommand(self, graph, value):
        try:
            num = int(value)
            return '%d      %3d' % (num * 3, num)
        except ValueError:
            num = float(value)
            return '%g      %3g' % (num * 3, num)

    def _setelementtype(self, type):
        elements = self._graph.element_names()
        self._graph.element_delete(*elements)

        if type == 'none':
            return

        colorList = Pmw.Color.spectrum(self._numElements)
        for elem in range(self._numElements):
            if elem == 0:
                hue = None
            else:
                hue = (elem + 1.0) / self._numElements * 6.28318
            foreground = colorList[elem]
            background = Pmw.Color.changebrightness(self, foreground, 0.8)
            if type == 'mixed':
                if elem < self._numElements / 2:
                    bar = 0
                else:
                    bar = 1
            elif type == 'bars':
                bar = 1
            else:
                bar = 0
            if bar:
                self._graph.bar_create(
                    'var' + str(elem),
                    xdata=self._vector_x,
                    ydata=self._vector_y[elem],
                    foreground = foreground,
                    background = background)
            else:
                self._graph.line_create(
                    'var' + str(elem),
                    linewidth = 4,
                    xdata=self._vector_x,
                    ydata=self._vector_y[elem],
                    smooth = self.smooth.getcurselection(),
                    color = foreground)

    def _setbarmode(self, tag):
        self._graph.configure(barmode = tag)

    def _setsmooth(self, tag):
        for element in self._graph.element_show():
            if self._graph.element_type(element) == 'line':
                self._graph.element_configure(element, smooth = tag)

    def _setbufferelements(self, tag):
        self._graph.configure(bufferelements = (tag == 'buffered'))

    def _setsortelements(self, tag):
        element_list = list(self._graph.element_show())
        if len(element_list) > 1:
            if (tag == 'normal') == (element_list[-1] != 'var0'):
                element_list.reverse()
                self._graph.element_show(element_list)

    def _addpoint(self):
        self._vector_x.append(self._vectorSize)
        for y in range(self._numElements):
            self._vector_y[y].append(random() % 100)
        self._vectorSize = self._vectorSize + 1

class Demo:
    def __init__(self, parent):
        if not Pmw.Blt.haveblt(parent):
            message = 'Sorry\nThe BLT package has not been\n' + \
                    'installed on this system.\n' + \
                    'Please install it and try again.'
            w = tkinter.Label(parent, text = message)
            w.pack(padx = 8, pady = 8)
            return

        message = 'This is a simple demonstration of the\n' + \
                'BLT graph widget.\n' + \
                'Select the number of points to display and\n' + \
                'click on the button to display the graph.'
        w = tkinter.Label(parent, text = message)
        w.pack(padx = 8, pady = 8)

        # Create combobox to select number of points to display.
        self.combo = Pmw.ComboBox(parent,
                scrolledlist_items = ('10', '25', '50', '100', '300'),
                entryfield_value = '10')
        self.combo.pack(padx = 8, pady = 8)

        # Create button to start blt graph.
        start = tkinter.Button(parent,
                text = 'Show BLT graph',
                command = Pmw.busycallback(self.showGraphDemo))
        start.pack(padx = 8, pady = 8)

        self.parent = parent

    def showGraphDemo(self):
        size = int(self.combo.get())
        demo = GraphDemo(self.parent, size = size)
        demo.focus()

######################################################################

# Create demo in root window for testing.
if __name__ == '__main__':
    root = tkinter.Tk()
    Pmw.initialise(root)
    root.title(title)

    exitButton = tkinter.Button(root, text = 'Exit', command = root.destroy)
    exitButton.pack(side = 'bottom')
    widget = Demo(root)
    root.mainloop()
