diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e7c5d8c9..f24426e79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -344,6 +344,15 @@ MARK_AS_ADVANCED(FORCE FREECAD_LIBPACK_CHECKFILE6X FREECAD_LIBPACK_CHECKFILE7X)
find_package(Spnav)
endif(WIN32)
+# ------------------------------ Matplotlib ------------------------------
+
+ find_package(Matplotlib)
+ IF(MATPLOTLIB_FOUND)
+ message("-- matplotlib-${MATPLOTLIB_VERSION} has been found.")
+ ELSE(MATPLOTLIB_FOUND)
+ message("matplotlib not found, Plot module will not available until matplotlib is installed!")
+ ENDIF(MATPLOTLIB_FOUND)
+
# ------------------------------------------------------------------------
diff --git a/cMake/FindMatplotlib.cmake b/cMake/FindMatplotlib.cmake
new file mode 100644
index 000000000..ef2c831a5
--- /dev/null
+++ b/cMake/FindMatplotlib.cmake
@@ -0,0 +1,48 @@
+# - Find the matplotlib libraries
+# This module finds IF matplotlib is installed, and sets the following variables
+# indicating where it is.
+#
+# MATPLOTLIB_FOUND - was matplotlib found
+# MATPLOTLIB_VERSION - the version of matplotlib found as a string
+# MATPLOTLIB_VERSION_MAJOR - the major version number of matplotlib
+# MATPLOTLIB_VERSION_MINOR - the minor version number of matplotlib
+# MATPLOTLIB_VERSION_PATCH - the patch version number of matplotlib
+# MATPLOTLIB_VERSION_DECIMAL - e.g. version 1.1.1r is 10101
+# MATPLOTLIB_PATH_DIRS - path to the matplotlib include files
+
+IF(PYTHONINTERP_FOUND)
+ # Try to import matplotlib into Python interpreter. Python
+ # interpreter was found previously as required package, so
+ # don't take care about this.
+ execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+ "import matplotlib as m; print(m.__version__); print(m.__path__[0]);"
+ RESULT_VARIABLE _MATPLOTLIB_SEARCH_SUCCESS
+ OUTPUT_VARIABLE _MATPLOTLIB_VALUES
+ ERROR_VARIABLE _MATPLOTLIB_ERROR_VALUE
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ IF(_MATPLOTLIB_SEARCH_SUCCESS MATCHES 0)
+ set(MATPLOTLIB_FOUND TRUE)
+
+ # Convert the process output into a list
+ string(REGEX REPLACE ";" "\\\\;" _MATPLOTLIB_VALUES ${_MATPLOTLIB_VALUES})
+ string(REGEX REPLACE "\n" ";" _MATPLOTLIB_VALUES ${_MATPLOTLIB_VALUES})
+ list(GET _MATPLOTLIB_VALUES 0 MATPLOTLIB_VERSION)
+ list(GET _MATPLOTLIB_VALUES 1 MATPLOTLIB_PATH_DIRS)
+
+ # Make sure all directory separators are '/'
+ string(REGEX REPLACE "\\\\" "/" MATPLOTLIB_PATH_DIRS ${MATPLOTLIB_PATH_DIRS})
+
+ # Get the major and minor version numbers
+ string(REGEX REPLACE "\\." ";" _MATPLOTLIB_VERSION_LIST ${MATPLOTLIB_VERSION})
+ list(GET _MATPLOTLIB_VERSION_LIST 0 MATPLOTLIB_VERSION_MAJOR)
+ list(GET _MATPLOTLIB_VERSION_LIST 1 MATPLOTLIB_VERSION_MINOR)
+ list(GET _MATPLOTLIB_VERSION_LIST 2 MATPLOTLIB_VERSION_PATCH)
+ math(EXPR MATPLOTLIB_VERSION_DECIMAL
+ "(${MATPLOTLIB_VERSION_MAJOR} * 10000) + (${MATPLOTLIB_VERSION_MINOR} * 100) + ${MATPLOTLIB_VERSION_PATCH}")
+ ELSE()
+ set(MATPLOTLIB_FOUND FALSE)
+ ENDIF()
+ELSE()
+ set(MATPLOTLIB_FOUND FALSE)
+ENDIF()
diff --git a/configure.ac b/configure.ac
index dfdbf8524..2b5969e9e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -649,6 +649,23 @@ fi
AM_CONDITIONAL(HAVE_SPNAV_FOUND, test x"$fc_cv_lib_spnav_avail" = xyes)
+
+dnl checking for matplotlib
+dnl **************************************************************************
+fc_matplotlib_avail=no
+AC_MSG_CHECKING([for matplotlib])
+fc_matplotlib_ver=`python -c "import matplotlib as m; print m.__version__;"`;
+if test x$fc_py_ver = x; then
+ AC_MSG_WARN([
+ **** Cannot find matplotlib Python module.
+ Plot Module will not available until matplotlib is installed ****
+ ])
+else
+ fc_matplotlib_avail=yes
+fi;
+AC_MSG_RESULT([yes])
+AM_CONDITIONAL(HAVE_MATPLOTLIB, test x"$fc_matplotlib_avail" = xyes)
+
#---------------------------------------------------------------------
#
# Check if 64-bit platform
diff --git a/package/debian/control b/package/debian/control
index a0662ef91..6e539444f 100644
--- a/package/debian/control
+++ b/package/debian/control
@@ -21,7 +21,7 @@ Standards-Version: 3.9.2
Package: freecad
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}
-Recommends: python-pivy
+Recommends: python-pivy python-matplotlib
Suggests: freecad-doc
Description: Extensible Open Source CAx program (alpha)
FreeCAD is an Open Source CAx RAD based on OpenCasCade, Qt and Python.
diff --git a/src/Mod/CMakeLists.txt b/src/Mod/CMakeLists.txt
index d1f0736ae..19e5d0c48 100644
--- a/src/Mod/CMakeLists.txt
+++ b/src/Mod/CMakeLists.txt
@@ -48,4 +48,7 @@ add_subdirectory(Surfaces)
add_subdirectory(Ship)
add_subdirectory(OpenSCAD)
+add_subdirectory(Plot)
+
+
diff --git a/src/Mod/Makefile.am b/src/Mod/Makefile.am
index 246e76ba4..69e69f9b2 100644
--- a/src/Mod/Makefile.am
+++ b/src/Mod/Makefile.am
@@ -43,6 +43,8 @@ if BUILD_CAM
SUBDIRS += Cam
endif
+SUBDIRS += Plot
+
EXTRA_DIST = \
__init__.py \
CMakeLists.txt \
diff --git a/src/Mod/Plot/CMakeLists.txt b/src/Mod/Plot/CMakeLists.txt
new file mode 100644
index 000000000..f52bb5f70
--- /dev/null
+++ b/src/Mod/Plot/CMakeLists.txt
@@ -0,0 +1,119 @@
+SET(PlotMain_SRCS
+ Plot.py
+ InitGui.py
+ PlotGui.py
+)
+SOURCE_GROUP("" FILES ${PlotMain_SRCS})
+
+SET(PlotIcons_SRCS
+ Icons/Axes.svg
+ Icons/Grid.svg
+ Icons/Icon.svg
+ Icons/Labels.svg
+ Icons/Legend.svg
+ Icons/Positions.svg
+ Icons/Save.svg
+ Icons/Series.svg
+)
+SOURCE_GROUP("ploticons" FILES ${PlotIcons_SRCS})
+
+SET(PlotAxes_SRCS
+ plotAxes/__init__.py
+ plotAxes/TaskPanel.py
+ plotAxes/TaskPanel.ui
+)
+SOURCE_GROUP("plotaxes" FILES ${PlotAxes_SRCS})
+
+SET(PlotLabels_SRCS
+ plotLabels/__init__.py
+ plotLabels/TaskPanel.py
+ plotLabels/TaskPanel.ui
+)
+SOURCE_GROUP("plotlabels" FILES ${PlotLabels_SRCS})
+
+SET(PlotPositions_SRCS
+ plotPositions/__init__.py
+ plotPositions/TaskPanel.py
+ plotPositions/TaskPanel.ui
+)
+SOURCE_GROUP("plotpositions" FILES ${PlotPositions_SRCS})
+
+SET(PlotSave_SRCS
+ plotSave/__init__.py
+ plotSave/TaskPanel.py
+ plotSave/TaskPanel.ui
+)
+SOURCE_GROUP("plotsave" FILES ${PlotSave_SRCS})
+
+SET(PlotSeries_SRCS
+ plotSeries/__init__.py
+ plotSeries/TaskPanel.py
+ plotSeries/TaskPanel.ui
+)
+SOURCE_GROUP("plotseries" FILES ${PlotSeries_SRCS})
+
+SET(PlotUtils_SRCS
+ plotUtils/__init__.py
+ plotUtils/Paths.py
+ plotUtils/Translator.py
+)
+SOURCE_GROUP("plotutils" FILES ${PlotUtils_SRCS})
+
+SET(all_files ${PlotMain_SRCS} ${PlotAxes_SRCS} ${PlotLabels_SRCS} ${PlotPositions_SRCS} ${PlotSave_SRCS} ${PlotSeries_SRCS} ${PlotIcons_SRCS} ${PlotUtils_SRCS})
+
+ADD_CUSTOM_TARGET(Plot ALL
+ SOURCES ${all_files}
+)
+
+fc_copy_sources(Mod/Plot "${CMAKE_BINARY_DIR}/Mod/Plot" ${all_files})
+
+INSTALL(
+ FILES
+ ${PlotIcons_SRCS}
+ DESTINATION
+ Mod/Plot/Icons
+)
+INSTALL(
+ FILES
+ ${PlotAxes_SRCS}
+ DESTINATION
+ Mod/Plot/plotAxes
+)
+INSTALL(
+ FILES
+ ${PlotLabels_SRCS}
+ DESTINATION
+ Mod/Plot/plotLabels
+)
+INSTALL(
+ FILES
+ ${PlotPositions_SRCS}
+ DESTINATION
+ Mod/Plot/plotPositions
+)
+INSTALL(
+ FILES
+ ${PlotSave_SRCS}
+ DESTINATION
+ Mod/Plot/plotSave
+)
+INSTALL(
+ FILES
+ ${PlotSeries_SRCS}
+ DESTINATION
+ Mod/Plot/plotSeries
+)
+INSTALL(
+ FILES
+ ${PlotUtils_SRCS}
+ DESTINATION
+ Mod/Plot/plotUtils
+)
+INSTALL(
+ FILES
+ ${PlotMain_SRCS}
+ DESTINATION
+ Mod/Plot
+)
+
+
diff --git a/src/Mod/Plot/Icons/Axes.svg b/src/Mod/Plot/Icons/Axes.svg
new file mode 100644
index 000000000..63786323b
--- /dev/null
+++ b/src/Mod/Plot/Icons/Axes.svg
@@ -0,0 +1,451 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Grid.svg b/src/Mod/Plot/Icons/Grid.svg
new file mode 100755
index 000000000..8e0847f9a
--- /dev/null
+++ b/src/Mod/Plot/Icons/Grid.svg
@@ -0,0 +1,407 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Icon.svg b/src/Mod/Plot/Icons/Icon.svg
new file mode 100755
index 000000000..ff800bde3
--- /dev/null
+++ b/src/Mod/Plot/Icons/Icon.svg
@@ -0,0 +1,403 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Labels.svg b/src/Mod/Plot/Icons/Labels.svg
new file mode 100644
index 000000000..d448517e1
--- /dev/null
+++ b/src/Mod/Plot/Icons/Labels.svg
@@ -0,0 +1,705 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Legend.svg b/src/Mod/Plot/Icons/Legend.svg
new file mode 100644
index 000000000..8cf15e82d
--- /dev/null
+++ b/src/Mod/Plot/Icons/Legend.svg
@@ -0,0 +1,679 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Positions.svg b/src/Mod/Plot/Icons/Positions.svg
new file mode 100644
index 000000000..895eb42a3
--- /dev/null
+++ b/src/Mod/Plot/Icons/Positions.svg
@@ -0,0 +1,702 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Save.svg b/src/Mod/Plot/Icons/Save.svg
new file mode 100755
index 000000000..ebdf1f1e0
--- /dev/null
+++ b/src/Mod/Plot/Icons/Save.svg
@@ -0,0 +1,701 @@
+
+
+
+
diff --git a/src/Mod/Plot/Icons/Series.svg b/src/Mod/Plot/Icons/Series.svg
new file mode 100644
index 000000000..1664f1a41
--- /dev/null
+++ b/src/Mod/Plot/Icons/Series.svg
@@ -0,0 +1,457 @@
+
+
+
+
diff --git a/src/Mod/Plot/InitGui.py b/src/Mod/Plot/InitGui.py
new file mode 100644
index 000000000..35d9d465a
--- /dev/null
+++ b/src/Mod/Plot/InitGui.py
@@ -0,0 +1,50 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+class PlotWorkbench ( Workbench ):
+ """ @brief Workbench of Plot module. Here toolbars & icons are append. """
+ from plotUtils import Paths, Translator
+ import PlotGui
+
+ Icon = Paths.iconsPath() + "/Icon.svg"
+ MenuText = str(Translator.translate("Plot"))
+ ToolTip = str(Translator.translate("Plot"))
+
+ def Initialize(self):
+ from plotUtils import Translator
+ # ToolBar
+ list = ["Plot_SaveFig", "Plot_Axes", "Plot_Series", "Plot_Grid", "Plot_Legend", "Plot_Labels", "Plot_Positions"]
+ self.appendToolbar("Plot",list)
+
+ # Menu
+ list = ["Plot_SaveFig", "Plot_Axes", "Plot_Series", "Plot_Grid", "Plot_Legend", "Plot_Labels", "Plot_Positions"]
+ self.appendMenu("Plot",list)
+
+try:
+ import matplotlib
+ Gui.addWorkbench(PlotWorkbench())
+except ImportError:
+ from plotUtils import Translator
+ msg = Translator.translate("matplotlib not found, Plot module will be disabled.\n")
+ FreeCAD.Console.PrintMessage(msg)
+
diff --git a/src/Mod/Plot/Makefile.am b/src/Mod/Plot/Makefile.am
new file mode 100644
index 000000000..c0411b0d8
--- /dev/null
+++ b/src/Mod/Plot/Makefile.am
@@ -0,0 +1,45 @@
+# Change data dir from default ($(prefix)/share) to actual dir
+datadir = $(prefix)/Mod/Plot
+
+data_DATA = \
+ Plot.py \
+ InitGui.py \
+ PlotGui.py
+
+nobase_data_DATA = \
+ Icons/Axes.svg \
+ Icons/Grid.svg \
+ Icons/Icon.svg \
+ Icons/Labels.svg \
+ Icons/Legend.svg \
+ Icons/Positions.svg \
+ Icons/Save.svg \
+ Icons/Series.svg \
+ plotAxes/__init__.py \
+ plotAxes/TaskPanel.py \
+ plotAxes/TaskPanel.ui \
+ plotLabels/__init__.py \
+ plotLabels/TaskPanel.py \
+ plotLabels/TaskPanel.ui \
+ plotPositions/__init__.py \
+ plotPositions/TaskPanel.py \
+ plotPositions/TaskPanel.ui \
+ plotSave/__init__.py \
+ plotSave/TaskPanel.py \
+ plotSave/TaskPanel.ui \
+ plotSeries/__init__.py \
+ plotSeries/TaskPanel.py \
+ plotSeries/TaskPanel.ui \
+ plotUtils/__init__.py \
+ plotUtils/Paths.py \
+ plotUtils/Translator.py
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = \
+ $(data_DATA) \
+ $(nobase_data_DATA) \
+ CMakeLists.txt \
+ README \
+ plot.dox
+
diff --git a/src/Mod/Plot/Plot.py b/src/Mod/Plot/Plot.py
new file mode 100644
index 000000000..fc8767799
--- /dev/null
+++ b/src/Mod/Plot/Plot.py
@@ -0,0 +1,314 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD
+import FreeCAD
+
+# Plot module
+from plotUtils import Paths, Translator
+
+# PyQt4
+from PyQt4 import QtCore, QtGui
+
+# Matplot lib
+try:
+ import matplotlib
+ import matplotlib.pyplot as plt
+ from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+ from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
+ from matplotlib.figure import Figure
+except ImportError:
+ msg = Translator.translate("matplotlib not installed, so Plot module is disabled.\n")
+ FreeCAD.Console.PrintError(msg)
+ raise ImportError("matplotlib not installed")
+
+def getMainWindow():
+ """ getMainWindow(): Gets FreeCAD main window. """
+ toplevel = QtGui.qApp.topLevelWidgets()
+ for i in toplevel:
+ if i.metaObject().className() == "Gui::MainWindow":
+ return i
+ return None
+
+def getMdiArea():
+ """ getMdiArea(): Gets FreeCAD MdiArea. """
+ mw = getMainWindow()
+ if not mw:
+ return None
+ return mw.findChild(QtGui.QMdiArea)
+
+def getPlot():
+ """ getPlot(): Gets selected Plot document if exist. """
+ # Get active tab
+ mdi = getMdiArea()
+ if not mdi:
+ return None
+ sub = mdi.activeSubWindow()
+ if not sub:
+ return None
+ # Explore childrens looking for Plot class
+ for i in sub.children():
+ if i.metaObject().className() == "Plot":
+ return i
+ return None
+
+def figure(winTitle="plot"):
+ """ figure(winTitle="plot"): Create a new plot subwindow.\n winTitle = Tab title. """
+ mdi = getMdiArea()
+ if not mdi:
+ return None
+ win = Plot(winTitle)
+ sub=mdi.addSubWindow(win)
+ sub.show()
+ return win
+
+def plot(x,y,name=None):
+ """ plot(x,y,name=None): Plots a new serie (as line plot)\n x = X values\n y = Y values\n name = Serie name (for legend). """
+ # Get active plot, or create another one if don't exist
+ plt = getPlot()
+ if not plt:
+ plt = figure()
+ # Call to plot
+ return plt.plot(x,y,name)
+
+def series():
+ """ lines(): Get all lines from selected plot. """
+ plt = getPlot()
+ if not plt:
+ return []
+ return plt.series
+
+def removeSerie(index):
+ """ removeSerie(index): Removes a serie from plot.\n index = Index of serie to remove. """
+ # Get active series
+ plt = getPlot()
+ if not plt:
+ return
+ plots = plt.series
+ if not plots:
+ return
+ # Remove line from plot
+ axes = plots[index].axes
+ axes.lines.pop(plots[index].lid)
+ # Remove serie from list
+ del plt.series[index]
+ # Update GUI
+ plt.update()
+
+def legend(status=True, pos=None, fontsize=None):
+ """ legend(status=True): Show/Hide legend.\n status = True if legend must be shown, False otherwise.\n pos = Legend position.\n fontsize = Font size """
+ plt = getPlot()
+ if not plt:
+ return
+ plt.legend = status
+ if pos:
+ plt.legPos = pos
+ if fontsize:
+ plt.legSiz = fontsize
+ # Hide all legends
+ for axes in plt.axesList:
+ axes.legend_ = None
+ # Legend must be activated on last axes
+ axes = plt.axesList[-1]
+ if status:
+ # Setup legend handles and names
+ lines = series()
+ handles = []
+ names = []
+ for l in lines:
+ if l.name != None:
+ handles.append(l.line)
+ names.append(l.name)
+ # Show the legend
+ l = axes.legend(handles, names, bbox_to_anchor=plt.legPos)
+ for t in l.get_texts():
+ t.set_fontsize(plt.legSiz)
+ plt.update()
+
+def grid(status=True):
+ """ grid(status=True): Show/Hide grid.\n status = True if grid must be shown, False otherwise. """
+ plt = getPlot()
+ if not plt:
+ return
+ plt.grid = status
+ axes = plt.axes
+ axes.grid(status)
+ plt.update()
+
+def title(string):
+ """ title(string): Setup plot title.\n string = Title to set. """
+ plt = getPlot()
+ if not plt:
+ return
+ axes = plt.axes
+ axes.set_title(string)
+ plt.update()
+
+def xlabel(string):
+ """ xlabel(string): Setup x label.\n string = Title to set. """
+ plt = getPlot()
+ if not plt:
+ return
+ axes = plt.axes
+ axes.set_xlabel(string)
+ plt.update()
+
+def ylabel(string):
+ """ ylabel(string): Setup y label.\n string = Title to set. """
+ plt = getPlot()
+ if not plt:
+ return
+ axes = plt.axes
+ axes.set_ylabel(string)
+ plt.update()
+
+def axesList():
+ """ axesList(): Gets plot axes list. """
+ plt = getPlot()
+ if not plt:
+ return []
+ return plt.axesList
+
+def axes():
+ """ axes(): Gets active plot axes. """
+ plt = getPlot()
+ if not plt:
+ return None
+ return plt.axes
+
+def addNewAxes(rect=None, frameon=True, patchcolor='none'):
+ """ addNewAxes(pos=None, frameon=True): Add new axes to plot, setting it as active one.\n rect = Axes area, None to copy last axes data.\n frameon = True to show frame, False otherwise.\n patchcolor = Patch color, 'none' for transparent plot. """
+ plt = getPlot()
+ if not plt:
+ return None
+ fig = plt.fig
+ if rect == None:
+ rect = plt.axes.get_position()
+ ax = fig.add_axes(rect, frameon=frameon)
+ ax.xaxis.set_ticks_position('bottom')
+ ax.spines['top'].set_color('none')
+ ax.yaxis.set_ticks_position('left')
+ ax.spines['right'].set_color('none')
+ ax.patch.set_facecolor(patchcolor)
+ plt.axesList.append(ax)
+ plt.setActiveAxes(-1)
+ plt.update()
+ return ax
+
+def save(path, figsize=None, dpi=None):
+ """ save(path): Save plot.\n path = Destination file path.\n figsize = w,h figure size tuple in inches.\n dpi = Dots per inch."""
+ plt = getPlot()
+ if not plt:
+ return
+ # Backup figure options
+ fig = plt.fig
+ sizeBack = fig.get_size_inches()
+ dpiBack = fig.get_dpi()
+ # Save figure with new options
+ if figsize:
+ fig.set_size_inches(figsize[0], figsize[1])
+ if dpi:
+ fig.set_dpi(dpi)
+ plt.canvas.print_figure(path)
+ # Restore figure options
+ fig.set_size_inches(sizeBack[0], sizeBack[1])
+ fig.set_dpi(dpiBack)
+ plt.update()
+
+class Line():
+ def __init__(self, axes, x, y, name):
+ """ __init__(axes, x, y, name): Construct new plot serie.\n axes = Active axes\n x = X values\n y = Y values\n name = Serie name (for legend). """
+ self.axes = axes
+ self.x = x
+ self.y = y
+ self.name = name
+ self.lid = len(axes.lines)
+ self.line, = axes.plot(x,y)
+
+ def setp(self, prop, value):
+ """ setp(prop, value): Change line property value.\n prop = Property name.\n value = New property value. """
+ plt.setp(self.line, prop, value)
+
+ def getp(self, prop):
+ """ getp(prop): Get property value.\n prop = Property name."""
+ return plt.getp(self.line, prop)
+
+class Plot(QtGui.QWidget):
+ def __init__(self, winTitle="plot", parent = None, flags = QtCore.Qt.WindowFlags(0)):
+ """ __init__(winTitle="plot", parent = None, flags = Qt.WindowFlags(0)): Construct a new plot widget.\n winTitle = Tab title.\n parent = Widget parent.\n flags = QWidget flags"""
+ QtGui.QWidget.__init__(self, parent, flags)
+ self.setWindowTitle(winTitle)
+ # Create matplotlib canvas
+ self.fig = Figure()
+ self.canvas = FigureCanvas(self.fig)
+ self.canvas.setParent(self)
+ # Get axes
+ self.axes = self.fig.add_subplot(111)
+ self.axesList = [self.axes]
+ self.axes.xaxis.set_ticks_position('bottom')
+ self.axes.spines['top'].set_color('none')
+ self.axes.yaxis.set_ticks_position('left')
+ self.axes.spines['right'].set_color('none')
+ # Setup layout
+ vbox = QtGui.QVBoxLayout()
+ vbox.addWidget(self.canvas)
+ self.setLayout(vbox)
+ # Active series
+ self.series = []
+ # Indicators
+ self.skip = False
+ self.legend = False
+ self.legPos = (1.0, 1.0)
+ self.legSiz = 14
+ self.grid = False
+
+ def plot(self, x, y, name=None):
+ """ plot(self, x, y, name=None): Plot a new line and return it.\n x = X values\n y = Y values\n name = Serie name (for legend). """
+ l = Line(self.axes, x, y, name)
+ self.series.append(l)
+ # Update window
+ self.update()
+ return l
+
+ def update(self):
+ """ update(): Updates plot. """
+ if not self.skip:
+ self.skip = True
+ if self.legend:
+ legend(self.legend)
+ self.canvas.draw()
+ self.skip = False
+
+ def isGrid(self):
+ """ isGrid(): Return True if Grid is active, False otherwise. """
+ return bool(self.grid)
+
+ def isLegend(self):
+ """ isLegend(): Return True if Legend is active, False otherwise. """
+ return bool(self.legend)
+
+ def setActiveAxes(self, index):
+ """ setActiveAxes(index): Change current active axes.\n index = Index of the new active axes. """
+ self.axes = self.axesList[index]
+ self.fig.sca(self.axes)
+
diff --git a/src/Mod/Plot/PlotGui.py b/src/Mod/Plot/PlotGui.py
new file mode 100644
index 000000000..7f220b90f
--- /dev/null
+++ b/src/Mod/Plot/PlotGui.py
@@ -0,0 +1,132 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+from PyQt4 import QtCore, QtGui
+import FreeCAD, FreeCADGui, os
+
+class Save:
+ def Activated(self):
+ import plotSave
+ plotSave.load()
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Save.svg"
+ MenuText = str(Translator.translate('Save plot'))
+ ToolTip = str(Translator.translate('Save plot as image file.'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+class Axes:
+ def Activated(self):
+ import plotAxes
+ plotAxes.load()
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Axes.svg"
+ MenuText = str(Translator.translate('Configure axes'))
+ ToolTip = str(Translator.translate('Configure axes parameters.'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+class Series:
+ def Activated(self):
+ import plotSeries
+ plotSeries.load()
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Series.svg"
+ MenuText = str(Translator.translate('Configure series'))
+ ToolTip = str(Translator.translate('Configure series drawing style and label.'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+class Grid:
+ def Activated(self):
+ import Plot
+ from plotUtils import Translator
+ plt = Plot.getPlot()
+ if not plt:
+ msg = Translator.translate("Grid must be activated on top of a plot document.")
+ FreeCAD.Console.PrintError(msg+"\n")
+ return
+ flag = plt.isGrid()
+ Plot.grid(not flag)
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Grid.svg"
+ MenuText = str(Translator.translate('Show/Hide grid'))
+ ToolTip = str(Translator.translate('Show/Hide grid on selected plot'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+class Legend:
+ def Activated(self):
+ import Plot
+ from plotUtils import Translator
+ plt = Plot.getPlot()
+ if not plt:
+ msg = Translator.translate("Legend must be activated on top of a plot document.")
+ FreeCAD.Console.PrintError(msg+"\n")
+ return
+ flag = plt.isLegend()
+ Plot.legend(not flag)
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Legend.svg"
+ MenuText = str(Translator.translate('Show/Hide legend'))
+ ToolTip = str(Translator.translate('Show/Hide legend on selected plot'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+class Labels:
+ def Activated(self):
+ import plotLabels
+ plotLabels.load()
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Labels.svg"
+ MenuText = str(Translator.translate('Set labels'))
+ ToolTip = str(Translator.translate('Set title and axes labels'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+class Positions:
+ def Activated(self):
+ import plotPositions
+ plotPositions.load()
+
+ def GetResources(self):
+ from plotUtils import Paths, Translator
+ IconPath = Paths.iconsPath() + "/Positions.svg"
+ MenuText = str(Translator.translate('Set positions and sizes'))
+ ToolTip = str(Translator.translate('Set labels and legend positions and sizes.'))
+ return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
+
+FreeCADGui.addCommand('Plot_SaveFig', Save())
+FreeCADGui.addCommand('Plot_Axes', Axes())
+FreeCADGui.addCommand('Plot_Series', Series())
+FreeCADGui.addCommand('Plot_Grid', Grid())
+FreeCADGui.addCommand('Plot_Legend', Legend())
+FreeCADGui.addCommand('Plot_Labels', Labels())
+FreeCADGui.addCommand('Plot_Positions', Positions())
+
diff --git a/src/Mod/Plot/README b/src/Mod/Plot/README
new file mode 100644
index 000000000..5f82de5e9
--- /dev/null
+++ b/src/Mod/Plot/README
@@ -0,0 +1,10 @@
+* Authors
+---------
+
+Jose Luis Cercós Pita
+
+* Introduction
+--------------
+
+Plot is a module that provide an interface to perform plots.
+
diff --git a/src/Mod/Plot/plot.dox b/src/Mod/Plot/plot.dox
new file mode 100644
index 000000000..46ee0b474
--- /dev/null
+++ b/src/Mod/Plot/plot.dox
@@ -0,0 +1,3 @@
+/** \defgroup PLOT Plot
+ * \ingroup WORKBENCHES */
+
diff --git a/src/Mod/Plot/plotAxes/TaskPanel.py b/src/Mod/Plot/plotAxes/TaskPanel.py
new file mode 100644
index 000000000..848b9506a
--- /dev/null
+++ b/src/Mod/Plot/plotAxes/TaskPanel.py
@@ -0,0 +1,425 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD as App
+import FreeCADGui as Gui
+# Qt library
+from PyQt4 import QtGui,QtCore
+# Module
+import Plot
+from plotUtils import Paths, Translator
+
+class TaskPanel:
+ def __init__(self):
+ self.ui = Paths.modulePath() + "/plotAxes/TaskPanel.ui"
+ self.skip = False
+
+ def accept(self):
+ return True
+
+ def reject(self):
+ return True
+
+ def clicked(self, index):
+ pass
+
+ def open(self):
+ pass
+
+ def needsFullSpace(self):
+ return True
+
+ def isAllowedAlterSelection(self):
+ return False
+
+ def isAllowedAlterView(self):
+ return True
+
+ def isAllowedAlterDocument(self):
+ return False
+
+ def helpRequested(self):
+ pass
+
+ def setupUi(self):
+ mw = self.getMainWindow()
+ form = mw.findChild(QtGui.QWidget, "TaskPanel")
+ form.axId = form.findChild(QtGui.QSpinBox, "axesIndex")
+ form.new = form.findChild(QtGui.QPushButton, "newAxesButton")
+ form.remove = form.findChild(QtGui.QPushButton, "delAxesButton")
+ form.all = form.findChild(QtGui.QCheckBox, "allAxes")
+ form.xMin = form.findChild(QtGui.QSlider, "posXMin")
+ form.xMax = form.findChild(QtGui.QSlider, "posXMax")
+ form.yMin = form.findChild(QtGui.QSlider, "posYMin")
+ form.yMax = form.findChild(QtGui.QSlider, "posYMax")
+ form.xAlign = form.findChild(QtGui.QComboBox, "xAlign")
+ form.yAlign = form.findChild(QtGui.QComboBox, "yAlign")
+ form.xOffset = form.findChild(QtGui.QSpinBox, "xOffset")
+ form.yOffset = form.findChild(QtGui.QSpinBox, "yOffset")
+ form.xAuto = form.findChild(QtGui.QCheckBox, "xAuto")
+ form.yAuto = form.findChild(QtGui.QCheckBox, "yAuto")
+ form.xSMin = form.findChild(QtGui.QLineEdit, "xMin")
+ form.xSMax = form.findChild(QtGui.QLineEdit, "xMax")
+ form.ySMin = form.findChild(QtGui.QLineEdit, "yMin")
+ form.ySMax = form.findChild(QtGui.QLineEdit, "yMax")
+ self.form = form
+ self.retranslateUi()
+ # Look for active axes if can
+ axId = 0
+ plt = Plot.getPlot()
+ if plt:
+ while plt.axes != plt.axesList[axId]:
+ axId = axId + 1
+ form.axId.setValue(axId)
+ self.updateUI()
+ QtCore.QObject.connect(form.axId, QtCore.SIGNAL('valueChanged(int)'),self.onAxesId)
+ QtCore.QObject.connect(form.new, QtCore.SIGNAL("pressed()"),self.onNew)
+ QtCore.QObject.connect(form.remove, QtCore.SIGNAL("pressed()"),self.onRemove)
+ QtCore.QObject.connect(form.xMin, QtCore.SIGNAL("valueChanged(int)"),self.onDims)
+ QtCore.QObject.connect(form.xMax, QtCore.SIGNAL("valueChanged(int)"),self.onDims)
+ QtCore.QObject.connect(form.yMin, QtCore.SIGNAL("valueChanged(int)"),self.onDims)
+ QtCore.QObject.connect(form.yMax, QtCore.SIGNAL("valueChanged(int)"),self.onDims)
+ QtCore.QObject.connect(form.xAlign, QtCore.SIGNAL("currentIndexChanged(int)"),self.onAlign)
+ QtCore.QObject.connect(form.yAlign, QtCore.SIGNAL("currentIndexChanged(int)"),self.onAlign)
+ QtCore.QObject.connect(form.xOffset,QtCore.SIGNAL("valueChanged(int)"),self.onOffset)
+ QtCore.QObject.connect(form.yOffset,QtCore.SIGNAL("valueChanged(int)"),self.onOffset)
+ QtCore.QObject.connect(form.xAuto, QtCore.SIGNAL("stateChanged(int)"),self.onScales)
+ QtCore.QObject.connect(form.yAuto, QtCore.SIGNAL("stateChanged(int)"),self.onScales)
+ QtCore.QObject.connect(form.xSMin, QtCore.SIGNAL("editingFinished()"),self.onScales)
+ QtCore.QObject.connect(form.xSMax, QtCore.SIGNAL("editingFinished()"),self.onScales)
+ QtCore.QObject.connect(form.ySMin, QtCore.SIGNAL("editingFinished()"),self.onScales)
+ QtCore.QObject.connect(form.ySMax, QtCore.SIGNAL("editingFinished()"),self.onScales)
+ QtCore.QObject.connect(Plot.getMdiArea(),QtCore.SIGNAL("subWindowActivated(QMdiSubWindow*)"),self.onMdiArea)
+ return False
+
+ def getMainWindow(self):
+ "returns the main window"
+ # using QtGui.qApp.activeWindow() isn't very reliable because if another
+ # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
+ # returned
+ toplevel = QtGui.qApp.topLevelWidgets()
+ for i in toplevel:
+ if i.metaObject().className() == "Gui::MainWindow":
+ return i
+ raise Exception("No main window found")
+
+ def retranslateUi(self):
+ """ Set user interface locale strings.
+ """
+ self.form.setWindowTitle(Translator.translate("Configure axes"))
+ self.form.findChild(QtGui.QLabel, "axesLabel").setText(Translator.translate("Active axes"))
+ self.form.all.setText(Translator.translate("Apply to all axes"))
+ self.form.findChild(QtGui.QLabel, "dimLabel").setText(Translator.translate("Dimensions"))
+ self.form.findChild(QtGui.QLabel, "xPosLabel").setText(Translator.translate("X axis position"))
+ self.form.findChild(QtGui.QLabel, "yPosLabel").setText(Translator.translate("Y axis position"))
+ self.form.findChild(QtGui.QLabel, "scalesLabel").setText(Translator.translate("Scales"))
+ self.form.xAuto.setText(Translator.translate("X auto"))
+ self.form.yAuto.setText(Translator.translate("Y auto"))
+ self.form.findChild(QtGui.QCheckBox, "allAxes").setText(Translator.translate("Apply to all axes"))
+ self.form.findChild(QtGui.QLabel, "dimLabel").setText(Translator.translate("Dimensions"))
+ self.form.findChild(QtGui.QLabel, "xPosLabel").setText(Translator.translate("X axis position"))
+ self.form.findChild(QtGui.QLabel, "yPosLabel").setText(Translator.translate("Y axis position"))
+
+ def onAxesId(self, value):
+ """ Executed when axes index is modified. """
+ if not self.skip:
+ self.skip = True
+ # UI control in some special plot cases
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ self.skip = False
+ return
+ # UI control in most cases
+ self.form.axId.setMaximum(len(plt.axesList))
+ if self.form.axId.value() >= len(plt.axesList):
+ self.form.axId.setValue(len(plt.axesList)-1)
+ # Send new control to Plot instance
+ plt.setActiveAxes(self.form.axId.value())
+ self.updateUI()
+ self.skip = False
+
+ def onNew(self):
+ """ Executed when new axes must be created. """
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ Plot.addNewAxes()
+ self.form.axId.setValue(len(plt.axesList)-1)
+ plt.update()
+
+ def onRemove(self):
+ """ Executed when axes must be deleted. """
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ # Don't remove first axes
+ if not self.form.axId.value():
+ msg = Translator.translate("Axes 0 can't be deleted")
+ App.Console.PrintError(msg+"\n")
+ return
+ # Remove axes
+ ax = plt.axes
+ ax.set_axis_off()
+ plt.axesList.pop(self.form.axId.value())
+ # Ensure that active axes is correct
+ index = min(self.form.axId.value(), len(plt.axesList)-1)
+ self.form.axId.setValue(index)
+ plt.update()
+
+ def onDims(self, value):
+ """ Executed when axes dims have been modified. """
+ # Get apply environment
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ axesList = [plt.axes]
+ if self.form.all.isChecked():
+ axesList = plt.axesList
+ # Set new dimensions
+ xmin = self.form.xMin.value() / 100.0
+ xmax = self.form.xMax.value() / 100.0
+ ymin = self.form.yMin.value() / 100.0
+ ymax = self.form.yMax.value() / 100.0
+ for axes in axesList:
+ axes.set_position([xmin, ymin, xmax-xmin, ymax-ymin])
+ plt.update()
+
+ def onAlign(self, value):
+ """ Executed when axes align have been modified. """
+ # Get apply environment
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ axesList = [plt.axes]
+ if self.form.all.isChecked():
+ axesList = plt.axesList
+ # Set new alignement
+ for axes in axesList:
+ if self.form.xAlign.currentIndex() == 0:
+ axes.xaxis.tick_bottom()
+ axes.spines['bottom'].set_color((0.0,0.0,0.0))
+ axes.spines['top'].set_color('none')
+ axes.xaxis.set_ticks_position('bottom')
+ axes.xaxis.set_label_position('bottom')
+ else:
+ axes.xaxis.tick_top()
+ axes.spines['top'].set_color((0.0,0.0,0.0))
+ axes.spines['bottom'].set_color('none')
+ axes.xaxis.set_ticks_position('top')
+ axes.xaxis.set_label_position('top')
+ if self.form.yAlign.currentIndex() == 0:
+ axes.yaxis.tick_left()
+ axes.spines['left'].set_color((0.0,0.0,0.0))
+ axes.spines['right'].set_color('none')
+ axes.yaxis.set_ticks_position('left')
+ axes.yaxis.set_label_position('left')
+ else:
+ axes.yaxis.tick_right()
+ axes.spines['right'].set_color((0.0,0.0,0.0))
+ axes.spines['left'].set_color('none')
+ axes.yaxis.set_ticks_position('right')
+ axes.yaxis.set_label_position('right')
+ plt.update()
+
+ def onOffset(self, value):
+ """ Executed when axes offsets have been modified. """
+ # Get apply environment
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ axesList = [plt.axes]
+ if self.form.all.isChecked():
+ axesList = plt.axesList
+ # Set new offset
+ for axes in axesList:
+ # For some reason, modify spines offset erase axes labels, so we
+ # need store it in order to regenerate later
+ x = axes.get_xlabel()
+ y = axes.get_ylabel()
+ for loc, spine in axes.spines.iteritems():
+ if loc in ['bottom', 'top']:
+ spine.set_position(('outward',self.form.xOffset.value()))
+ if loc in ['left', 'right']:
+ spine.set_position(('outward',self.form.yOffset.value()))
+ # Now we can restore axes labels
+ Plot.xlabel(unicode(x))
+ Plot.ylabel(unicode(y))
+ plt.update()
+
+ def onScales(self):
+ """ Executed when axes scales have been modified. """
+ # Get apply environment
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ axesList = [plt.axes]
+ if self.form.all.isChecked():
+ axesList = plt.axesList
+ if not self.skip:
+ self.skip = True
+ # X axis
+ if self.form.xAuto.isChecked():
+ for ax in axesList:
+ ax.set_autoscalex_on(True)
+ self.form.xSMin.setEnabled(False)
+ self.form.xSMax.setEnabled(False)
+ lim = plt.axes.get_xlim()
+ self.form.xSMin.setText(str(lim[0]))
+ self.form.xSMax.setText(str(lim[1]))
+ else:
+ self.form.xSMin.setEnabled(True)
+ self.form.xSMax.setEnabled(True)
+ try:
+ xMin = float(self.form.xSMin.text())
+ except:
+ xMin = plt.axes.get_xlim()[0]
+ self.form.xSMin.setText(str(xMin))
+ try:
+ xMax = float(self.form.xSMax.text())
+ except:
+ xMax = plt.axes.get_xlim()[1]
+ self.form.xSMax.setText(str(xMax))
+ for ax in axesList:
+ ax.set_xlim(( xMin,xMax ))
+ # Y axis
+ if self.form.yAuto.isChecked():
+ for ax in axesList:
+ ax.set_autoscaley_on(True)
+ self.form.ySMin.setEnabled(False)
+ self.form.ySMax.setEnabled(False)
+ lim = plt.axes.get_ylim()
+ self.form.ySMin.setText(str(lim[0]))
+ self.form.ySMax.setText(str(lim[1]))
+ else:
+ self.form.ySMin.setEnabled(True)
+ self.form.ySMax.setEnabled(True)
+ try:
+ yMin = float(self.form.ySMin.text())
+ except:
+ yMin = plt.axes.get_ylim()[0]
+ self.form.ySMin.setText(str(yMin))
+ try:
+ yMax = float(self.form.ySMax.text())
+ except:
+ yMax = plt.axes.get_ylim()[1]
+ self.form.ySMax.setText(str(yMax))
+ for ax in axesList:
+ ax.set_ylim(( yMin,yMax ))
+ plt.update()
+ self.skip = False
+
+ def onMdiArea(self, subWin):
+ """ Executed when window is selected on mdi area.
+ @param subWin Selected window.
+ """
+ plt = Plot.getPlot()
+ if plt != subWin:
+ self.updateUI()
+
+ def updateUI(self):
+ """ Setup UI controls values if possible """
+ plt = Plot.getPlot()
+ self.form.axId.setEnabled(bool(plt))
+ self.form.new.setEnabled(bool(plt))
+ self.form.remove.setEnabled(bool(plt))
+ self.form.all.setEnabled(bool(plt))
+ self.form.xMin.setEnabled(bool(plt))
+ self.form.xMax.setEnabled(bool(plt))
+ self.form.yMin.setEnabled(bool(plt))
+ self.form.yMax.setEnabled(bool(plt))
+ self.form.xAlign.setEnabled(bool(plt))
+ self.form.yAlign.setEnabled(bool(plt))
+ self.form.xOffset.setEnabled(bool(plt))
+ self.form.yOffset.setEnabled(bool(plt))
+ self.form.xAuto.setEnabled(bool(plt))
+ self.form.yAuto.setEnabled(bool(plt))
+ self.form.xSMin.setEnabled(bool(plt))
+ self.form.xSMax.setEnabled(bool(plt))
+ self.form.ySMin.setEnabled(bool(plt))
+ self.form.ySMax.setEnabled(bool(plt))
+ if not plt:
+ return
+ # Ensure that active axes is correct
+ index = min(self.form.axId.value(), len(plt.axesList)-1)
+ self.form.axId.setValue(index)
+ # Set dimensions
+ ax = plt.axes
+ bb = ax.get_position()
+ self.form.xMin.setValue(int(100*bb._get_xmin()))
+ self.form.xMax.setValue(int(100*bb._get_xmax()))
+ self.form.yMin.setValue(int(100*bb._get_ymin()))
+ self.form.yMax.setValue(int(100*bb._get_ymax()))
+ # Set alignment and offset
+ xPos = ax.xaxis.get_ticks_position()
+ yPos = ax.yaxis.get_ticks_position()
+ xOffset = ax.spines['bottom'].get_position()[1]
+ yOffset = ax.spines['left'].get_position()[1]
+ if xPos == 'bottom' or xPos == 'default':
+ self.form.xAlign.setCurrentIndex(0)
+ else:
+ self.form.xAlign.setCurrentIndex(1)
+ self.form.xOffset.setValue(xOffset)
+ if yPos == 'left' or yPos == 'default':
+ self.form.yAlign.setCurrentIndex(0)
+ else:
+ self.form.yAlign.setCurrentIndex(1)
+ self.form.yOffset.setValue(yOffset)
+ # Set scales
+ if ax.get_autoscalex_on():
+ self.form.xAuto.setChecked(True)
+ self.form.xSMin.setEnabled(False)
+ self.form.xSMax.setEnabled(False)
+ else:
+ self.form.xAuto.setChecked(False)
+ self.form.xSMin.setEnabled(True)
+ self.form.xSMax.setEnabled(True)
+ lim = ax.get_xlim()
+ self.form.xSMin.setText(str(lim[0]))
+ self.form.xSMax.setText(str(lim[1]))
+ if ax.get_autoscaley_on():
+ self.form.yAuto.setChecked(True)
+ self.form.ySMin.setEnabled(False)
+ self.form.ySMax.setEnabled(False)
+ else:
+ self.form.yAuto.setChecked(False)
+ self.form.ySMin.setEnabled(True)
+ self.form.ySMax.setEnabled(True)
+ lim = ax.get_ylim()
+ self.form.ySMin.setText(str(lim[0]))
+ self.form.ySMax.setText(str(lim[1]))
+
+def createTask():
+ panel = TaskPanel()
+ Gui.Control.showDialog(panel)
+ if panel.setupUi():
+ Gui.Control.closeDialog(panel)
+ return None
+ return panel
diff --git a/src/Mod/Plot/plotAxes/TaskPanel.ui b/src/Mod/Plot/plotAxes/TaskPanel.ui
new file mode 100644
index 000000000..26c267af1
--- /dev/null
+++ b/src/Mod/Plot/plotAxes/TaskPanel.ui
@@ -0,0 +1,332 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 276
+ 416
+
+
+
+
+ 0
+ 336
+
+
+
+ Configure axes
+
+
+ -
+
+
+ 0
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Active axes:
+
+
+
+ -
+
+
+
+ 5
+ 0
+
+
+
+ 1
+
+
+
+ -
+
+
+
+ 2
+ 0
+
+
+
+ add
+
+
+
+ -
+
+
+
+ 2
+ 0
+
+
+
+
+ 10
+ 0
+
+
+
+ del
+
+
+
+
+
+ -
+
+
+ Apply to all axes
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+
+ 0
+ 1
+
+
+
+ 100
+
+
+ 90
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ 100
+
+
+ 90
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 100
+
+
+ 10
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 1
+
+
+
+ 100
+
+
+ 10
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Dimensions:
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
-
+
+
+ Y axis position
+
+
+ Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
+
+
+
+ -
+
+
+ 0
+
+
-
+
+ y at Left
+
+
+ -
+
+ y at Right
+
+
+
+
+ -
+
+
+ 99999
+
+
+
+
+
+ -
+
+
-
+
+
+ X axis position
+
+
+ Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
+
+
+
+ -
+
+
+ 0
+
+
-
+
+ x at bottom
+
+
+ -
+
+ x at top
+
+
+
+
+ -
+
+
+ 99999
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Scales
+
+
+
+ -
+
+
+ X auto
+
+
+
+ -
+
+
+ Y auto
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Plot/plotAxes/__init__.py b/src/Mod/Plot/plotAxes/__init__.py
new file mode 100644
index 000000000..24058d7a9
--- /dev/null
+++ b/src/Mod/Plot/plotAxes/__init__.py
@@ -0,0 +1,36 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD
+import FreeCADGui
+
+# Qt libraries
+from PyQt4 import QtGui,QtCore
+
+# Main object
+import TaskPanel
+
+def load():
+ """ Loads the tool """
+ TaskPanel.createTask()
diff --git a/src/Mod/Plot/plotLabels/TaskPanel.py b/src/Mod/Plot/plotLabels/TaskPanel.py
new file mode 100644
index 000000000..fe47acbeb
--- /dev/null
+++ b/src/Mod/Plot/plotLabels/TaskPanel.py
@@ -0,0 +1,206 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD as App
+import FreeCADGui as Gui
+# Qt library
+from PyQt4 import QtGui,QtCore
+# Module
+import Plot
+from plotUtils import Paths, Translator
+
+class TaskPanel:
+ def __init__(self):
+ self.ui = Paths.modulePath() + "/plotLabels/TaskPanel.ui"
+ self.skip = False
+
+ def accept(self):
+ return True
+
+ def reject(self):
+ return True
+
+ def clicked(self, index):
+ pass
+
+ def open(self):
+ pass
+
+ def needsFullSpace(self):
+ return True
+
+ def isAllowedAlterSelection(self):
+ return False
+
+ def isAllowedAlterView(self):
+ return True
+
+ def isAllowedAlterDocument(self):
+ return False
+
+ def helpRequested(self):
+ pass
+
+ def setupUi(self):
+ mw = self.getMainWindow()
+ form = mw.findChild(QtGui.QWidget, "TaskPanel")
+ form.axId = form.findChild(QtGui.QSpinBox, "axesIndex")
+ form.title = form.findChild(QtGui.QLineEdit, "title")
+ form.titleSize = form.findChild(QtGui.QSpinBox, "titleSize")
+ form.xLabel = form.findChild(QtGui.QLineEdit, "titleX")
+ form.xSize = form.findChild(QtGui.QSpinBox, "xSize")
+ form.yLabel = form.findChild(QtGui.QLineEdit, "titleY")
+ form.ySize = form.findChild(QtGui.QSpinBox, "ySize")
+ self.form = form
+ self.retranslateUi()
+ # Look for active axes if can
+ axId = 0
+ plt = Plot.getPlot()
+ if plt:
+ while plt.axes != plt.axesList[axId]:
+ axId = axId + 1
+ form.axId.setValue(axId)
+ self.updateUI()
+ QtCore.QObject.connect(form.axId, QtCore.SIGNAL('valueChanged(int)'),self.onAxesId)
+ QtCore.QObject.connect(form.title, QtCore.SIGNAL("editingFinished()"),self.onLabels)
+ QtCore.QObject.connect(form.xLabel, QtCore.SIGNAL("editingFinished()"),self.onLabels)
+ QtCore.QObject.connect(form.yLabel, QtCore.SIGNAL("editingFinished()"),self.onLabels)
+ QtCore.QObject.connect(form.titleSize,QtCore.SIGNAL("valueChanged(int)"),self.onFontSizes)
+ QtCore.QObject.connect(form.xSize, QtCore.SIGNAL("valueChanged(int)"),self.onFontSizes)
+ QtCore.QObject.connect(form.ySize, QtCore.SIGNAL("valueChanged(int)"),self.onFontSizes)
+ QtCore.QObject.connect(Plot.getMdiArea(),QtCore.SIGNAL("subWindowActivated(QMdiSubWindow*)"),self.onMdiArea)
+ return False
+
+ def getMainWindow(self):
+ "returns the main window"
+ # using QtGui.qApp.activeWindow() isn't very reliable because if another
+ # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
+ # returned
+ toplevel = QtGui.qApp.topLevelWidgets()
+ for i in toplevel:
+ if i.metaObject().className() == "Gui::MainWindow":
+ return i
+ raise Exception("No main window found")
+
+ def retranslateUi(self):
+ """ Set user interface locale strings.
+ """
+ self.form.setWindowTitle(Translator.translate("Set labels"))
+ self.form.findChild(QtGui.QLabel, "axesLabel").setText(Translator.translate("Active axes"))
+ self.form.findChild(QtGui.QLabel, "titleLabel").setText(Translator.translate("Title"))
+ self.form.findChild(QtGui.QLabel, "xLabel").setText(Translator.translate("X label"))
+ self.form.findChild(QtGui.QLabel, "yLabel").setText(Translator.translate("Y label"))
+
+ def onAxesId(self, value):
+ """ Executed when axes index is modified. """
+ if not self.skip:
+ self.skip = True
+ # UI control in some special plot cases
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ self.skip = False
+ return
+ # UI control in most cases
+ self.form.axId.setMaximum(len(plt.axesList))
+ if self.form.axId.value() >= len(plt.axesList):
+ self.form.axId.setValue(len(plt.axesList)-1)
+ # Send new control to Plot instance
+ plt.setActiveAxes(self.form.axId.value())
+ self.updateUI()
+ self.skip = False
+
+ def onLabels(self):
+ """ Executed when labels have been modified. """
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ # Set labels
+ Plot.title(unicode(self.form.title.text()))
+ Plot.xlabel(unicode(self.form.xLabel.text()))
+ Plot.ylabel(unicode(self.form.yLabel.text()))
+ plt.update()
+
+ def onFontSizes(self, value):
+ """ Executed when font sizes have been modified. """
+ # Get apply environment
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ # Set font sizes
+ ax = plt.axes
+ ax.title.set_fontsize(self.form.titleSize.value())
+ ax.xaxis.label.set_fontsize(self.form.xSize.value())
+ ax.yaxis.label.set_fontsize(self.form.ySize.value())
+ plt.update()
+
+ def onMdiArea(self, subWin):
+ """ Executed when window is selected on mdi area.
+ @param subWin Selected window.
+ """
+ plt = Plot.getPlot()
+ if plt != subWin:
+ self.updateUI()
+
+ def updateUI(self):
+ """ Setup UI controls values if possible """
+ plt = Plot.getPlot()
+ self.form.axId.setEnabled(bool(plt))
+ self.form.title.setEnabled(bool(plt))
+ self.form.titleSize.setEnabled(bool(plt))
+ self.form.xLabel.setEnabled(bool(plt))
+ self.form.xSize.setEnabled(bool(plt))
+ self.form.yLabel.setEnabled(bool(plt))
+ self.form.ySize.setEnabled(bool(plt))
+ if not plt:
+ return
+ # Ensure that active axes is correct
+ index = min(self.form.axId.value(), len(plt.axesList)-1)
+ self.form.axId.setValue(index)
+ # Store data before starting changing it.
+ ax = plt.axes
+ t = ax.get_title()
+ x = ax.get_xlabel()
+ y = ax.get_ylabel()
+ tt = ax.title.get_fontsize()
+ xx = ax.xaxis.label.get_fontsize()
+ yy = ax.yaxis.label.get_fontsize()
+ # Set labels
+ self.form.title.setText(t)
+ self.form.xLabel.setText(x)
+ self.form.yLabel.setText(y)
+ # Set font sizes
+ self.form.titleSize.setValue(tt)
+ self.form.xSize.setValue(xx)
+ self.form.ySize.setValue(yy)
+
+def createTask():
+ panel = TaskPanel()
+ Gui.Control.showDialog(panel)
+ if panel.setupUi():
+ Gui.Control.closeDialog(panel)
+ return None
+ return panel
diff --git a/src/Mod/Plot/plotLabels/TaskPanel.ui b/src/Mod/Plot/plotLabels/TaskPanel.ui
new file mode 100644
index 000000000..203816390
--- /dev/null
+++ b/src/Mod/Plot/plotLabels/TaskPanel.ui
@@ -0,0 +1,134 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 276
+ 228
+
+
+
+
+ 0
+ 0
+
+
+
+ Set labels
+
+
+ -
+
+
+ 0
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Active axes:
+
+
+
+ -
+
+
+
+ 5
+ 0
+
+
+
+ 1
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ -
+
+
+ Title
+
+
+
+ -
+
+
+ 1
+
+
+ 1024
+
+
+
+ -
+
+
+ 1
+
+
+ 1024
+
+
+
+ -
+
+
+ X label
+
+
+
+ -
+
+
+ -
+
+
+ Y label
+
+
+
+ -
+
+
+ -
+
+
+ 1
+
+
+ 1024
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Plot/plotLabels/__init__.py b/src/Mod/Plot/plotLabels/__init__.py
new file mode 100644
index 000000000..24058d7a9
--- /dev/null
+++ b/src/Mod/Plot/plotLabels/__init__.py
@@ -0,0 +1,36 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD
+import FreeCADGui
+
+# Qt libraries
+from PyQt4 import QtGui,QtCore
+
+# Main object
+import TaskPanel
+
+def load():
+ """ Loads the tool """
+ TaskPanel.createTask()
diff --git a/src/Mod/Plot/plotPositions/TaskPanel.py b/src/Mod/Plot/plotPositions/TaskPanel.py
new file mode 100644
index 000000000..a5737d396
--- /dev/null
+++ b/src/Mod/Plot/plotPositions/TaskPanel.py
@@ -0,0 +1,223 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD as App
+import FreeCADGui as Gui
+# Qt library
+from PyQt4 import QtGui,QtCore
+# Module
+import Plot
+from plotUtils import Paths, Translator
+
+class TaskPanel:
+ def __init__(self):
+ self.ui = Paths.modulePath() + "/plotPositions/TaskPanel.ui"
+ self.skip = False
+ self.item = 0
+ self.names = []
+ self.objs = []
+ self.plt = None
+
+ def accept(self):
+ return True
+
+ def reject(self):
+ return True
+
+ def clicked(self, index):
+ pass
+
+ def open(self):
+ pass
+
+ def needsFullSpace(self):
+ return True
+
+ def isAllowedAlterSelection(self):
+ return False
+
+ def isAllowedAlterView(self):
+ return True
+
+ def isAllowedAlterDocument(self):
+ return False
+
+ def helpRequested(self):
+ pass
+
+ def setupUi(self):
+ mw = self.getMainWindow()
+ form = mw.findChild(QtGui.QWidget, "TaskPanel")
+ form.items = form.findChild(QtGui.QListWidget, "items")
+ form.x = form.findChild(QtGui.QDoubleSpinBox, "x")
+ form.y = form.findChild(QtGui.QDoubleSpinBox, "y")
+ form.s = form.findChild(QtGui.QDoubleSpinBox, "size")
+ self.form = form
+ self.retranslateUi()
+ self.updateUI()
+ QtCore.QObject.connect(form.items, QtCore.SIGNAL("currentRowChanged(int)"),self.onItem)
+ QtCore.QObject.connect(form.x, QtCore.SIGNAL("valueChanged(double)"),self.onData)
+ QtCore.QObject.connect(form.y, QtCore.SIGNAL("valueChanged(double)"),self.onData)
+ QtCore.QObject.connect(form.s, QtCore.SIGNAL("valueChanged(double)"),self.onData)
+ QtCore.QObject.connect(Plot.getMdiArea(),QtCore.SIGNAL("subWindowActivated(QMdiSubWindow*)"),self.onMdiArea)
+ return False
+
+ def getMainWindow(self):
+ "returns the main window"
+ # using QtGui.qApp.activeWindow() isn't very reliable because if another
+ # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
+ # returned
+ toplevel = QtGui.qApp.topLevelWidgets()
+ for i in toplevel:
+ if i.metaObject().className() == "Gui::MainWindow":
+ return i
+ raise Exception("No main window found")
+
+ def retranslateUi(self):
+ """ Set user interface locale strings.
+ """
+ self.form.setWindowTitle(Translator.translate("Set positions and sizes"))
+ self.form.findChild(QtGui.QLabel, "posLabel").setText(Translator.translate("Position"))
+ self.form.findChild(QtGui.QLabel, "sizeLabel").setText(Translator.translate("Size"))
+
+ def onItem(self, row):
+ """ Executed when selected item is modified. """
+ # Get selected item
+ self.item = row
+ # Call to update
+ self.updateUI()
+
+ def onData(self, value):
+ """ Executed when selected item data is modified. """
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ if not self.skip:
+ self.skip = True
+ name = self.names[self.item]
+ obj = self.objs[self.item]
+ x = self.form.x.value()
+ y = self.form.y.value()
+ s = self.form.s.value()
+ # x/y labels only have one position control
+ if name.find('x label') >= 0:
+ self.form.y.setValue(x)
+ elif name.find('y label') >= 0:
+ self.form.x.setValue(y)
+ # title and labels only have one size control
+ if name.find('title') >= 0 or name.find('label') >= 0:
+ obj.set_position((x,y))
+ obj.set_size(s)
+ # legend have all controls
+ else:
+ Plot.legend(plt.legend, (x,y), s)
+ plt.update()
+ self.skip = False
+
+ def onMdiArea(self, subWin):
+ """ Executed when window is selected on mdi area.
+ @param subWin Selected window.
+ """
+ plt = Plot.getPlot()
+ if plt != subWin:
+ self.updateUI()
+
+ def updateUI(self):
+ """ Setup UI controls values if possible """
+ plt = Plot.getPlot()
+ self.form.items.setEnabled(bool(plt))
+ self.form.x.setEnabled(bool(plt))
+ self.form.y.setEnabled(bool(plt))
+ self.form.s.setEnabled(bool(plt))
+ if not plt:
+ self.plt = plt
+ self.form.items.clear()
+ return
+ # Refill items list only if Plot instance have been changed
+ if self.plt != plt:
+ self.plt = plt
+ self.plt.update() # Update plot in order to put legend in correct place
+ self.setList()
+ # Get data for controls
+ name = self.names[self.item]
+ obj = self.objs[self.item]
+ if name.find('title') >= 0 or name.find('label') >= 0:
+ p = obj.get_position()
+ x = p[0]
+ y = p[1]
+ s = obj.get_size()
+ if name.find('x label') >= 0:
+ self.form.y.setEnabled(False)
+ self.form.y.setValue(x)
+ elif name.find('y label') >= 0:
+ self.form.x.setEnabled(False)
+ self.form.x.setValue(y)
+ else:
+ x = plt.legPos[0]
+ y = plt.legPos[1]
+ s = obj.get_texts()[-1].get_fontsize()
+ # Send it to controls
+ self.form.x.setValue(x)
+ self.form.y.setValue(y)
+ self.form.s.setValue(s)
+
+ def setList(self):
+ """ Setup UI controls values if possible """
+ # Clear lists
+ self.names = []
+ self.objs = []
+ # Fill lists with available objects
+ if self.plt:
+ # Axes data
+ for i in range(0,len(self.plt.axesList)):
+ ax = self.plt.axesList[i]
+ # Each axes have title, xaxis and yaxis
+ self.names.append('title (axes %d)' % (i))
+ self.objs.append(ax.title)
+ self.names.append('x label (axes %d)' % (i))
+ self.objs.append(ax.xaxis.get_label())
+ self.names.append('y label (axes %d)' % (i))
+ self.objs.append(ax.yaxis.get_label())
+ # Legend if exist
+ ax = self.plt.axesList[-1]
+ if ax.legend_:
+ self.names.append('legend')
+ self.objs.append(ax.legend_)
+ # Send list to widget
+ self.form.items.clear()
+ for name in self.names:
+ self.form.items.addItem(name)
+ # Ensure that selected item is correct
+ if self.item >= len(self.names):
+ self.item = len(self.names)-1
+ self.form.items.setCurrentIndex(self.item)
+
+def createTask():
+ panel = TaskPanel()
+ Gui.Control.showDialog(panel)
+ if panel.setupUi():
+ Gui.Control.closeDialog(panel)
+ return None
+ return panel
diff --git a/src/Mod/Plot/plotPositions/TaskPanel.ui b/src/Mod/Plot/plotPositions/TaskPanel.ui
new file mode 100644
index 000000000..18cc7ac03
--- /dev/null
+++ b/src/Mod/Plot/plotPositions/TaskPanel.ui
@@ -0,0 +1,107 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 296
+ 336
+
+
+
+
+ 0
+ 336
+
+
+
+ Set positions and sizes
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+ Position
+
+
+
+ -
+
+
+ 3
+
+
+ -99999.000000000000000
+
+
+ 99999.000000000000000
+
+
+ 0.010000000000000
+
+
+
+ -
+
+
+ 3
+
+
+ -99999.000000000000000
+
+
+ 99999.000000000000000
+
+
+ 0.010000000000000
+
+
+
+ -
+
+
+ Size
+
+
+
+ -
+
+
+ 1
+
+
+ 0.000000000000000
+
+
+ 99999.000000000000000
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Plot/plotPositions/__init__.py b/src/Mod/Plot/plotPositions/__init__.py
new file mode 100644
index 000000000..24058d7a9
--- /dev/null
+++ b/src/Mod/Plot/plotPositions/__init__.py
@@ -0,0 +1,36 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD
+import FreeCADGui
+
+# Qt libraries
+from PyQt4 import QtGui,QtCore
+
+# Main object
+import TaskPanel
+
+def load():
+ """ Loads the tool """
+ TaskPanel.createTask()
diff --git a/src/Mod/Plot/plotSave/TaskPanel.py b/src/Mod/Plot/plotSave/TaskPanel.py
new file mode 100644
index 000000000..f545d9ff5
--- /dev/null
+++ b/src/Mod/Plot/plotSave/TaskPanel.py
@@ -0,0 +1,149 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+import os
+# FreeCAD modules
+import FreeCAD as App
+import FreeCADGui as Gui
+# Qt library
+from PyQt4 import QtGui,QtCore
+# Module
+import Plot
+from plotUtils import Paths, Translator
+
+class TaskPanel:
+ def __init__(self):
+ self.ui = Paths.modulePath() + "/plotSave/TaskPanel.ui"
+
+ def accept(self):
+ plt = Plot.getPlot()
+ if not plt:
+ msg = Translator.translate("Plot document must be selected in order to save it.")
+ App.Console.PrintError(msg+"\n")
+ return False
+ path = unicode(self.form.path.text())
+ size = (self.form.sizeX.value(), self.form.sizeY.value())
+ dpi = self.form.dpi.value()
+ Plot.save(path, size, dpi)
+ return True
+
+ def reject(self):
+ return True
+
+ def clicked(self, index):
+ pass
+
+ def open(self):
+ pass
+
+ def needsFullSpace(self):
+ return True
+
+ def isAllowedAlterSelection(self):
+ return False
+
+ def isAllowedAlterView(self):
+ return True
+
+ def isAllowedAlterDocument(self):
+ return False
+
+ def helpRequested(self):
+ pass
+
+ def setupUi(self):
+ mw = self.getMainWindow()
+ form = mw.findChild(QtGui.QWidget, "TaskPanel")
+ form.path = form.findChild(QtGui.QLineEdit, "path")
+ form.pathButton = form.findChild(QtGui.QPushButton, "pathButton")
+ form.sizeX = form.findChild(QtGui.QDoubleSpinBox, "sizeX")
+ form.sizeY = form.findChild(QtGui.QDoubleSpinBox, "sizeY")
+ form.dpi = form.findChild(QtGui.QSpinBox, "dpi")
+ self.form = form
+ self.retranslateUi()
+ QtCore.QObject.connect(form.pathButton,QtCore.SIGNAL("pressed()"),self.onPathButton)
+ QtCore.QObject.connect(Plot.getMdiArea(),QtCore.SIGNAL("subWindowActivated(QMdiSubWindow*)"),self.onMdiArea)
+ home = os.getenv('USERPROFILE') or os.getenv('HOME')
+ form.path.setText(os.path.join(home,"plot.png"))
+ self.updateUI()
+ return False
+
+ def getMainWindow(self):
+ "returns the main window"
+ # using QtGui.qApp.activeWindow() isn't very reliable because if another
+ # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
+ # returned
+ toplevel = QtGui.qApp.topLevelWidgets()
+ for i in toplevel:
+ if i.metaObject().className() == "Gui::MainWindow":
+ return i
+ raise Exception("No main window found")
+
+ def retranslateUi(self):
+ """ Set user interface locale strings.
+ """
+ self.form.setWindowTitle(Translator.translate("Save figure"))
+ self.form.findChild(QtGui.QLabel, "sizeLabel").setText(Translator.translate("Inches"))
+ self.form.findChild(QtGui.QLabel, "dpiLabel").setText(Translator.translate("Dots per Inch"))
+
+ def updateUI(self):
+ """ Setup UI controls values if possible """
+ plt = Plot.getPlot()
+ self.form.path.setEnabled(bool(plt))
+ self.form.pathButton.setEnabled(bool(plt))
+ self.form.sizeX.setEnabled(bool(plt))
+ self.form.sizeY.setEnabled(bool(plt))
+ self.form.dpi.setEnabled(bool(plt))
+ if not plt:
+ return
+ fig = plt.fig
+ size = fig.get_size_inches()
+ dpi = fig.get_dpi()
+ self.form.sizeX.setValue(size[0])
+ self.form.sizeY.setValue(size[1])
+ self.form.dpi.setValue(dpi)
+
+ def onPathButton(self):
+ """ Executed when path button is pressed.
+ """
+ path = self.form.path.text()
+ file_choices = "Portable Network Graphics (*.png)|*.png;;Portable Document Format (*.pdf)|*.pdf;;PostScript (*.ps)|*.ps;;Encapsulated PostScript (*.eps)|*.eps"
+ path = QtGui.QFileDialog.getSaveFileName(None, 'Save figure', path, file_choices)
+ if path:
+ self.form.path.setText(path)
+
+ def onMdiArea(self, subWin):
+ """ Executed when window is selected on mdi area.
+ @param subWin Selected window.
+ """
+ plt = Plot.getPlot()
+ if plt != subWin:
+ self.updateUI()
+
+def createTask():
+ panel = TaskPanel()
+ Gui.Control.showDialog(panel)
+ if panel.setupUi():
+ Gui.Control.closeDialog(panel)
+ return None
+ return panel
diff --git a/src/Mod/Plot/plotSave/TaskPanel.ui b/src/Mod/Plot/plotSave/TaskPanel.ui
new file mode 100644
index 000000000..9bc79fc3e
--- /dev/null
+++ b/src/Mod/Plot/plotSave/TaskPanel.ui
@@ -0,0 +1,141 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 260
+ 253
+
+
+
+ Save figure
+
+
+ -
+
+
-
+
+
-
+
+
+
+ 7
+ 0
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 1
+ 0
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
-
+
+
+ 0.010000000000000
+
+
+ 99999.000000000000000
+
+
+ 6.400000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ x
+
+
+
+ -
+
+
+ 0.010000000000000
+
+
+ 99999.000000000000000
+
+
+ 4.800000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Inches
+
+
+
+
+
+ -
+
+
-
+
+
+ 1
+
+
+ 2048
+
+
+ 100
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Dots per Inch
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Plot/plotSave/__init__.py b/src/Mod/Plot/plotSave/__init__.py
new file mode 100644
index 000000000..24058d7a9
--- /dev/null
+++ b/src/Mod/Plot/plotSave/__init__.py
@@ -0,0 +1,36 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD
+import FreeCADGui
+
+# Qt libraries
+from PyQt4 import QtGui,QtCore
+
+# Main object
+import TaskPanel
+
+def load():
+ """ Loads the tool """
+ TaskPanel.createTask()
diff --git a/src/Mod/Plot/plotSeries/TaskPanel.py b/src/Mod/Plot/plotSeries/TaskPanel.py
new file mode 100644
index 000000000..3d129ebf7
--- /dev/null
+++ b/src/Mod/Plot/plotSeries/TaskPanel.py
@@ -0,0 +1,309 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD as App
+import FreeCADGui as Gui
+# Qt library
+from PyQt4 import QtGui,QtCore
+# Module
+import Plot
+from plotUtils import Paths, Translator
+# matplotlib
+import matplotlib
+from matplotlib.lines import Line2D
+import matplotlib.colors as Colors
+
+class TaskPanel:
+ def __init__(self):
+ self.ui = Paths.modulePath() + "/plotSeries/TaskPanel.ui"
+ self.skip = False
+ self.item = 0
+ self.plt = None
+
+ def accept(self):
+ return True
+
+ def reject(self):
+ return True
+
+ def clicked(self, index):
+ pass
+
+ def open(self):
+ pass
+
+ def needsFullSpace(self):
+ return True
+
+ def isAllowedAlterSelection(self):
+ return False
+
+ def isAllowedAlterView(self):
+ return True
+
+ def isAllowedAlterDocument(self):
+ return False
+
+ def helpRequested(self):
+ pass
+
+ def setupUi(self):
+ mw = self.getMainWindow()
+ form = mw.findChild(QtGui.QWidget, "TaskPanel")
+ form.items = form.findChild(QtGui.QListWidget, "items")
+ form.label = form.findChild(QtGui.QLineEdit, "label")
+ form.isLabel = form.findChild(QtGui.QCheckBox, "isLabel")
+ form.style = form.findChild(QtGui.QComboBox, "lineStyle")
+ form.marker = form.findChild(QtGui.QComboBox, "markers")
+ form.width = form.findChild(QtGui.QDoubleSpinBox, "lineWidth")
+ form.size = form.findChild(QtGui.QSpinBox, "markerSize")
+ form.color = form.findChild(QtGui.QPushButton, "color")
+ form.remove = form.findChild(QtGui.QPushButton, "remove")
+ self.form = form
+ self.retranslateUi()
+ self.fillStyles()
+ self.updateUI()
+ QtCore.QObject.connect(form.items, QtCore.SIGNAL("currentRowChanged(int)"),self.onItem)
+ QtCore.QObject.connect(form.label, QtCore.SIGNAL("editingFinished()"),self.onData)
+ QtCore.QObject.connect(form.isLabel,QtCore.SIGNAL("stateChanged(int)"),self.onData)
+ QtCore.QObject.connect(form.style, QtCore.SIGNAL("currentIndexChanged(int)"),self.onData)
+ QtCore.QObject.connect(form.marker, QtCore.SIGNAL("currentIndexChanged(int)"),self.onData)
+ QtCore.QObject.connect(form.width, QtCore.SIGNAL("valueChanged(double)"),self.onData)
+ QtCore.QObject.connect(form.size, QtCore.SIGNAL("valueChanged(int)"),self.onData)
+ QtCore.QObject.connect(form.color, QtCore.SIGNAL("pressed()"),self.onColor)
+ QtCore.QObject.connect(form.remove, QtCore.SIGNAL("pressed()"),self.onRemove)
+ QtCore.QObject.connect(Plot.getMdiArea(),QtCore.SIGNAL("subWindowActivated(QMdiSubWindow*)"),self.onMdiArea)
+ return False
+
+ def getMainWindow(self):
+ "returns the main window"
+ # using QtGui.qApp.activeWindow() isn't very reliable because if another
+ # widget than the mainwindow is active (e.g. a dialog) the wrong widget is
+ # returned
+ toplevel = QtGui.qApp.topLevelWidgets()
+ for i in toplevel:
+ if i.metaObject().className() == "Gui::MainWindow":
+ return i
+ raise Exception("No main window found")
+
+ def retranslateUi(self):
+ """ Set user interface locale strings.
+ """
+ self.form.setWindowTitle(Translator.translate("Set positions and sizes"))
+ self.form.isLabel.setText(Translator.translate("No label"))
+ self.form.remove.setText(Translator.translate("Remove serie"))
+ self.form.findChild(QtGui.QLabel, "styleLabel").setText(Translator.translate("Line style"))
+ self.form.findChild(QtGui.QLabel, "markerLabel").setText(Translator.translate("Marker"))
+
+ def fillStyles(self):
+ """ Fill style combo boxes. """
+ # Line styles
+ linestyles = Line2D.lineStyles.keys()
+ for i in range(0,len(linestyles)):
+ style = linestyles[i]
+ string = "\'" + str(style) + "\' (" + Line2D.lineStyles[style] + ")"
+ self.form.style.addItem(string)
+ # Markers
+ markers = Line2D.markers.keys()
+ for i in range(0,len(markers)):
+ marker = markers[i]
+ string = "\'" + str(marker) + "\' (" + Line2D.markers[marker] + ")"
+ self.form.marker.addItem(string)
+
+ def onItem(self, row):
+ """ Executed when selected item is modified. """
+ if not self.skip:
+ self.skip = True
+ # Get selected item
+ self.item = row
+ # Call to update
+ self.updateUI()
+ self.skip = False
+
+ def onData(self):
+ """ Executed when selected item data is modified. """
+ if not self.skip:
+ self.skip = True
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ # Ensure that selected serie exist
+ if self.item >= len(Plot.series()):
+ self.updateUI()
+ return
+ # Set label
+ serie = Plot.series()[self.item]
+ if(self.form.isLabel.isChecked()):
+ serie.name = None
+ self.form.label.setEnabled(False)
+ else:
+ serie.name = self.form.label.text()
+ self.form.label.setEnabled(True)
+ # Set line style and marker
+ style = self.form.style.currentIndex()
+ linestyles = Line2D.lineStyles.keys()
+ serie.line.set_linestyle(linestyles[style])
+ marker = self.form.marker.currentIndex()
+ markers = Line2D.markers.keys()
+ serie.line.set_marker(markers[marker])
+ # Set line width and marker size
+ serie.line.set_linewidth(self.form.width.value())
+ serie.line.set_markersize(self.form.size.value())
+ plt.update()
+ # Regenerate series labels
+ self.setList()
+ self.skip = False
+
+ def onColor(self):
+ """ Executed when color pallete is requested. """
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ # Ensure that selected serie exist
+ if self.item >= len(Plot.series()):
+ self.updateUI()
+ return
+ # Show widget to select color
+ col = QtGui.QColorDialog.getColor()
+ # Send color to widget and serie
+ if col.isValid():
+ serie = plt.series[self.item]
+ self.form.color.setStyleSheet("background-color: rgb(%d, %d, %d);" % (col.red(),
+ col.green(), col.blue()))
+ serie.line.set_color((col.redF(), col.greenF(), col.blueF()))
+ plt.update()
+
+ def onRemove(self):
+ """ Executed when data serie must be removed. """
+ plt = Plot.getPlot()
+ if not plt:
+ self.updateUI()
+ return
+ # Ensure that selected serie exist
+ if self.item >= len(Plot.series()):
+ self.updateUI()
+ return
+ # Remove serie
+ Plot.removeSerie(self.item)
+ self.setList()
+ self.updateUI()
+ plt.update()
+
+ def onMdiArea(self, subWin):
+ """ Executed when window is selected on mdi area.
+ @param subWin Selected window.
+ """
+ plt = Plot.getPlot()
+ if plt != subWin:
+ self.updateUI()
+
+ def updateUI(self):
+ """ Setup UI controls values if possible """
+ plt = Plot.getPlot()
+ self.form.items.setEnabled(bool(plt))
+ self.form.label.setEnabled(bool(plt))
+ self.form.isLabel.setEnabled(bool(plt))
+ self.form.style.setEnabled(bool(plt))
+ self.form.marker.setEnabled(bool(plt))
+ self.form.width.setEnabled(bool(plt))
+ self.form.size.setEnabled(bool(plt))
+ self.form.color.setEnabled(bool(plt))
+ self.form.remove.setEnabled(bool(plt))
+ if not plt:
+ self.plt = plt
+ self.form.items.clear()
+ return
+ self.skip = True
+ # Refill list
+ if self.plt != plt or len(Plot.series()) != self.form.items.count():
+ self.plt = plt
+ self.setList()
+ # Ensure that have series
+ if not len(Plot.series()):
+ self.form.label.setEnabled(False)
+ self.form.isLabel.setEnabled(False)
+ self.form.style.setEnabled(False)
+ self.form.marker.setEnabled(False)
+ self.form.width.setEnabled(False)
+ self.form.size.setEnabled(False)
+ self.form.color.setEnabled(False)
+ self.form.remove.setEnabled(False)
+ return
+ # Set label
+ serie = Plot.series()[self.item]
+ if serie.name == None:
+ self.form.isLabel.setChecked(True)
+ self.form.label.setEnabled(False)
+ self.form.label.setText("")
+ else:
+ self.form.isLabel.setChecked(False)
+ self.form.label.setText(serie.name)
+ # Set line style and marker
+ self.form.style.setCurrentIndex(0)
+ linestyles = Line2D.lineStyles.keys()
+ for i in range(0,len(linestyles)):
+ style = linestyles[i]
+ if style == serie.line.get_linestyle():
+ self.form.style.setCurrentIndex(i)
+ self.form.marker.setCurrentIndex(0)
+ markers = Line2D.markers.keys()
+ for i in range(0,len(markers)):
+ marker = markers[i]
+ if marker == serie.line.get_marker():
+ self.form.marker.setCurrentIndex(i)
+ # Set line width and marker size
+ self.form.width.setValue(serie.line.get_linewidth())
+ self.form.size.setValue(serie.line.get_markersize())
+ # Set color
+ color = Colors.colorConverter.to_rgb(serie.line.get_color())
+ self.form.color.setStyleSheet("background-color: rgb(%d, %d, %d);" % (int(color[0]*255),
+ int(color[1]*255), int(color[2]*255)))
+ self.skip = False
+
+ def setList(self):
+ """ Setup UI controls values if possible """
+ self.form.items.clear()
+ series = Plot.series()
+ for i in range(0,len(series)):
+ serie = series[i]
+ string = 'serie ' + str(i) + ': '
+ if serie.name == None:
+ string = string + '\"No label\"'
+ else:
+ string = string + serie.name
+ self.form.items.addItem(string)
+ # Ensure that selected item is correct
+ if len(series) and self.item >= len(series):
+ self.item = len(series)-1
+ self.form.items.setCurrentIndex(self.item)
+
+def createTask():
+ panel = TaskPanel()
+ Gui.Control.showDialog(panel)
+ if panel.setupUi():
+ Gui.Control.closeDialog(panel)
+ return None
+ return panel
diff --git a/src/Mod/Plot/plotSeries/TaskPanel.ui b/src/Mod/Plot/plotSeries/TaskPanel.ui
new file mode 100644
index 000000000..3fbde165a
--- /dev/null
+++ b/src/Mod/Plot/plotSeries/TaskPanel.ui
@@ -0,0 +1,154 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 296
+ 336
+
+
+
+
+ 0
+ 336
+
+
+
+ Configure series
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+
+ 2
+ 0
+
+
+
+
+ -
+
+
+ 0.010000000000000
+
+
+ 9999.000000000000000
+
+
+ 0.500000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Line style
+
+
+
+ -
+
+
+ Remove serie
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+ Markers
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ No label
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ 1
+
+
+ 9999
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Plot/plotSeries/__init__.py b/src/Mod/Plot/plotSeries/__init__.py
new file mode 100644
index 000000000..24058d7a9
--- /dev/null
+++ b/src/Mod/Plot/plotSeries/__init__.py
@@ -0,0 +1,36 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# FreeCAD modules
+import FreeCAD
+import FreeCADGui
+
+# Qt libraries
+from PyQt4 import QtGui,QtCore
+
+# Main object
+import TaskPanel
+
+def load():
+ """ Loads the tool """
+ TaskPanel.createTask()
diff --git a/src/Mod/Plot/plotUtils/Paths.py b/src/Mod/Plot/plotUtils/Paths.py
new file mode 100644
index 000000000..8549681c7
--- /dev/null
+++ b/src/Mod/Plot/plotUtils/Paths.py
@@ -0,0 +1,55 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+import FreeCAD, FreeCADGui, os
+
+def modulePath():
+ """returns the current Ship design module path
+ @return Module path"""
+ path1 = FreeCAD.ConfigGet("AppHomePath") + "Mod/Plot"
+ path2 = FreeCAD.ConfigGet("UserAppData") + "Mod/Plot"
+ if os.path.exists(path2):
+ return path2
+ else:
+ return path1
+
+def iconsPath():
+ """returns the current Ship design module icons path
+ @return Icons path"""
+ path = modulePath() + "/Icons"
+ return path
+
+def getPathFromFile(fileName):
+ """ Gets the directory path from a file name
+ @param fileName Name of the file
+ @return Directory path.
+ """
+ if not fileName:
+ return ''
+ i = 1
+ try:
+ while 1:
+ i = fileName.index("/", i+1)
+ except ValueError:
+ pass
+ return fileName[0:i+1]
diff --git a/src/Mod/Plot/plotUtils/Translator.py b/src/Mod/Plot/plotUtils/Translator.py
new file mode 100644
index 000000000..fc6b562d5
--- /dev/null
+++ b/src/Mod/Plot/plotUtils/Translator.py
@@ -0,0 +1,30 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+import FreeCAD, FreeCADGui, os
+from PyQt4 import QtCore,QtGui
+
+def translate(text,context="plot"):
+ "convenience function for Qt translator"
+ return QtGui.QApplication.translate(context, text, None,
+ QtGui.QApplication.UnicodeUTF8)
diff --git a/src/Mod/Plot/plotUtils/__init__.py b/src/Mod/Plot/plotUtils/__init__.py
new file mode 100644
index 000000000..00b200f14
--- /dev/null
+++ b/src/Mod/Plot/plotUtils/__init__.py
@@ -0,0 +1,25 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program 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. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+# Empty file to treat the folder as a package
+
diff --git a/src/WindowsInstaller/FreeCAD.wxs b/src/WindowsInstaller/FreeCAD.wxs
index 8a3a8833c..0caaa311c 100644
--- a/src/WindowsInstaller/FreeCAD.wxs
+++ b/src/WindowsInstaller/FreeCAD.wxs
@@ -181,6 +181,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/WindowsInstaller/FreeCADModules.wxs b/src/WindowsInstaller/FreeCADModules.wxs
index cc820ca32..e0698d53d 100644
--- a/src/WindowsInstaller/FreeCADModules.wxs
+++ b/src/WindowsInstaller/FreeCADModules.wxs
@@ -54,6 +54,7 @@
+
diff --git a/src/WindowsInstaller/ModPlot.wxi b/src/WindowsInstaller/ModPlot.wxi
new file mode 100644
index 000000000..1565aae3b
--- /dev/null
+++ b/src/WindowsInstaller/ModPlot.wxi
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+