## Filename : chart_view.py
## Author(s) : Michel Le Borgne
## Created : 4/3/2010
## Revision :
## Source :
##
## Copyright 2009 - 2010 - 2020 IRISA/IRSET
##
## This library is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published
## by the Free Software Foundation; either version 2.1 of the License, or
## any later version.
##
## This library is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF
## MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. The software and
## documentation provided hereunder is on an "as is" basis, and IRISA has
## no obligations to provide maintenance, support, updates, enhancements
## or modifications.
## In no event shall IRISA be liable to any party for direct, indirect,
## special, incidental or consequential damages, including lost profits,
## arising out of the use of this software and its documentation, even if
## IRISA have been advised of the possibility of such damage. See
## the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this library; if not, write to the Free Software Foundation,
## Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## The original code contained here was initially developed by:
##
## Michel Le Borgne.
## IRISA
## Symbiose team
## IRISA Campus de Beaulieu
## 35042 RENNES Cedex, FRANCE
##
##
## Contributor(s): Geoffroy Andrieux, Nolwenn Le Meur
##
"""
Main views for the Cadbiom gui
Classes available and inheritance hierarchy:
gtk.DrawingArea:
:class:`ChartView`: Used by the graph editor window/page of charter
:class:`NavView`: overview widget
gtk.Frame:
:class:`ChartPage`: Graph editor window/page of charter
"""
import gtk
[docs]class ChartView(gtk.DrawingArea):
"""Class for the main drawing window of charter
.. note:: Also instanciated by the overview widget.
.. note:: DrawingArea can capture key-press-events:
- add gtk.gdk.KEY_PRESS_MASK mask to event
- be sure that the DrawingArea has the focus by using grab_focus() on
mouse events.
.. TODO:: Optimize events handling:
- on_motion_notify: Mouse over the widget, causing multiple calls to
find_element() of the top node.
- on_expose_event: When the window is covered/partially covered;
all the pixmap is redrawn.
"""
def __init__(self, width, height, drawing_style, controler, model):
"""
@param width, height: int - size of the view
@param drawing_style: a Drawing style associated with the pattern
@param controler: the controller as in the MVC pattern
NavControler or ChartControler objects
@param model: a ChartModel
"""
gtk.DrawingArea.__init__(self)
self.controler = controler
controler.set_view(self)
self.model = model
# attach as model observer
model.attach(self)
# signals
self.add_events(
gtk.gdk.EXPOSURE_MASK
| gtk.gdk.LEAVE_NOTIFY_MASK
| gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk._2BUTTON_PRESS
| gtk.gdk.BUTTON_RELEASE_MASK
| gtk.gdk.POINTER_MOTION_MASK
| gtk.gdk.POINTER_MOTION_HINT_MASK
)
# Get the focus
self.set_flags(gtk.HAS_FOCUS | gtk.CAN_FOCUS)
self.grab_focus()
self.connect("expose-event", self.on_expose_event)
self.connect("configure_event", self.on_configure_event)
# controlled events
self.connect("button_press_event", self.on_button_press)
self.connect("button_release_event", controler.on_button_release)
self.connect("motion_notify_event", controler.on_motion_notify)
# drawing stuffs
drawing_style.view = self
self.drawing_style = drawing_style
controler.drawing_style = drawing_style
self.draw_width = width # drawing window
self.min_width = width
self.draw_height = height
self.min_height = height
self.set_size_request(width, height)
self.stylepango = self.create_pango_layout("")
self.pixmap = None # cannot be initialized before self.window creation
self.page = None
self.zoom_count = 0
[docs] def on_expose_event(self, widget, event):
"""
Redisplay drawing after cover and exposure of the window
"""
self.pixmap.draw_rectangle(
self.get_style().white_gc, True, 0, 0, self.draw_width, self.draw_height
)
# draw the full model from its top node in the pixmap
self.model.draw(self)
# expose the drawing
self.window.draw_drawable(self.get_style().fg_gc[gtk.STATE_NORMAL],
self.pixmap, 0, 0, 0, 0,
self.draw_width, self.draw_height)
if self.page:
self.page.notify(widget)
return True
[docs] def update(self):
"""Display a new drawing of the model
Notified by the model when changes have been made (move/delete/add item)
Or by the ChartControler when a transition/node is selected.
"""
# update only visible views
if not self.window:
return
# resize if virtual drawing window size changed
size = self.pixmap.get_size()
if size[0] != self.draw_width or size[1] != self.draw_height:
self.set_size_request(self.draw_width, self.draw_height)
self.pixmap = gtk.gdk.Pixmap(self.window, self.draw_width, self.draw_height)
self.pixmap.draw_rectangle(
self.get_style().white_gc, True, 0, 0, self.draw_width, self.draw_height
)
# draw in the pixmap
self.model.draw(self)
# expose the drawing
self.window.draw_drawable(self.get_style().fg_gc[gtk.STATE_NORMAL],
self.pixmap, 0, 0, 0, 0,
self.draw_width, self.draw_height)
[docs] def zoom_minus(self):
"""
Zoom out function
"""
if self.zoom_count < -10:
return
self.min_height = int(self.min_height * 0.9)
self.draw_height = self.min_height
self.min_width = int(self.min_width * 0.9)
self.draw_width = self.min_width
self.update()
if self.page:
self.page.notify(None)
self.zoom_count = self.zoom_count - 1
[docs] def zoom_plus(self):
"""
Zoom in function
"""
if self.zoom_count > 15:
return
self.min_height = int(self.min_height * 1.1)
self.draw_height = self.min_height
self.min_width = int(self.min_width * 1.1)
self.draw_width = self.min_width
self.update()
if self.page:
self.page.notify(None)
self.zoom_count += 1
[docs]class NavView(ChartView):
"""Specialized ChartView for navigator (used by the overview widget)"""
def __init__(self, width, height, drst, controler, model):
"""
same parameters than in upper class constructor
"""
ChartView.__init__(self, width, height, drst, controler, model)
self.x_ret = 0
self.y_ret = 0
self.w_ret = 1
self.h_ret = 1
self.show_all()
[docs] def on_expose_event(self, widget, event):
"""
Redisplay drawing after cover and exposure of the window
"""
self.pixmap.draw_rectangle(
self.get_style().white_gc, True, 0, 0, self.draw_width, self.draw_height
)
# draw in the pixmap
self.model.draw(self)
# expose the drawing
self.window.draw_drawable(self.get_style().fg_gc[gtk.STATE_NORMAL],
self.pixmap, 0, 0, 0, 0,
self.draw_width, self.draw_height)
# draw reticule
self.window.draw_rectangle(
self.ret_gc, False, self.x_ret, self.y_ret, self.w_ret, self.h_ret
)
return True
[docs] def update(self):
"""Display a new drawing of the model
Notified by the model when changes have been made (move/delete/add item)
"""
# update only visible views
if not self.window:
return
# hack for notebook
if not self.pixmap:
return
# draw in the pixmap
self.pixmap.draw_rectangle(
self.get_style().white_gc, True, 0, 0, self.draw_width, self.draw_height
)
self.model.draw(self)
# expose the drawing
self.window.draw_drawable(self.get_style().fg_gc[gtk.STATE_NORMAL],
self.pixmap, 0, 0, 0, 0,
self.draw_width, self.draw_height)
# draw reticule
self.window.draw_rectangle(
self.ret_gc, False, self.x_ret, self.y_ret, self.w_ret, self.h_ret
)
[docs] def clear(self):
"""
As it says
"""
self.window.draw_rectangle(
self.get_style().white_gc, True, 0, 0, self.draw_width, self.draw_height
)
[docs]class ChartPage(gtk.Frame):
"""
A simple graph editor for pages: a graph area wrapped in a scroll window
"""
def __init__(self, width, height, drst, controler, model):
"""
"""
gtk.Frame.__init__(self)
# for observers
self.__observer_list = []
# chart view and model
self.draw = ChartView(width, height, drst, controler, model)
self.draw.page = self
# wrap in a scroll window
scr_w = gtk.ScrolledWindow()
self.scr_w = scr_w
self.vadj = scr_w.get_vadjustment()
self.hadj = scr_w.get_hadjustment()
self.hadj_h_id = self.hadj.connect("value_changed", self.notify)
self.vadj_h_id = self.vadj.connect("value_changed", self.notify)
self.allow_adj = True
scr_w.set_policy(gtk.POLICY_ALWAYS, gtk.POLICY_ALWAYS)
viewport = gtk.Viewport()
scr_w.add(viewport)
viewport.add(self.draw)
self.add(scr_w)
self.show_all()
# observer pattern methods
[docs] def attach(self, obs):
"""
standard in observer pattern
"""
if not obs in self.__observer_list:
self.__observer_list.append(obs)
[docs] def detach(self, obs):
"""
standard in observer pattern
"""
self.__observer_list.remove(obs)
[docs] def notify(self, widget):
"""
used when the viewport is moved from inside (hbar and vbar)
"""
if self.allow_adj:
x_pos = float(self.hadj.value) / self.draw.draw_width
y_pos = float(self.vadj.value) / self.draw.draw_height
width = self.hadj.page_size / self.draw.draw_width
height = self.vadj.page_size / self.draw.draw_height
for obs in self.__observer_list:
obs.scroll_update(x_pos, y_pos, width, height)
# signal call back
[docs] def update(self, dxx, dyy):
"""
Used when the viewport is moved from outside (navigator)
"""
d_hadj = dxx * self.draw.draw_width
d_vadj = dyy * self.draw.draw_height
self.allow_adj = False
self.hadj.value = int(self.hadj.value + d_hadj)
self.vadj.value = int(self.vadj.value + d_vadj)
self.scr_w.set_hadjustment(self.hadj)
self.scr_w.set_vadjustment(self.vadj)
self.allow_adj = True
self.notify(None)