Download src/Mod/Plot/Plot.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 13.6 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Plot/Plot.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Plot/Plot.py
-
curl -L -o Plot.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Plot/Plot.py
13.6 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2011, 2012 Jose Luis Cercos Pita <jlcercos@gmail.com> * | |
| # * * | |
| # * 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 | |
| import PySide | |
| from PySide import QtCore, QtGui | |
| import sys | |
| try: | |
| import matplotlib | |
| matplotlib.use("QtAgg") | |
| # Force matplotlib to use PySide backend by temporarily unloading PyQt | |
| pyqt5_unloaded = False | |
| pyqt6_unloaded = False | |
| if "PyQt5.QtCore" in sys.modules: | |
| del sys.modules["PyQt5.QtCore"] | |
| pyqt5_unloaded = True | |
| if "PyQt6.QtCore" in sys.modules: | |
| del sys.modules["PyQt6.QtCore"] | |
| pyqt6_unloaded = True | |
| import matplotlib.pyplot as plt | |
| # restore PyQt if unloaded | |
| if pyqt5_unloaded: | |
| import PyQt5.QtCore | |
| if pyqt6_unloaded: | |
| import PyQt6.QtCore | |
| from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas | |
| from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar | |
| from matplotlib.figure import Figure | |
| except ImportError: | |
| FreeCAD.Console.PrintWarning( | |
| "The 'matplotlib' Python package was not found. Plot module cannot be loaded\n" | |
| ) | |
| raise ImportError("matplotlib not installed") | |
| def getMainWindow(): | |
| """Return the FreeCAD main window.""" | |
| toplevel = PySide.QtGui.QApplication.topLevelWidgets() | |
| for i in toplevel: | |
| if i.metaObject().className() == "Gui::MainWindow": | |
| return i | |
| return None | |
| def getMdiArea(): | |
| """Return FreeCAD MdiArea.""" | |
| mw = getMainWindow() | |
| if not mw: | |
| return None | |
| childs = mw.children() | |
| for c in childs: | |
| if isinstance(c, PySide.QtGui.QMdiArea): | |
| return c | |
| return None | |
| def getPlot(): | |
| """Return the 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 closePlot(): | |
| """closePlot(): Close the active plot window.""" | |
| # 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": | |
| sub.close() | |
| def figure(winTitle="plot"): | |
| """Create a new plot subwindow/tab. | |
| Keyword arguments: | |
| winTitle -- Plot 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): | |
| """Plots a new serie (as line plot) | |
| Keyword arguments: | |
| x -- X values | |
| y -- Y values | |
| name -- Data 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(): | |
| """Return all the lines from a selected plot.""" | |
| plt = getPlot() | |
| if not plt: | |
| return [] | |
| return plt.series | |
| def removeSerie(index): | |
| """Remove a data serie from the active plot. | |
| Keyword arguments: | |
| index -- Index of the 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): | |
| """Show/Hide the legend from the active plot. | |
| Keyword arguments: | |
| status -- True if legend must be shown, False otherwise. | |
| pos -- Legend position. | |
| fontsize -- Font size | |
| """ | |
| plt = getPlot() | |
| if not plt: | |
| return | |
| plt.legend = status | |
| 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 is not None: | |
| handles.append(l.line) | |
| names.append(l.name) | |
| # Show the legend (at selected position or at best) | |
| if pos: | |
| l = axes.legend(handles, names, bbox_to_anchor=pos) | |
| plt.legPos = pos | |
| else: | |
| l = axes.legend(handles, names, loc="best") | |
| # Update canvas in order to compute legend data | |
| plt.canvas.draw() | |
| # Get resultant position | |
| try: | |
| fax = axes.get_frame().get_extents() | |
| except Exception: | |
| fax = axes.patch.get_extents() | |
| fl = l.get_frame() | |
| plt.legPos = ( | |
| (fl._x + fl._width - fax.x0) / fax.width, | |
| (fl._y + fl._height - fax.y0) / fax.height, | |
| ) | |
| # Set fontsize | |
| for t in l.get_texts(): | |
| t.set_fontsize(plt.legSiz) | |
| plt.update() | |
| def grid(status=True): | |
| """Show/Hide the grid from the active plot. | |
| Keyword arguments: | |
| 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): | |
| """Setup the plot title. | |
| Keyword arguments: | |
| string -- Plot title. | |
| """ | |
| plt = getPlot() | |
| if not plt: | |
| return | |
| axes = plt.axes | |
| axes.set_title(string) | |
| plt.update() | |
| def xlabel(string): | |
| """Setup the x label. | |
| Keyword arguments: | |
| string -- Title to set. | |
| """ | |
| plt = getPlot() | |
| if not plt: | |
| return | |
| axes = plt.axes | |
| axes.set_xlabel(string) | |
| plt.update() | |
| def ylabel(string): | |
| """Setup the y label. | |
| Keyword arguments: | |
| string -- Title to set. | |
| """ | |
| plt = getPlot() | |
| if not plt: | |
| return | |
| axes = plt.axes | |
| axes.set_ylabel(string) | |
| plt.update() | |
| def axesList(): | |
| """Return the plot axes sets list.""" | |
| plt = getPlot() | |
| if not plt: | |
| return [] | |
| return plt.axesList | |
| def axes(): | |
| """Return the active plot axes.""" | |
| plt = getPlot() | |
| if not plt: | |
| return None | |
| return plt.axes | |
| def addNewAxes(rect=None, frameon=True, patchcolor="none"): | |
| """Add new axes to plot, setting it as the active one. | |
| Keyword arguments: | |
| rect -- Axes area, None to copy from the last axes data. | |
| frameon -- True to show frame, False otherwise. | |
| patchcolor -- Patch color, 'none' for transparent plot. | |
| """ | |
| plt = getPlot() | |
| if not plt: | |
| return None | |
| fig = plt.fig | |
| if rect is 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 plot. | |
| Keyword arguments: | |
| path -- Destination file path. | |
| figsize -- w,h figure size tuple in inches. | |
| 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() | |
| def addNavigationToolbar(): | |
| """Add the matplotlib QT navigation toolbar to the plot.""" | |
| plt = getPlot() | |
| if not plt: | |
| return | |
| # Check that the navigation toolbar has not been already created | |
| if plt.mpl_toolbar is not None: | |
| return | |
| # Create the navigation toolbar and add it | |
| plt.mpl_toolbar = NavigationToolbar(plt.canvas, plt) | |
| vbox = plt.layout() | |
| vbox.addWidget(plt.mpl_toolbar) | |
| def delNavigationToolbar(): | |
| """Remove the matplotlib QT navigation toolbar from the plot.""" | |
| plt = getPlot() | |
| if not plt: | |
| return | |
| # Check that the navigation toolbar already exist | |
| if plt.mpl_toolbar is None: | |
| return | |
| # Remove the widget from the layout | |
| vbox = plt.layout() | |
| vbox.removeWidget(plt.mpl_toolbar) | |
| # Destroy the navigation toolbar | |
| plt.mpl_toolbar.deleteLater() | |
| plt.mpl_toolbar = None | |
| class Line: | |
| def __init__(self, axes, x, y, name): | |
| """Construct a new plot serie. | |
| Keyword arguments: | |
| axes -- Active axes | |
| x -- X values | |
| y -- Y values | |
| name -- Data 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): | |
| """Change a line property value. | |
| Keyword arguments: | |
| prop -- Property name. | |
| value -- New property value. | |
| """ | |
| plt.setp(self.line, prop, value) | |
| def getp(self, prop): | |
| """Get line property value. | |
| Keyword arguments: | |
| prop -- Property name. | |
| """ | |
| return plt.getp(self.line, prop) | |
| class Plot(PySide.QtGui.QWidget): | |
| def __init__(self, winTitle="plot", parent=None, flags=PySide.QtCore.Qt.WindowFlags(0)): | |
| """Construct a new plot widget. | |
| Keyword arguments: | |
| winTitle -- Tab title. | |
| parent -- Widget parent. | |
| flags -- QWidget flags | |
| """ | |
| PySide.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") | |
| # Add the navigation toolbar by default | |
| self.mpl_toolbar = NavigationToolbar(self.canvas, self) | |
| # Setup layout | |
| vbox = PySide.QtGui.QVBoxLayout() | |
| vbox.addWidget(self.canvas) | |
| vbox.addWidget(self.mpl_toolbar) | |
| 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 a new line and return it. | |
| Keyword arguments: | |
| x -- X values | |
| y -- Y values | |
| 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 the plot, redrawing the canvas.""" | |
| if not self.skip: | |
| self.skip = True | |
| if self.legend: | |
| legend(self.legend, self.legPos, self.legSiz) | |
| self.canvas.draw() | |
| self.skip = False | |
| def isGrid(self): | |
| """Return True if Grid is active, False otherwise.""" | |
| return bool(self.grid) | |
| def isLegend(self): | |
| """Return True if Legend is active, False otherwise.""" | |
| return bool(self.legend) | |
| def setActiveAxes(self, index): | |
| """Change the current active axes. | |
| Keyword arguments: | |
| index -- Index of the new active axes set. | |
| """ | |
| self.axes = self.axesList[index] | |
| self.fig.sca(self.axes) | |