Commit 93def510 authored by Alessandro dos Santos Ferreira's avatar Alessandro dos Santos Ferreira
Browse files

Pynovisao - Documentando Tk interface

parent 40c35591
......@@ -13,20 +13,32 @@ import traceback
from abc import ABCMeta, abstractmethod
class Interface(object):
"""Abstract class for graphical interface."""
__metaclass__ = ABCMeta
@abstractmethod
def show(self):
"""Open a rendered GUI.
Implement this method to extend this class with a new classifier algorithm.
"""
pass
class InterfaceException(Exception):
"""Customized class for handle exceptions."""
DEBUG = True
@staticmethod
def format_exception(message = None):
"""Format a exception message.
Returns
----------
fmt_message : string
A formatted exception message.
"""
if message is not None:
return "Unexpected error:\n%s" % message.replace('%', '%%')
elif InterfaceException.DEBUG == True:
......
......@@ -21,9 +21,17 @@ from matplotlib import pylab as plt
class Image(object):
"""Class to manipulate structured graphics interface."""
def __init__(self, parent):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
"""
self.parent = parent
self._im = None
......@@ -37,6 +45,8 @@ class Image(object):
def toggle_axes(self):
"""Toogle the axes of image.
"""
self._axes_visible = not self._axes_visible
if self._canvas is not None:
......@@ -47,12 +57,23 @@ class Image(object):
self._canvas.draw()
def toggle_toolbar(self):
"""Toogle the matplotlib image toolbar.
"""
self._toolbar_visible = not self._toolbar_visible
self._show_toolbar() if self._toolbar_visible else self._hide_toolbar()
def render(self, image, onclick = None):
"""Render a image in window GUI.
Parameters
----------
image : opencv 3-channel color image
OpenCV Image.
onclick : function, optional, default = None
Callback to be executed on image click.
"""
self.parent.image = image
self._fig = plt.figure(facecolor='white', edgecolor='black', linewidth=1)
......@@ -76,6 +97,13 @@ class Image(object):
def refresh(self, image = None):
"""Refresh the image content.
Parameters
----------
image : opencv 3-channel color image, optional, default = None
OpenCV Image.
"""
if self._canvas is not None:
if image is not None:
self.parent.image = image
......@@ -85,6 +113,8 @@ class Image(object):
self._canvas.draw()
def close(self):
"""Close the image.
"""
if self._canvas is not None:
self.parent.image = None
#self._im.set_data(np.zeros((0,0,3), float))
......@@ -113,6 +143,8 @@ class Image(object):
def _show_toolbar(self):
"""Show the matplotlib image toolbar.
"""
if self._toolbar is None and self._canvas is not None:
self._toolbar = NavigationToolbar2TkAgg(self._canvas, self.parent)
self._toolbar.configure(background='white', borderwidth=0)
......@@ -122,6 +154,8 @@ class Image(object):
self._toolbar.pack(side=Tk.TOP, fill=Tk.X, expand=False)
def _hide_toolbar(self):
"""Hide the matplotlib image toolbar.
"""
if self._toolbar is not None:
self._toolbar.pack_forget()
self._toolbar.destroy()
......
......@@ -11,13 +11,36 @@
from util.config import Config
class TkConfig(Config):
"""Customized class used to store tk configs."""
def __init__(self, label, value, c_type, tk_entry = None, hidden = False, meta = None):
"""Constructor.
Parameters
----------
label : string
Label of option configuration.
value : string
Value of option configuration
c_type : any, optional, default = str
Type of data of option configuration.
tk_entry : Tk Entry, optional, default = None
Tk Entry of option configuration.
hidden : boolean, optional, default = False
If false is not visible to end user, on configuration step.
"""
super(self.__class__, self).__init__(label, value, c_type, hidden, meta)
self.tk_entry = tk_entry
def get_entry_val(self):
"""Converts the value to pre-defined type.
Returns
-------
value : any
The value converted to pre-defined type c_type.
"""
if self.tk_entry is not None:
self.value = self.tk_entry.get()
return self.get_cast_val()
......@@ -19,9 +19,20 @@ from collections import OrderedDict
from tk_config import TkConfig
class SimpleDialog(Tk.Toplevel):
"""Basic Dialogue class."""
def __init__(self, parent, title = None, command_ok = None):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
title : string, optional, default = None
Dialog title.
command_ok : function, optional, default = None
Unused argument.
"""
self.parent = parent
Tk.Toplevel.__init__(self, self.parent, padx=10, pady=10)
......@@ -39,9 +50,22 @@ class SimpleDialog(Tk.Toplevel):
class ConfigDialog(SimpleDialog):
"""Config Dialogue class."""
def __init__(self, parent, title = None, configs = None, command_ok = None):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
title : string, optional, default = None
Dialog title.
configs : dictionary, optional, default = None
Dictionary of configs to be added to dialogue.
callback: function, optional, default = None
Method to be excecuted after Ok or enter click.
"""
SimpleDialog.__init__(self, parent, title, command_ok)
self._configs = None
......@@ -50,6 +74,15 @@ class ConfigDialog(SimpleDialog):
def add_configs(self, configs, command_ok):
"""Add config options to dialogue.
Parameters
----------
configs : dictionary, optional, default = None
Dictionary of configs to be added to dialogue.
callback: function, optional, default = None
Method to be excecuted after Ok or enter click.
"""
row = 0
self._configs = OrderedDict()
for key in configs:
......@@ -79,16 +112,34 @@ class ConfigDialog(SimpleDialog):
def update_and_validate_configs(self):
"""Update and validate the value of all config options.
"""
for key in self._configs:
self._configs[key].value = self._configs[key].get_entry_val()
def get_configs(self):
"""Return all config options.
"""
return self._configs
class ChooseOneDialog(SimpleDialog):
"""Choose One Dialogue class."""
def __init__(self, parent, title = None, configs = None, command_ok = None):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
title : string, optional, default = None
Dialog title.
configs : dictionary, optional, default = None
Dictionary of configs to be added to dialogue.
callback: function, optional, default = None
Method to be excecuted after Ok or enter click.
"""
SimpleDialog.__init__(self, parent, title, command_ok)
self.v = Tk.StringVar()
......@@ -99,6 +150,15 @@ class ChooseOneDialog(SimpleDialog):
def add_configs(self, configs, command_ok):
"""Add config options to dialogue.
Parameters
----------
configs : dictionary, optional, default = None
Dictionary of configs to be added to dialogue.
callback: function, optional, default = None
Method to be excecuted after Ok or enter click.
"""
row = 0
self._configs = OrderedDict()
for key in configs:
......@@ -128,17 +188,35 @@ class ChooseOneDialog(SimpleDialog):
self.bind("<Escape>", lambda *_: self.destroy())
def update_and_validate_configs(self):
"""Update and validate the value of all config options.
"""
for key in self._configs:
self._configs[key].value = False
self._configs[self.v.get()].value = True
def get_configs(self):
"""Return all config options.
"""
return self._configs
class SelectDialog(SimpleDialog):
"""Select One Dialogue class."""
def __init__(self, parent, title = None, configs = None, command_ok = None):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
title : string, optional, default = None
Dialog title.
configs : dictionary, optional, default = None
Dictionary of configs to be added to dialogue.
callback: function, optional, default = None
Method to be excecuted after Ok or enter click.
"""
SimpleDialog.__init__(self, parent, title, command_ok)
self._configs = None
......@@ -147,6 +225,15 @@ class SelectDialog(SimpleDialog):
def add_configs(self, configs, command_ok):
"""Add config options to dialogue.
Parameters
----------
configs : dictionary, optional, default = None
Dictionary of configs to be added to dialogue.
callback: function, optional, default = None
Method to be excecuted after Ok or enter click.
"""
row = 0
self._configs = OrderedDict()
for key in configs:
......@@ -178,8 +265,12 @@ class SelectDialog(SimpleDialog):
self.bind("<Escape>", lambda *_: self.destroy())
def update_and_validate_configs(self):
"""Update and validate the value of all config options.
"""
for key in self._configs:
self._configs[key].value = self._configs[key].get_entry_val()
def get_configs(self):
"""Return all config options.
"""
return self._configs
......@@ -18,9 +18,22 @@ from tk_utils import Utils
from util.x11_colors import X11Colors
class CustomGrid(Tk.Frame):
"""Provide a customized grid."""
def __init__(self, parent, width = 0, height = 0, bg='white'):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
width : integer, optional, default = 0
Width of grid.
height : integer, optional, default = 0
Height of grid.
bg : string, optional, default = 'white'
Background color of grid. X11Color.
"""
self.parent = parent
self.v = Tk.IntVar()
......@@ -30,12 +43,52 @@ class CustomGrid(Tk.Frame):
def add_cell_label(self, text, row, column, width=0, height=0, bg='white', fg="black"):
"""Add a cell with a label in the grid.
Parameters
----------
text : string
Content of cell.
row : integer
Row with the grid. Initial position is 0.
column : integer
Column with the grid. Initial position is 0.
width : integer, optional, default = 0
Width of cell.
height : integer, optional, default = 0
Height of cell.
bg : string, optional, default = 'white'
Background color of cell. X11Color.
fg : string, optional, default = 'white'
Foreground color of cell. X11Color.
"""
Tk.Label(self, text=text, width=width, height=height, bg=bg, fg=fg, padx=4, pady=4).grid(row=row, column=column)
def add_cell_button_color(self, text, row, column, width=0, height=0, bg='white', fg="black", command=None, command_args=None):
"""Add a cell with a button in the grid.
Parameters
----------
text : string
Label of button.
row : integer
Row with the grid. Initial position is 0.
column : integer
Column with the grid. Initial position is 0.
width : integer, optional, default = 0
Width of cell.
height : integer, optional, default = 0
Height of cell.
bg : string, optional, default = 'white'
Background color of cell. X11Color.
fg : string, optional, default = 'white'
Foreground color of cell. X11Color.
command : function, optional, default = None
Method to be executed on click button.
command_args : integer, optional, default = None
Arguments of method executed on click button.
"""
bg_color = X11Colors.get_color_hex(bg)
bt = Tk.Button(self, text=text, width=width, height=height, bg=bg_color, fg=fg, padx=0, pady=0, cursor="hand1",
......@@ -44,7 +97,33 @@ class CustomGrid(Tk.Frame):
def add_cell_radio_button(self, text, value, row, column, width=0, height=0, bg='white', fg="black", selected=False, command=None, command_args=None):
"""Add a cell with a radio button in the grid.
Parameters
----------
text : string
Label of radio button.
value : integer
Value of radio button.
row : integer
Row with the grid. Initial position is 0.
column : integer
Column with the grid. Initial position is 0.
width : integer, optional, default = 0
Width of cell.
height : integer, optional, default = 0
Height of cell.
bg : string, optional, default = 'white'
Background color of cell. X11Color.
fg : string, optional, default = 'white'
Foreground color of cell. X11Color.
selected : boolean, optional, default = False
State of radio button, if true, set selected.
command : function, optional, default = None
Method to be executed on click radio button.
command_args : integer, optional, default = None
Arguments of method executed on click radio button.
"""
radio = Tk.Radiobutton(self, text=text, variable=self.v, value=value,
width=width, height=height, bg=bg, fg=fg, padx=4, pady=4,
indicatoron=1, anchor=Tk.W, command=lambda *_: command(command_args),
......
......@@ -16,6 +16,7 @@ else:
from abc import ABCMeta, abstractmethod
class ItemMenu(object):
"""Abstract class for menu items."""
__metaclass__ = ABCMeta
......@@ -24,18 +25,42 @@ class ItemMenu(object):
action = None
def __init__(self, parent):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
"""
self.parent = parent
@abstractmethod
def render(self, menu):
"""Render item menu.
Implement this method to extend this class with a new type of item menu.
"""
pass
class Command(ItemMenu):
"""Menu option object."""
label = None
def __init__(self, parent, label, action, shortcut = None):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
label : string
Menu option label.
action : function
Callback to be executed on menu option click.
shortcut : string, optional, default = None
Menu option shortcut number ou letter. Not case sensitive.
"""
super(self.__class__, self).__init__(parent)
self.label = label
self.action = action
......@@ -43,6 +68,13 @@ class Command(ItemMenu):
self.shortcut = str(shortcut).upper()
def render(self, menu):
"""Render item menu.
Parameters
----------
menu : Tk widget
Menu parent of item.
"""
accelerator = None if self.shortcut is None else "Ctrl+" + str(self.shortcut)
underline = None if self.shortcut is None else self.label.upper().find( self.shortcut )
......@@ -50,20 +82,51 @@ class Command(ItemMenu):
class Separator(ItemMenu):
"""Menu separator object."""
def __init__(self, parent):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
"""
super(self.__class__, self).__init__(parent)
def render(self, menu):
"""Render item menu.
Parameters
----------
menu : Tk widget
Menu parent of item.
"""
menu.add_separator()
class CheckButton(ItemMenu):
"""Menu check button object."""
label = None
default_state = None
def __init__(self, parent, label, action, shortcut = None, default_state = True):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
label : string
Check button label.
action : function
Callback to be executed on check button click.
shortcut : string, optional, default = None
Check button shortcut number ou letter. Not case sensitive.
default_state : boolean, optional, default = True
Initial state of check button. If true set to on.
"""
super(self.__class__, self).__init__(parent)
self.label = label
self.action = action
......@@ -72,6 +135,13 @@ class CheckButton(ItemMenu):
self.default_state = default_state
def render(self, menu):
"""Render item menu.
Parameters
----------
menu : Tk widget
Menu parent of item.
"""
accelerator = None if self.shortcut is None else "Ctrl+" + str(self.shortcut)
underline = None if self.shortcut is None else self.label.upper().find( self.shortcut )
......
......@@ -15,9 +15,16 @@ else:
import tkinter as Tk
class Log(object):
"""Console log implementation."""
def __init__(self, parent):
"""Constructor.
Parameters
----------
parent : Tk widget
Parent widget of this class.
"""
self.parent = parent
self._body = Tk.Text(self.parent, width=0, height=10, bg="white", fg="black", padx=5, pady=5)
self._body.insert(Tk.INSERT, "$ Pynovisao is ready to use!\n")
......@@ -26,21 +33,43 @@ class Log(object):
def write_logger(self, fmt, *args):
"""Log a new formatted message.
Parameters
----------
fmt : string
Message with format variables.
*args : arguments
List of arguments of message.
"""
self.clear_logger()
self.append_logger(fmt % args)
def append_logger(self, fmt, *args):
"""Append a formatted message to log.
Parameters
----------
fmt : string
Message with format variables.
*args : arguments
List of arguments of message.
"""
self._body.config(state=Tk.NORMAL)
self._body.insert(Tk.END, fmt % args)
self._body.insert(Tk.END, '\n')
self._body.config(state=Tk.DISABLED)
def clear_logger(self):
"""Clear log content.
"""
self._body.config(state=Tk.NORMAL)
self._body.delete('1.0', Tk.END)