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. """
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
39 from tkFileDialog
import askopenfilename
, asksaveasfilename
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):
68 frame
= self
.frame
= Frame(master
)
69 frame
.pack(expand
=YES
, fill
=BOTH
)
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
)
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
()
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
)
103 statusbar
.pack(side
=BOTTOM
, fill
=X
)
105 self
.status_status
= True # Is the status displayed and not versions.
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.
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
,
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
,
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()
229 c
= Checkbutton(buttonbar
, text
="Clear", variable
=alwaysclear
,
230 justify
=LEFT
, indicatoron
=0, command
=self
.on_clear
)
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())
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
):
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
):
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
324 ftypes
= [("MATPOWER file", ".m"), ("All files", "*")]
325 filename
= askopenfilename(filetypes
=ftypes
, defaultextension
='.m')
327 self
.set_case(MATPOWERReader().read(filename
))
331 self
.set_case(PickleReader().read(CASE_6_WW
))
335 self
.set_case(PickleReader().read(CASE_30
))
338 def on_save_as(self
):
339 filename
= asksaveasfilename(filetypes
=[("MATPOWER file", ".m")])
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')
353 self
.set_case(PickleReader().read(filename
))
357 ftypes
= [("PSS/E file", ".raw"), ("All files", "*")]
358 filename
= askopenfilename(filetypes
=ftypes
, defaultextension
='.raw')
360 self
.set_case(PSSEReader().read(filename
))
364 ftypes
= [("PSAT file", ".m"), ("All files", "*")]
365 filename
= askopenfilename(filetypes
=ftypes
, defaultextension
='.m')
367 self
.set_case(PSATReader().read(filename
))
369 # Export handlers ---------------------------------------------------------
372 filename
= asksaveasfilename(filetypes
=[("Pickle file", ".pkl")])
374 PickleWriter(self
.case
).write(filename
)
378 from pylon
.io
.excel
import ExcelWriter
379 filename
= asksaveasfilename(filetypes
=[("Excel file", ".xls")])
381 ExcelWriter(self
.case
).write(filename
)
385 filename
= asksaveasfilename(filetypes
=[("CSV file", ".csv")])
387 CSVWriter(self
.case
).write(filename
)
391 ftypes
= [("ReStructuredText file", ".rst")]
392 filename
= asksaveasfilename(filetypes
=ftypes
)
394 ReSTWriter(self
.case
).write(filename
)
396 # View --------------------------------------------------------------------
399 # GraphView(self.root, self.case, self.e)
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)
414 # UI Log ------------------------------------------------------------------
417 if self
.alwaysclear
.get():
421 def on_summary(self
):
422 if self
.alwaysclear
.get():
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():
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():
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():
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():
452 ReSTExperimentWriter(self
.e
).write_state_data(self
.ui_log
)
455 def on_action_info(self
):
456 if self
.alwaysclear
.get():
458 ReSTExperimentWriter(self
.e
).write_action_data(self
.ui_log
)
461 def on_reward_info(self
):
462 if self
.alwaysclear
.get():
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"),
471 filename
= asksaveasfilename(filetypes
=ftypes
)
473 log
= self
.ui_log
.log
477 file = open(filename
, "wb")
478 file.write(log
.get(1.0, END
))
480 logger
.error("Error writing to '%s'." % filename
)
485 # -------------------------------------------------------------------------
488 DCPF(self
.case
).solve()
492 NewtonPF(self
.case
).solve()
496 FastDecoupledPF(self
.case
).solve()
500 OPF(self
.case
, dc
=True, opt
={"verbose": True}).solve()
504 OPF(self
.case
, dc
=False, opt
={"verbose": True}).solve()
508 UDOPF(self
.case
, dc
=True).solve()
512 UDOPF(self
.case
, dc
=False).solve()
515 def on_exit(self
, event
=None):
518 # -------------------------------------------------------------------------
521 AboutDialog(self
.root
)
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.
535 tkSimpleDialog
.Dialog
.__init
__(self
, parent
, title
)
538 def body(self
, frame
):
539 """ Creates the dialog body. Returns the widget that should have
543 master
.pack(padx
=5, pady
=0, expand
=1, fill
=BOTH
)
545 title
= Label(master
, text
="Buses")
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",
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
)
572 ''' Validate the data. This method is called automatically to validate
573 the data before the dialog is destroyed.
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
):
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)
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.
626 # def __init__(self, parent, case, experiment):
627 # """ Initialises the font dialog.
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.
639 # prog = self.prog.get()
640 # format = self.format.get()
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)
657 # imagefd = file(tmp_name, "w+b")
658 # imagefd.write(imagedata) # DOT language.
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)
666 # img = plt.imshow(im)
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)
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)
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
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)
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)
755 # canvas = FigureCanvasTkAgg(fig, master=master)
757 # canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
759 # toolbar = NavigationToolbar2TkAgg(canvas, master)
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,
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)
797 # return refresh # Given initial focus.
800 # def buttonbox(self):
801 # ''' Adds a button box.
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,
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.
826 # ''' Process the data. This method is called automatically to process
827 # the data, *after* the dialog is destroyed.
834 def __init__(self
, master
):
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"
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
,
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
)
948 self
.log
.delete(1.0, END
)
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")
966 b
= Button(top
, text
="OK", command
=self
.ok
)
976 root
.minsize(300, 300)
977 # root.geometry("666x666")
978 root
.title('PylonTk')
979 app
= PylonTk(root
, case
)
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)