Merge pull request #5 from cuihantao/master
[pylon.git] / contrib / pylontk.py
blobedfbfaca21448142d635bf02f52bc942fed38386
1 # Copyright (C) 2010 Richard Lincoln
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """ Tk frontend for Pylon. """
17 import os
18 import logging
19 import platform
20 import webbrowser
22 #import Image # PIL
23 #import PIL.Image, PIL.ImageTk
24 #from StringIO import StringIO
26 #from matplotlib.backends.backend_tkagg \
27 # import FigureCanvasTkAgg, NavigationToolbar2TkAgg
29 #import matplotlib.pyplot as plt
30 #import matplotlib.image
31 #import matplotlib.figure
33 import networkx as nx
34 import pylab
35 import numpy
36 import scipy
38 from Tkinter import *
39 from tkFileDialog import askopenfilename, asksaveasfilename
40 import tkSimpleDialog
42 import pylon
44 from pylon import \
45 Case, DCPF, NewtonPF, FastDecoupledPF, OPF, UDOPF
47 from pylon.io import \
48 MATPOWERReader, MATPOWERWriter, ReSTWriter, PSSEReader, \
49 DotWriter, PickleReader, PickleWriter
51 from pylon.io.psat import PSATReader
52 from pylon.io.excel import CSVWriter
54 from pyreto.util import ReSTExperimentWriter
56 from pylon.io.common import BUS_ATTRS, BRANCH_ATTRS, GENERATOR_ATTRS
58 logger = logging.getLogger('pylon')
60 CASE_6_WW = os.path.dirname(pylon.__file__) + "/test/data/case6ww.pkl"
61 CASE_30 = os.path.dirname(pylon.__file__) + "/test/data/case30pwl.pkl"
64 class PylonTk(object):
65 def __init__(self, master, case=None):
66 self.root = master
68 frame = self.frame = Frame(master)
69 frame.pack(expand=YES, fill=BOTH)
71 self._init_menubar()
73 nameframe = Frame(frame)
75 n_name = self.case_name = StringVar()
76 n_name_label = Label(nameframe, textvariable=n_name, relief=SUNKEN,
77 anchor=W, padx=3, pady=3, justify=LEFT)
78 n_name_label.pack(side=LEFT, expand=YES, fill=X)
80 interactions = self.interactions = IntVar()
81 Spinbox(nameframe, from_=0, to=1000, width=5, bg="#40E0D0",
82 activebackground="#48D1CC",
83 textvariable=interactions).pack(fill=Y, side=RIGHT, anchor=E)
84 interactions.set(1)
86 go = self.go = Button(nameframe, text="Run",
87 bg="#FFD700", activebackground="#F4CE00",
88 anchor=E, command=self.on_run).pack(side=RIGHT, anchor=E, padx=1)
90 nameframe.pack(side=TOP, fill=X)
92 n_name_label.bind('<ButtonRelease>', self.on_properties)
94 self._init_buttonbar()
95 self._init_logframe()
97 statusbar = Frame(master)
98 status = self.status = StringVar()
99 status_label = Label(statusbar, textvariable=status, relief=SUNKEN,
100 anchor=W, padx=3, pady=3, justify=LEFT)
101 status_label.pack(side=LEFT, expand=YES, fill=X)
102 status.set("Ready.")
103 statusbar.pack(side=BOTTOM, fill=X)
105 self.status_status = True # Is the status displayed and not versions.
106 self._status = ""
107 status_label.bind('<Button>', self.on_status_down)
108 status_label.bind('<ButtonRelease>', self.on_status_leave)
109 status_label.bind('<Enter>', self.on_status_enter)
110 status_label.bind('<Leave>', self.on_status_leave)
112 if case is not None: # Initialise the singleton case.
113 self.set_case(case)
114 else:
115 self.on_new()
118 def _init_menubar(self):
119 menubar = Menu(self.root, name="menubar")
120 self.root.config(menu=menubar)
122 filemenu = Menu(menubar, tearoff=False)
123 filemenu.add_command(label="New", command=self.on_new)
124 filemenu.add_separator()
125 filemenu.add_command(label="Open...", command=self.on_open)
126 menubar.add_cascade(label="Case", menu=filemenu)
128 presetmenu = Menu(filemenu, tearoff=False)
129 filemenu.add_cascade(label='Preset', menu=presetmenu)
130 presetmenu.add_command(label="6 bus", command=self.on_6_bus)
131 presetmenu.add_command(label="30 bus", command=self.on_30_bus)
132 filemenu.add_separator()
134 filemenu.add_command(label="Save As...", command=self.on_save_as)
135 filemenu.add_separator()
137 importmenu = Menu(filemenu, tearoff=False)
138 filemenu.add_cascade(label='Import', menu=importmenu)
139 importmenu.add_command(label="PSS/E", command=self.on_psse)
140 importmenu.add_command(label="PSAT", command=self.on_psat)
141 importmenu.add_command(label="Pickle", command=self.on_unpickle)
143 exportmenu = Menu(filemenu, tearoff=False)
144 filemenu.add_cascade(label='Export', menu=exportmenu)
145 exportmenu.add_command(label="Excel", command=self.on_excel)
146 exportmenu.add_command(label="CSV", command=self.on_csv)
147 exportmenu.add_command(label="ReST", command=self.on_rest)
148 exportmenu.add_command(label="Pickle", command=self.on_pickle)
149 filemenu.add_separator()
151 filemenu.add_command(label="Properties", command=self.on_properties)
152 filemenu.add_separator()
154 filemenu.add_command(label="Exit", command=self.on_exit,
155 accelerator="Alt-X")
156 self.root.bind('<Alt-x>', self.on_exit)
159 viewmenu = Menu(menubar, tearoff=False)
160 menubar.add_cascade(label="Graph", menu=viewmenu)
161 viewmenu.add_command(label="Graph", command=self.on_graph)
163 pfmenu = Menu(menubar, tearoff=True)
164 menubar.add_cascade(label="Power Flow", menu=pfmenu)
165 pfmenu.add_command(label="DC PF", command=self.on_dcpf)
166 pfmenu.add_command(label="Newton's Method", command=self.on_newton)
167 pfmenu.add_command(label="Fast Decoupled", command=self.on_fd)
169 opfmenu = Menu(menubar, tearoff=True)
170 menubar.add_cascade(label="OPF", menu=opfmenu)
171 opfmenu.add_command(label="DC OPF", command=self.on_dcopf)
172 opfmenu.add_command(label="AC OPF", command=self.on_acopf)
173 opfmenu.add_command(label="DC (UD) OPF", command=self.on_duopf)
174 opfmenu.add_command(label="AC (UD) OPF", command=self.on_uopf)
176 mktmenu = Menu(menubar, tearoff=True)
177 menubar.add_cascade(label="RL", menu=mktmenu)
178 mktmenu.add_command(label="Run", command=self.on_run)
180 help = Menu(menubar, tearoff=False, name="help")
181 menubar.add_cascade(label="Help", menu=help)
182 help.add_command(label="About PylonTk", command=self.on_about)
183 help.add_command(label="Online Documentation", command=self.on_doc)
186 def _init_buttonbar(self):
187 buttonbar = Frame(self.frame, pady=1)
188 buttonbar.pack(side=LEFT, fill=Y, pady=1)
190 head = Label(buttonbar, text="Case", bg="#FFA07A")#"#FFD700")
191 head.pack(fill=X, padx=1)
193 Button(buttonbar, text="Summary",
194 command=self.on_summary).pack(fill=X)
195 Button(buttonbar, text="Bus",
196 command=self.on_bus_info).pack(fill=X)
197 Button(buttonbar, text="Branch",
198 command=self.on_branch_info).pack(fill=X)
199 Button(buttonbar, text="Generator",
200 command=self.on_generator_info).pack(fill=X)
202 self.writer_map = {"ReST": ReSTWriter,
203 "MATPOWER": MATPOWERWriter,
204 "CSV": CSVWriter,
205 "DOT": DotWriter}
207 writer_type = self.writer_type = StringVar(buttonbar)
208 writer_type.set("ReST") # default value
210 writer = OptionMenu(buttonbar, writer_type,
211 "ReST", "MATPOWER", "CSV", "DOT")
212 writer.pack(fill=X, pady=2)
214 sub = Label(buttonbar, text="RL", bg="#6495ED")
215 sub.pack(fill=X, padx=1, pady=2)
217 Button(buttonbar, text="State",
218 command=self.on_state_info).pack(fill=X)
219 Button(buttonbar, text="Action",
220 command=self.on_action_info).pack(fill=X)
221 Button(buttonbar, text="Reward",
222 command=self.on_reward_info).pack(fill=X)
224 foot = Label(buttonbar, text="Log", bg="#90EE90")
225 foot.pack(fill=X, padx=1, pady=2)
227 alwaysclear = self.alwaysclear = IntVar()
228 alwaysclear.set(0)
229 c = Checkbutton(buttonbar, text="Clear", variable=alwaysclear,
230 justify=LEFT, indicatoron=0, command=self.on_clear)
231 c.pack(fill=X)
233 Button(buttonbar, text="Save As",
234 command=self.on_save_log).pack(fill=X, pady=2)
238 # level = self.level = IntVar()
240 # debug = Radiobutton(buttonbar, text="DEBUG", variable=level,
241 # value=logging.DEBUG, command=self.on_level)
242 # debug.pack(anchor=W, pady=5)
243 # info = Radiobutton(buttonbar, text="INFO", variable=level,
244 # value=logging.INFO, command=self.on_level)
245 # info.pack(anchor=W)
246 # warn = Radiobutton(buttonbar, text="WARN", variable=level,
247 # value=logging.WARNING, command=self.on_level)
248 # warn.pack(anchor=W)
249 # error = Radiobutton(buttonbar, text="ERROR", variable=level,
250 # value=logging.ERROR, command=self.on_level)
251 # error.pack(anchor=W)
253 # level.set(logger.getEffectiveLevel())
256 def on_level(self):
257 pass
260 def _init_logframe(self):
261 self.ui_log = UILog(self.frame)
263 # sys.stdout = self.ui_log
264 # sys.stderr = self.ui_log
266 handler = logging.StreamHandler(self.ui_log)
267 logger.addHandler(handler)
268 # formatter = logging.Formatter("%(levelname)s: %(message)s")
269 # handler.setFormatter(formatter)
270 logger.setLevel(logging.INFO)
272 self.ui_log.level.set(logger.getEffectiveLevel())
275 def set_case(self, case):
276 self.case = case
277 # e = one_for_one(case)
278 # self.set_experiment(e)
280 self.root.title("PylonTk: %s" % self.case.name)
281 self.case_name.set("Current Case: %s" % self.case.name)
282 # self.ui_log.n_name.set(case.name)
285 def set_experiment(self, e):
286 self.e = e
289 def on_run(self):
290 number = self.interactions.get()
291 self.e.doInteractions(number)
294 def on_status_down(self, event=None):
295 self._status = self.status.get()
297 self.status_status = False
299 python_version = platform.python_version()
300 numpy_version = numpy.version.version
301 scipy_version = scipy.version.version
302 tk_version = TkVersion
304 self.status.set("Python %s, NumPy %s, SciPy %s, Tk %s" % \
305 (python_version, numpy_version, scipy_version, tk_version))
308 def on_status_enter(self, event=None):
309 self._status = self.status.get()
312 def on_status_leave(self, event=None):
313 if not self.status_status:
314 self.status.set(self._status)
315 self.status_status = True
318 def on_new(self):
319 c = Case()
320 self.set_case(c)
323 def on_open(self):
324 ftypes = [("MATPOWER file", ".m"), ("All files", "*")]
325 filename = askopenfilename(filetypes=ftypes, defaultextension='.m')
326 if filename:
327 self.set_case(MATPOWERReader().read(filename))
330 def on_6_bus(self):
331 self.set_case(PickleReader().read(CASE_6_WW))
334 def on_30_bus(self):
335 self.set_case(PickleReader().read(CASE_30))
338 def on_save_as(self):
339 filename = asksaveasfilename(filetypes=[("MATPOWER file", ".m")])
340 if filename:
341 MATPOWERWriter().write(self.case, filename)
344 def on_properties(self, event=None):
345 CaseProperties(self.root, self.case)
347 # Import handlers ---------------------------------------------------------
349 def on_unpickle(self):
350 ftypes = [("Pickle file", ".pkl"), ("All files", "*")]
351 filename = askopenfilename(filetypes=ftypes, defaultextension='.pkl')
352 if filename:
353 self.set_case(PickleReader().read(filename))
356 def on_psse(self):
357 ftypes = [("PSS/E file", ".raw"), ("All files", "*")]
358 filename = askopenfilename(filetypes=ftypes, defaultextension='.raw')
359 if filename:
360 self.set_case(PSSEReader().read(filename))
363 def on_psat(self):
364 ftypes = [("PSAT file", ".m"), ("All files", "*")]
365 filename = askopenfilename(filetypes=ftypes, defaultextension='.m')
366 if filename:
367 self.set_case(PSATReader().read(filename))
369 # Export handlers ---------------------------------------------------------
371 def on_pickle(self):
372 filename = asksaveasfilename(filetypes=[("Pickle file", ".pkl")])
373 if filename:
374 PickleWriter(self.case).write(filename)
377 def on_excel(self):
378 from pylon.io.excel import ExcelWriter
379 filename = asksaveasfilename(filetypes=[("Excel file", ".xls")])
380 if filename:
381 ExcelWriter(self.case).write(filename)
384 def on_csv(self):
385 filename = asksaveasfilename(filetypes=[("CSV file", ".csv")])
386 if filename:
387 CSVWriter(self.case).write(filename)
390 def on_rest(self):
391 ftypes = [("ReStructuredText file", ".rst")]
392 filename = asksaveasfilename(filetypes=ftypes)
393 if filename:
394 ReSTWriter(self.case).write(filename)
396 # View --------------------------------------------------------------------
398 def on_graph(self):
399 # GraphView(self.root, self.case, self.e)
400 graph = nx.Graph()
401 for edge in self.case.branches:
402 graph.add_edge(edge.from_bus._i, edge.to_bus._i)
403 for i, bus in enumerate(self.case.buses):
404 if bus.p_demand > 0.0:
405 graph.add_edge(i, "l" + str(i))
406 for g in self.case.generators:
407 graph.add_edge(g.bus._i, g.name)
409 pylab.figure(1,figsize=(8,8))
410 pos = nx.graphviz_layout(graph, prog="dot")
411 nx.draw(graph, pos)#, node_size=40)
412 pylab.show()
414 # UI Log ------------------------------------------------------------------
416 def on_clear(self):
417 if self.alwaysclear.get():
418 self.ui_log.clear()
421 def on_summary(self):
422 if self.alwaysclear.get():
423 self.ui_log.clear()
424 writer_klass = self.writer_map[self.writer_type.get()]
425 writer_klass(self.case).write_case_data(self.ui_log)
428 def on_bus_info(self):
429 if self.alwaysclear.get():
430 self.ui_log.clear()
431 writer_klass = self.writer_map[self.writer_type.get()]
432 writer_klass(self.case).write_bus_data(self.ui_log)
435 def on_branch_info(self):
436 if self.alwaysclear.get():
437 self.ui_log.clear()
438 writer_klass = self.writer_map[self.writer_type.get()]
439 writer_klass(self.case).write_branch_data(self.ui_log)
442 def on_generator_info(self):
443 if self.alwaysclear.get():
444 self.ui_log.clear()
445 writer_klass = self.writer_map[self.writer_type.get()]
446 writer_klass(self.case).write_generator_data(self.ui_log)
449 def on_state_info(self):
450 if self.alwaysclear.get():
451 self.ui_log.clear()
452 ReSTExperimentWriter(self.e).write_state_data(self.ui_log)
455 def on_action_info(self):
456 if self.alwaysclear.get():
457 self.ui_log.clear()
458 ReSTExperimentWriter(self.e).write_action_data(self.ui_log)
461 def on_reward_info(self):
462 if self.alwaysclear.get():
463 self.ui_log.clear()
464 ReSTExperimentWriter(self.e).write_reward_data(self.ui_log)
467 def on_save_log(self):
468 ftypes = [("Log file", ".log"),
469 ("Text file", ".txt"),
470 ("All files", "*")]
471 filename = asksaveasfilename(filetypes=ftypes)
472 if filename:
473 log = self.ui_log.log
475 file = None
476 try:
477 file = open(filename, "wb")
478 file.write(log.get(1.0, END))
479 except:
480 logger.error("Error writing to '%s'." % filename)
481 finally:
482 if file is not None:
483 file.close()
485 # -------------------------------------------------------------------------
487 def on_dcpf(self):
488 DCPF(self.case).solve()
491 def on_newton(self):
492 NewtonPF(self.case).solve()
495 def on_fd(self):
496 FastDecoupledPF(self.case).solve()
499 def on_dcopf(self):
500 OPF(self.case, dc=True, opt={"verbose": True}).solve()
503 def on_acopf(self):
504 OPF(self.case, dc=False, opt={"verbose": True}).solve()
507 def on_duopf(self):
508 UDOPF(self.case, dc=True).solve()
511 def on_uopf(self):
512 UDOPF(self.case, dc=False).solve()
515 def on_exit(self, event=None):
516 self.root.destroy()
518 # -------------------------------------------------------------------------
520 def on_about(self):
521 AboutDialog(self.root)
523 def on_doc(self):
524 webbrowser.open("http://pylon.eee.strath.ac.uk/")
527 class CaseProperties(tkSimpleDialog.Dialog):
528 """ A dialog for editing the properties of a case.
531 def __init__(self, parent, case, title="Case Properties"):
532 """ Initialises the font dialog.
534 self.case = case
535 tkSimpleDialog.Dialog.__init__(self, parent, title)
538 def body(self, frame):
539 """ Creates the dialog body. Returns the widget that should have
540 initial focus.
542 master = Frame(self)
543 master.pack(padx=5, pady=0, expand=1, fill=BOTH)
545 title = Label(master, text="Buses")
546 title.pack(side=TOP)
548 bus_lb = self.bus_lb = Listbox(master, selectmode=SINGLE, width=10)
549 bus_lb.pack(side=LEFT)
551 for bus in self.case.buses:
552 bus_lb.insert(END, bus.name)
554 bus_lb.bind("<<ListboxSelect>>", self.on_bus)
556 self.bus_params = BusProperties(master)
558 return bus_lb # Given initial focus.
561 def on_bus(self, event=None):
562 bus = self.case.buses[int(self.bus_lb.curselection()[0])]
564 self.excluded = ["zone", "v_base", "v_magnitude", "v_angle", "g_shunt",
565 "b_shunt", "zone"]
566 for attr in [a for a in BUS_ATTRS if a not in self.excluded]:
567 value = getattr(bus, attr)
568 getattr(self.bus_params, attr).set(value)
571 def validate(self):
572 ''' Validate the data. This method is called automatically to validate
573 the data before the dialog is destroyed.
575 return 1
578 def apply(self):
579 ''' Process the data. This method is called automatically to process
580 the data, *after* the dialog is destroyed.
582 bus = self.case.buses[int(self.bus_lb.curselection()[0])]
584 for attr in [a for a in BUS_ATTRS if a not in self.excluded+['mode']]:
585 value = getattr(self.bus_params, attr).get()
586 setattr(bus, attr, value)
589 class BusProperties(object):
590 def __init__(self, master):
591 self.master = master
592 frame = self.frame = Frame(master)
593 frame.pack(side=RIGHT)
595 name = self.name = StringVar()
596 Label(frame, text="Name:").grid(row=0, sticky=W)
597 nameentry = Entry(frame, textvariable=name)
598 nameentry.grid(row=0, column=1)
600 bustype = self.type = StringVar()
601 Label(frame, text="Type:").grid(row=1, sticky=W)
602 Label(frame, textvariable=bustype).grid(row=1, column=1, sticky=W)
603 # nameentry.grid(row=1, column=1)
604 bustype.set("PV")
606 # slack = self.slack = IntVar()
607 # Checkbutton(frame, text="Slack", variable=slack, justify=LEFT,
608 # # command=self.on_clear
609 # ).grid(row=2, columnspan=2)
611 v_max = self.v_max = StringVar()
612 Label(frame, text="Vmax:").grid(row=3, sticky=W)
613 vmaxentry = Entry(frame, textvariable=v_max)
614 vmaxentry.grid(row=3, column=1)
616 v_min = self.v_min = StringVar()
617 Label(frame, text="Vmin:").grid(row=4, sticky=W)
618 vminentry = Entry(frame, textvariable=v_min)
619 vminentry.grid(row=4, column=1)
622 #class GraphView(tkSimpleDialog.Dialog):
623 # """ A dialog for graph viewing.
624 # """
626 # def __init__(self, parent, case, experiment):
627 # """ Initialises the font dialog.
628 # """
629 # self.case = case
630 # self.e = experiment
632 # tkSimpleDialog.Dialog.__init__(self, parent, title="Graph")
635 # def draw_graph(self):
636 # """ Creates a representation of the graph and draws it on the canvas.
637 # """
638 # case = self.case
639 # prog = self.prog.get()
640 # format = self.format.get()
642 # fig = self.fig
644 # sbplt = fig.add_subplot(111)
646 # dotdata = StringIO()
647 # DotWriter().write(case, dotdata)
648 # dotdata.seek(0) # rewind
650 # imagedata = create_graph(dotdata.getvalue(), prog, format)
652 # # Write the image data to a temporary file.
653 # suffix = ".%s" % self.format.get()
654 # # Matplotlib features native PNG support with '.png' suffix.
655 # tmp_fd, tmp_name = tempfile.mkstemp(suffix=suffix)
656 # os.close(tmp_fd)
657 # imagefd = file(tmp_name, "w+b")
658 # imagefd.write(imagedata) # DOT language.
659 # imagefd.close()
661 # im = plt.imread(tmp_name)
663 # fig = matplotlib.figure.Figure(figsize=(6, 6), dpi=100)
664 # ax = plt.axes([0, 0, 1, 1], frameon=False)
665 # ax.set_axis_off()
666 # img = plt.imshow(im)
667 # plt.show()
669 # # Remove the temporary file.
670 # os.remove(tmp_name)
672 ## stream = StringIO()
673 ## stream.write(imagedata)
674 ## stream.seek(0) # rewind
675 ## pil_image = PIL.Image.open(stream) # Either a string or a file object.
677 ## f = Figure(figsize=figsize)#, dpi=100)
678 ## figure(figsize=figsize)
680 ## ax = axes([0, 0, 1, 1], frameon=False)
681 ## ax.set_axis_off()
683 ## im = imshow(graphimage, origin='lower')
684 ## im = matplotlib.image.frombuffer(graph_io)
686 ## im = matplotlib.image.pil_to_array(pil_image)
688 ## dpi = rcParams['figure.dpi']
689 ## figsize = im.size[0] / dpi, im.size[1] / dpi
691 # fig.figimage(im, 10, 10)
694 ## import base64
695 ## imagedata = base64.encodestring(imagedata)
697 ## if imagedata is not None:
698 ## stream = StringIO()
699 ## stream.write(imagedata)
700 ## stream.seek(0) # rewind
702 ### image = PIL.Image.open("/tmp/logo.png")
703 ### photo = PIL.ImageTk.PhotoImage(image)
705 ### imagedata = PIL.ImageTk.PhotoImage(data=imagedata)
707 ### pil_image = PIL.Image.open(stream)
708 ### photo = PIL.ImageTk.PhotoImage(pil_image)
710 ## photo = self.photo = PhotoImage(data=imagedata)
712 ## self.canvas.create_image(50, 50, image=photo, anchor=NW)
714 # # tkSimpleDialog.Dialog interface -----------------------------------------
716 # def body(self, frame):
717 # """ Creates the dialog body. Returns the widget that should have
718 # initial focus.
719 # """
720 # master = Frame(self)
721 ## master.pack(padx=5, pady=0, expand=1, fill=BOTH)
723 # buttonbar = Frame(master, pady=1)
724 # buttonbar.pack(side=LEFT, fill=Y, pady=1)
726 # head = Label(buttonbar, text="Graphviz", bg="#FFA07A")
727 # head.pack(fill=X, padx=1, pady=1)
729 # refresh = Button(buttonbar, text="Refresh",
730 # command=self.draw_graph).pack(fill=X)
732 # # Graphviz layout program.
733 # prog = self.prog = StringVar(buttonbar)
734 # prog.set("dot") # default value
735 # OptionMenu(buttonbar, prog, "dot", "circo", "neato", "twopi",
736 # "fdp").pack(fill=X, pady=2)
738 # # Image format.
739 # format = self.format = StringVar(buttonbar)
740 # format.set("png") # default value
741 # OptionMenu(buttonbar, format, "png", "jpg", "gif").pack(fill=X, pady=2)
743 # # Graph canvas frame.
744 # fig = self.fig = matplotlib.figure.Figure(figsize=(6, 4), dpi=100)
746 ## from numpy import arange, sin, pi
747 ## a = fig.add_subplot(111)
748 ## t = arange(0.0,3.0,0.01)
749 ## s = sin(2*pi*t)
750 ## a.plot(t,s)
753 # self.draw_graph()
755 # canvas = FigureCanvasTkAgg(fig, master=master)
756 # canvas.show()
757 # canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
759 # toolbar = NavigationToolbar2TkAgg(canvas, master)
760 # toolbar.update()
761 # canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)
765 ## drawframe = Frame(master)
767 ## drawframe.grid_rowconfigure(0, weight=1)
768 ## drawframe.grid_columnconfigure(0, weight=1)
770 ## xscrollbar = Scrollbar(drawframe, orient=HORIZONTAL)
771 ## xscrollbar.grid(row=1, column=0, sticky=E+W)
773 ## yscrollbar = Scrollbar(drawframe)
774 ## yscrollbar.grid(row=0, column=1, sticky=N+S)
776 ## canvas = self.canvas = Canvas(drawframe, width=600, height=400,
777 ## bg='white',
778 ## xscrollcommand=xscrollbar.set,
779 ## yscrollcommand=yscrollbar.set)
780 ### canvas.config(scrollregion=canvas.bbox(ALL))
781 ## canvas.config(scrollregion=(-800, -600, 1600, 1200))
783 ## canvas.grid(row=0, column=0, sticky=N+S+E+W)
785 ## xscrollbar.config(command=canvas.xview)
786 ## yscrollbar.config(command=canvas.yview)
789 ## xscrollbar.pack(side=BOTTOM, fill=X)
790 ## yscrollbar.pack(side=RIGHT, fill=Y)
791 ## canvas.pack(expand=YES, fill=BOTH)
793 ## drawframe.pack(expand=YES, fill=BOTH)
795 ## self.draw_graph()
797 # return refresh # Given initial focus.
800 # def buttonbox(self):
801 # ''' Adds a button box.
802 # '''
803 ## box = Frame(self)
805 ## w = Button(box, text="Cancel", width=10, command=self.cancel)
806 ## w.pack(side=LEFT, padx=5, pady=5)
808 ## w = Button(box, text="Select", width=10, command=self.ok,
809 ## default=ACTIVE)
810 ## w.pack(side=LEFT, padx=5, pady=5)
812 ## self.bind("<Return>", self.ok)
813 ## self.bind("<Escape>", self.cancel)
815 ## box.pack(side=RIGHT, padx=5, pady=5, anchor=S)
818 # def validate(self):
819 # ''' Validate the data. This method is called automatically to validate
820 # the data before the dialog is destroyed.
821 # '''
822 # return 1
825 # def apply(self):
826 # ''' Process the data. This method is called automatically to process
827 # the data, *after* the dialog is destroyed.
828 # '''
829 # pass
832 class UILog(object):
834 def __init__(self, master):
835 self.master = master
837 self._init_text()
838 # self._init_levels()
841 def _init_text(self):
842 frame = Frame(self.master, bd=1, relief=SUNKEN)
843 frame.pack(padx=2, pady=2)
845 orange = None#"#FFD700"
846 darker = "#D1B100"
848 headframe = Frame(frame)
849 head = Label(headframe, text="Pylon Log:", bg=orange,
850 justify=LEFT, anchor=W, padx=3, pady=1)
851 head.pack(side=LEFT, fill=X, expand=YES)
852 headframe.pack(side=TOP, fill=X, expand=YES)
855 radioframe = Frame(headframe, bg=orange)
856 radioframe.pack(side=RIGHT, padx=5)
858 # Label(radioframe, text="Log Level:", bg=orange).grid(row=0)
860 level = self.level = IntVar()
862 debug = Radiobutton(radioframe, text="Debug", variable=level,
863 value=logging.DEBUG, command=self.on_level,
864 bg=orange, relief=FLAT,
865 offrelief=FLAT)
866 # debug.pack(side=RIGHT, anchor=E)
867 debug.grid(row=0, column=1)
868 info = Radiobutton(radioframe, text="Info", variable=level,
869 value=logging.INFO, command=self.on_level,
870 bg=orange, relief=FLAT, offrelief=FLAT)
871 # info.pack(side=RIGHT, anchor=E)
872 info.grid(row=0, column=2)
873 warn = Radiobutton(radioframe, text="Warning", variable=level,
874 value=logging.WARNING, command=self.on_level,
875 bg=orange, relief=FLAT, offrelief=FLAT)
876 # warn.pack(side=RIGHT, anchor=E)
877 warn.grid(row=0, column=3)
878 error = Radiobutton(radioframe, text="Error", variable=level,
879 value=logging.ERROR, command=self.on_level,
880 bg=orange, relief=FLAT, offrelief=FLAT)
881 # error.pack(side=RIGHT, anchor=E)
882 error.grid(row=0, column=4)
884 level.set(logger.getEffectiveLevel())
888 logframe = Frame(frame)
890 logframe.grid_rowconfigure(0, weight=1)
891 logframe.grid_columnconfigure(0, weight=1)
893 xscrollbar = Scrollbar(logframe, orient=HORIZONTAL)
894 xscrollbar.grid(row=1, column=0, sticky=E+W)
896 yscrollbar = Scrollbar(logframe)
897 yscrollbar.grid(row=0, column=1, sticky=N+S)
899 log = self.log = Text(logframe, wrap=NONE, background="white",
900 xscrollcommand=xscrollbar.set,
901 yscrollcommand=yscrollbar.set)
903 # log.insert(END, "Pylon " + str(pylon.__version__))
905 log.grid(row=0, column=0, sticky=N+S+E+W)
907 xscrollbar.config(command=log.xview)
908 yscrollbar.config(command=log.yview)
910 logframe.pack(expand=YES, fill=BOTH)
913 def _init_levels(self):
914 loglevels = Frame(self.master)
915 loglevels.pack(fill=X)
917 level = self.level = IntVar()
919 debug = Radiobutton(loglevels, text="DEBUG", variable=level,
920 value=logging.DEBUG, command=self.on_level)
921 debug.pack(side=LEFT, anchor=E)
922 info = Radiobutton(loglevels, text="INFO", variable=level,
923 value=logging.INFO, command=self.on_level)
924 info.pack(side=LEFT, anchor=E)
925 warn = Radiobutton(loglevels, text="WARN", variable=level,
926 value=logging.WARNING, command=self.on_level)
927 warn.pack(side=LEFT, anchor=E)
928 error = Radiobutton(loglevels, text="ERROR", variable=level,
929 value=logging.ERROR, command=self.on_level)
930 error.pack(side=LEFT, anchor=E)
932 level.set(logger.getEffectiveLevel())
934 # n_name = self.case_name = StringVar()
935 # Label(loglevels, textvariable=n_name).pack(side=RIGHT, padx=5)
938 def write(self, buf):
939 self.log.insert(END, buf)
940 self.log.see(END)
943 def flush(self):
944 pass
947 def clear(self):
948 self.log.delete(1.0, END)
951 def on_level(self):
952 logger.setLevel(self.level.get())
955 class AboutDialog(object):
957 def __init__(self, parent):
958 top = self.top = Toplevel(parent)
960 l = self.l = Label(top, text="PylonTk")
961 l.pack(padx=100, pady=15)
963 license = Label(top, text="Apache License, Version 2.0")
964 license.pack(pady=5)
966 b = Button(top, text="OK", command=self.ok)
967 b.pack(pady=10)
970 def ok(self):
971 self.top.destroy()
974 def main(case=None):
975 root = Tk()
976 root.minsize(300, 300)
977 # root.geometry("666x666")
978 root.title('PylonTk')
979 app = PylonTk(root, case)
980 root.mainloop()
983 if __name__ == "__main__":
984 # logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,
985 # format="%(levelname)s: %(message)s")
987 # logger.addHandler(logging.StreamHandler(sys.stdout))
988 # logger.setLevel(logging.DEBUG)
990 main()