Source code for camelot.view.controls.editors.charteditor

#  ============================================================================
#
#  Copyright (C) 2007-2013 Conceptive Engineering bvba. All rights reserved.
#  www.conceptive.be / info@conceptive.be
#
#  This file is part of the Camelot Library.
#
#  This file may be used under the terms of the GNU General Public
#  License version 2.0 as published by the Free Software Foundation
#  and appearing in the file license.txt included in the packaging of
#  this file.  Please review this information to ensure GNU
#  General Public Licensing requirements will be met.
#
#  If you are unsure which license is appropriate for your use, please
#  visit www.python-camelot.com or contact info@conceptive.be
#
#  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
#  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
#  For use of this library in commercial applications, please contact
#  info@conceptive.be
#
#  ============================================================================

import logging

from PyQt4 import QtGui
from PyQt4 import QtCore

from camelot.admin.action.list_action import ListActionGuiContext
from camelot.core.utils import ugettext as _
from camelot.view.controls.editors.customeditor import AbstractCustomEditor
from camelot.view.controls.editors.wideeditor import WideEditor
from camelot.view.proxy import ValueLoading
from camelot.view.art import Icon

PAD_INCHES = 0.1

LOGGER = logging.getLogger('camelot.view.controls.editors.charteditor')

[docs]class ChartEditor( QtGui.QFrame, AbstractCustomEditor, WideEditor ): """Editor to display and manipulate matplotlib charts. The editor itself is generic for all kinds of plots, it simply provides the data to be ploted with a set of axes. The data itself should know how exactly to plot itself. """ show_fullscreen_signal = QtCore.pyqtSignal() editingFinished = QtCore.pyqtSignal() def __init__(self, parent=None, width=50, height=40, dpi=50, field_name='chart', **kwargs): from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas super(ChartEditor, self).__init__( parent ) AbstractCustomEditor.__init__( self ) self.setObjectName( field_name ) chart_frame = QtGui.QFrame( self ) chart_frame.setFrameShape( self.Box ) chart_frame.setContentsMargins( 1, 1, 1, 1 ) chart_frame_layout = QtGui.QHBoxLayout() chart_frame_layout.setContentsMargins( 0, 0, 0, 0) chart_frame.setLayout( chart_frame_layout ) # find out background color, because using a transparent # figure fails when the window is resized: the background # is not redrawn # need to do str() else matplotlib gets confused by the qstring # bgcolorrgb = str(self.palette().background().color().name()) self.fig = Figure( figsize=(width, height), dpi=dpi, facecolor='#ffffff', ) layout = QtGui.QHBoxLayout() self.canvas = FigureCanvas( self.fig ) chart_frame_layout.addWidget( self.canvas ) layout.addWidget(chart_frame) button_layout = QtGui.QVBoxLayout() button_layout.setSpacing( 0 ) icon = Icon( 'tango/16x16/actions/document-print-preview.png' ).getQIcon() button_layout.addStretch() print_button = QtGui.QToolButton() print_button.setIcon( icon ) print_button.setAutoRaise( True ) print_button.setToolTip( _('Print Preview') ) print_button.clicked.connect( self.print_preview ) button_layout.addWidget( print_button ) icon = Icon( 'tango/16x16/actions/edit-copy.png' ).getQIcon() copy_button = QtGui.QToolButton() copy_button.setIcon( icon ) copy_button.setAutoRaise( True ) copy_button.setToolTip( _('Copy to clipboard') ) copy_button.clicked.connect( self.copy_to_clipboard ) button_layout.addWidget( copy_button ) layout.addLayout( button_layout ) layout.setContentsMargins( 0, 0, 0, 0) self.setLayout(layout) self.canvas.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding ) self.canvas.installEventFilter(self) self.show_fullscreen_signal.connect(self.show_fullscreen) self.canvas.updateGeometry() self._litebox = None self.gui_context = ListActionGuiContext() @QtCore.pyqtSlot()
[docs] def copy_to_clipboard(self): """Copy the chart to the clipboard""" clipboard = QtGui.QApplication.clipboard() pixmap = QtGui.QPixmap.grabWidget( self.canvas ) clipboard.setPixmap( pixmap )
@QtCore.pyqtSlot()
[docs] def print_preview(self): """Popup a print preview dialog for the Chart""" from camelot.view.action_steps import PrintChart print_chart = PrintChart( self._value ) print_chart.gui_run( self.gui_context )
[docs] def set_field_attributes(self, *args, **kwargs): """Overwrite set_field attributes because a ChartEditor cannot be disabled or have its background color changed""" pass
@staticmethod
[docs] def show_fullscreen_chart(chart, parent): """ :param chart: a chart container :return: the widget showing the chart, by default a LiteBoxView """ from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from camelot.view.controls.liteboxview import LiteBoxView from camelot.container.chartcontainer import structure_to_figure_container figure_container = structure_to_figure_container( chart ) litebox = LiteBoxView(parent) fig = Figure(facecolor='#ffffff') canvas = FigureCanvas(fig) canvas.updateGeometry() figure_container.plot_on_figure(fig) proxy = QtGui.QGraphicsProxyWidget() proxy.setWidget(canvas) litebox.show_fullscreen_item(proxy) canvas.draw() return litebox
@QtCore.pyqtSlot()
[docs] def show_fullscreen(self): """Show the plot full screen, using the litebox""" if self._value: # if we give the litebox a parent, the close button does not work ?? self._litebox = self.show_fullscreen_chart(self._value, None)
[docs] def eventFilter(self, object, event): """intercept mouse clicks on a chart to show the chart fullscreen""" if not object.isWidgetType(): return False if event.type() != QtCore.QEvent.MouseButtonPress: return False if event.modifiers() != QtCore.Qt.NoModifier: return False if event.buttons() == QtCore.Qt.LeftButton: self.show_fullscreen_signal.emit() return True return False
[docs] def set_value(self, value): """Accepts a camelot.container.chartcontainer.FigureContainer or a camelot.container.chartcontainer.AxesContainer """ from camelot.container.chartcontainer import structure_to_figure_container self._value = structure_to_figure_container( AbstractCustomEditor.set_value( self, value ) ) self.on_draw()
def get_value(self): return AbstractCustomEditor.get_value( self ) or self._value # def _get_tightbbox(self, fig, pad_inches): # renderer = fig.canvas.get_renderer() # bbox_inches = fig.get_tightbbox(renderer) # return bbox_inches.padded(pad_inches) # # def tight_borders(self, fig, pad_inches=PAD_INCHES): # """Stretch subplot boundaries to figure edges plus padding.""" # # call draw to update the renderer and get accurate bboxes. # import numpy as np # fig.canvas.draw() # bbox_original = fig.bbox_inches # bbox_tight = self._get_tightbbox(fig, pad_inches) # # print bbox_tight # # # figure dimensions ordered like bbox.extents: x0, y0, x1, y1 # lengths = np.array([bbox_original.width, bbox_original.height, # bbox_original.width, bbox_original.height]) # whitespace = (bbox_tight.extents - bbox_original.extents) / lengths # # # border padding ordered like bbox.extents: x0, y0, x1, y1 # current_borders = np.array([fig.subplotpars.left, fig.subplotpars.bottom, # fig.subplotpars.right, fig.subplotpars.top]) # # # left, bottom, right, top = current_borders - whitespace # print self.canvas.size() # print left, bottom, right, top # fig.subplots_adjust(bottom=bottom, top=top, left=left, right=right)
[docs] def on_draw(self): """draw the matplotlib figure on the canvas""" if self._value not in (None, ValueLoading): self._value.plot_on_figure(self.fig) self.canvas.draw() # renderer = self.fig.canvas.get_renderer() # print 'widget size', self.canvas.size() # print 'renderer size', renderer.get_canvas_width_height() # print 'points_to_pixels', renderer.points_to_pixels(1.0) # print 'tightbbox', self.fig.get_tightbbox(renderer) # #self.fig.subplots_adjust(bottom=0.3, right=0.9, top=0.9, left=0.1) # self.tight_borders(self.fig) # self.canvas.draw()