From 919659a21c9b5d070111a08988fb251395d3cfc6 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Sun, 28 Oct 2012 19:54:09 +0100 Subject: [PATCH 1/4] Re-Created branch form 0, to avoid conflicts --- CMakeLists.txt | 9 + cMake/FindMatplotlib.cmake | 48 ++ configure.ac | 17 + package/debian/control | 2 +- src/Mod/CMakeLists.txt | 3 + src/Mod/Makefile.am | 2 + src/Mod/Plot/CMakeLists.txt | 119 ++++ src/Mod/Plot/Icons/Axes.svg | 451 +++++++++++++++ src/Mod/Plot/Icons/Grid.svg | 407 ++++++++++++++ src/Mod/Plot/Icons/Icon.svg | 403 ++++++++++++++ src/Mod/Plot/Icons/Labels.svg | 705 ++++++++++++++++++++++++ src/Mod/Plot/Icons/Legend.svg | 679 +++++++++++++++++++++++ src/Mod/Plot/Icons/Positions.svg | 702 +++++++++++++++++++++++ src/Mod/Plot/Icons/Save.svg | 701 +++++++++++++++++++++++ src/Mod/Plot/Icons/Series.svg | 457 +++++++++++++++ src/Mod/Plot/InitGui.py | 50 ++ src/Mod/Plot/Makefile.am | 45 ++ src/Mod/Plot/Plot.py | 314 +++++++++++ src/Mod/Plot/PlotGui.py | 132 +++++ src/Mod/Plot/README | 10 + src/Mod/Plot/plot.dox | 3 + src/Mod/Plot/plotAxes/TaskPanel.py | 425 ++++++++++++++ src/Mod/Plot/plotAxes/TaskPanel.ui | 332 +++++++++++ src/Mod/Plot/plotAxes/__init__.py | 36 ++ src/Mod/Plot/plotLabels/TaskPanel.py | 206 +++++++ src/Mod/Plot/plotLabels/TaskPanel.ui | 134 +++++ src/Mod/Plot/plotLabels/__init__.py | 36 ++ src/Mod/Plot/plotPositions/TaskPanel.py | 223 ++++++++ src/Mod/Plot/plotPositions/TaskPanel.ui | 107 ++++ src/Mod/Plot/plotPositions/__init__.py | 36 ++ src/Mod/Plot/plotSave/TaskPanel.py | 149 +++++ src/Mod/Plot/plotSave/TaskPanel.ui | 141 +++++ src/Mod/Plot/plotSave/__init__.py | 36 ++ src/Mod/Plot/plotSeries/TaskPanel.py | 309 +++++++++++ src/Mod/Plot/plotSeries/TaskPanel.ui | 154 ++++++ src/Mod/Plot/plotSeries/__init__.py | 36 ++ src/Mod/Plot/plotUtils/Paths.py | 55 ++ src/Mod/Plot/plotUtils/Translator.py | 30 + src/Mod/Plot/plotUtils/__init__.py | 25 + src/WindowsInstaller/FreeCAD.wxs | 9 + src/WindowsInstaller/FreeCADModules.wxs | 1 + src/WindowsInstaller/ModPlot.wxi | 86 +++ 42 files changed, 7824 insertions(+), 1 deletion(-) create mode 100644 cMake/FindMatplotlib.cmake create mode 100644 src/Mod/Plot/CMakeLists.txt create mode 100644 src/Mod/Plot/Icons/Axes.svg create mode 100755 src/Mod/Plot/Icons/Grid.svg create mode 100755 src/Mod/Plot/Icons/Icon.svg create mode 100644 src/Mod/Plot/Icons/Labels.svg create mode 100644 src/Mod/Plot/Icons/Legend.svg create mode 100644 src/Mod/Plot/Icons/Positions.svg create mode 100755 src/Mod/Plot/Icons/Save.svg create mode 100644 src/Mod/Plot/Icons/Series.svg create mode 100644 src/Mod/Plot/InitGui.py create mode 100644 src/Mod/Plot/Makefile.am create mode 100644 src/Mod/Plot/Plot.py create mode 100644 src/Mod/Plot/PlotGui.py create mode 100644 src/Mod/Plot/README create mode 100644 src/Mod/Plot/plot.dox create mode 100644 src/Mod/Plot/plotAxes/TaskPanel.py create mode 100644 src/Mod/Plot/plotAxes/TaskPanel.ui create mode 100644 src/Mod/Plot/plotAxes/__init__.py create mode 100644 src/Mod/Plot/plotLabels/TaskPanel.py create mode 100644 src/Mod/Plot/plotLabels/TaskPanel.ui create mode 100644 src/Mod/Plot/plotLabels/__init__.py create mode 100644 src/Mod/Plot/plotPositions/TaskPanel.py create mode 100644 src/Mod/Plot/plotPositions/TaskPanel.ui create mode 100644 src/Mod/Plot/plotPositions/__init__.py create mode 100644 src/Mod/Plot/plotSave/TaskPanel.py create mode 100644 src/Mod/Plot/plotSave/TaskPanel.ui create mode 100644 src/Mod/Plot/plotSave/__init__.py create mode 100644 src/Mod/Plot/plotSeries/TaskPanel.py create mode 100644 src/Mod/Plot/plotSeries/TaskPanel.ui create mode 100644 src/Mod/Plot/plotSeries/__init__.py create mode 100644 src/Mod/Plot/plotUtils/Paths.py create mode 100644 src/Mod/Plot/plotUtils/Translator.py create mode 100644 src/Mod/Plot/plotUtils/__init__.py create mode 100644 src/WindowsInstaller/ModPlot.wxi 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f90b2e47b97b8a99929ba057c22965f042bc9b16 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Sun, 28 Oct 2012 19:55:09 +0100 Subject: [PATCH 2/4] Fixed missing stuff on autotools --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 2b5969e9e..f2418d77f 100644 --- a/configure.ac +++ b/configure.ac @@ -1074,6 +1074,7 @@ src/Mod/Sandbox/Gui/Makefile src/Mod/Surfaces/Makefile src/Mod/Ship/Makefile src/Mod/OpenSCAD/Makefile +src/Mod/Plot/Makefile src/Tools/Makefile src/Tools/_TEMPLATE_/Makefile src/Tools/_TEMPLATE_/App/Makefile From 42f5b936850154c478df8560957c34f36816cb92 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Sun, 28 Oct 2012 19:54:09 +0100 Subject: [PATCH 3/4] Re-Created branch form 0, to avoid conflicts --- CMakeLists.txt | 9 + cMake/FindMatplotlib.cmake | 48 ++ configure.ac | 17 + package/debian/control | 2 +- src/Mod/CMakeLists.txt | 3 + src/Mod/Makefile.am | 2 + src/Mod/Plot/CMakeLists.txt | 119 ++++ src/Mod/Plot/Icons/Axes.svg | 451 +++++++++++++++ src/Mod/Plot/Icons/Grid.svg | 407 ++++++++++++++ src/Mod/Plot/Icons/Icon.svg | 403 ++++++++++++++ src/Mod/Plot/Icons/Labels.svg | 705 ++++++++++++++++++++++++ src/Mod/Plot/Icons/Legend.svg | 679 +++++++++++++++++++++++ src/Mod/Plot/Icons/Positions.svg | 702 +++++++++++++++++++++++ src/Mod/Plot/Icons/Save.svg | 701 +++++++++++++++++++++++ src/Mod/Plot/Icons/Series.svg | 457 +++++++++++++++ src/Mod/Plot/InitGui.py | 50 ++ src/Mod/Plot/Makefile.am | 45 ++ src/Mod/Plot/Plot.py | 314 +++++++++++ src/Mod/Plot/PlotGui.py | 132 +++++ src/Mod/Plot/README | 10 + src/Mod/Plot/plot.dox | 3 + src/Mod/Plot/plotAxes/TaskPanel.py | 425 ++++++++++++++ src/Mod/Plot/plotAxes/TaskPanel.ui | 332 +++++++++++ src/Mod/Plot/plotAxes/__init__.py | 36 ++ src/Mod/Plot/plotLabels/TaskPanel.py | 206 +++++++ src/Mod/Plot/plotLabels/TaskPanel.ui | 134 +++++ src/Mod/Plot/plotLabels/__init__.py | 36 ++ src/Mod/Plot/plotPositions/TaskPanel.py | 223 ++++++++ src/Mod/Plot/plotPositions/TaskPanel.ui | 107 ++++ src/Mod/Plot/plotPositions/__init__.py | 36 ++ src/Mod/Plot/plotSave/TaskPanel.py | 149 +++++ src/Mod/Plot/plotSave/TaskPanel.ui | 141 +++++ src/Mod/Plot/plotSave/__init__.py | 36 ++ src/Mod/Plot/plotSeries/TaskPanel.py | 309 +++++++++++ src/Mod/Plot/plotSeries/TaskPanel.ui | 154 ++++++ src/Mod/Plot/plotSeries/__init__.py | 36 ++ src/Mod/Plot/plotUtils/Paths.py | 55 ++ src/Mod/Plot/plotUtils/Translator.py | 30 + src/Mod/Plot/plotUtils/__init__.py | 25 + src/WindowsInstaller/FreeCAD.wxs | 9 + src/WindowsInstaller/FreeCADModules.wxs | 1 + src/WindowsInstaller/ModPlot.wxi | 86 +++ 42 files changed, 7824 insertions(+), 1 deletion(-) create mode 100644 cMake/FindMatplotlib.cmake create mode 100644 src/Mod/Plot/CMakeLists.txt create mode 100644 src/Mod/Plot/Icons/Axes.svg create mode 100755 src/Mod/Plot/Icons/Grid.svg create mode 100755 src/Mod/Plot/Icons/Icon.svg create mode 100644 src/Mod/Plot/Icons/Labels.svg create mode 100644 src/Mod/Plot/Icons/Legend.svg create mode 100644 src/Mod/Plot/Icons/Positions.svg create mode 100755 src/Mod/Plot/Icons/Save.svg create mode 100644 src/Mod/Plot/Icons/Series.svg create mode 100644 src/Mod/Plot/InitGui.py create mode 100644 src/Mod/Plot/Makefile.am create mode 100644 src/Mod/Plot/Plot.py create mode 100644 src/Mod/Plot/PlotGui.py create mode 100644 src/Mod/Plot/README create mode 100644 src/Mod/Plot/plot.dox create mode 100644 src/Mod/Plot/plotAxes/TaskPanel.py create mode 100644 src/Mod/Plot/plotAxes/TaskPanel.ui create mode 100644 src/Mod/Plot/plotAxes/__init__.py create mode 100644 src/Mod/Plot/plotLabels/TaskPanel.py create mode 100644 src/Mod/Plot/plotLabels/TaskPanel.ui create mode 100644 src/Mod/Plot/plotLabels/__init__.py create mode 100644 src/Mod/Plot/plotPositions/TaskPanel.py create mode 100644 src/Mod/Plot/plotPositions/TaskPanel.ui create mode 100644 src/Mod/Plot/plotPositions/__init__.py create mode 100644 src/Mod/Plot/plotSave/TaskPanel.py create mode 100644 src/Mod/Plot/plotSave/TaskPanel.ui create mode 100644 src/Mod/Plot/plotSave/__init__.py create mode 100644 src/Mod/Plot/plotSeries/TaskPanel.py create mode 100644 src/Mod/Plot/plotSeries/TaskPanel.ui create mode 100644 src/Mod/Plot/plotSeries/__init__.py create mode 100644 src/Mod/Plot/plotUtils/Paths.py create mode 100644 src/Mod/Plot/plotUtils/Translator.py create mode 100644 src/Mod/Plot/plotUtils/__init__.py create mode 100644 src/WindowsInstaller/ModPlot.wxi 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a02a354322940d2d680ecd87cbf2e3f554a1e7f0 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Sun, 28 Oct 2012 19:55:09 +0100 Subject: [PATCH 4/4] Fixed missing stuff on autotools --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 2b5969e9e..f2418d77f 100644 --- a/configure.ac +++ b/configure.ac @@ -1074,6 +1074,7 @@ src/Mod/Sandbox/Gui/Makefile src/Mod/Surfaces/Makefile src/Mod/Ship/Makefile src/Mod/OpenSCAD/Makefile +src/Mod/Plot/Makefile src/Tools/Makefile src/Tools/_TEMPLATE_/Makefile src/Tools/_TEMPLATE_/App/Makefile