From cfaf8945f74e8a12c1fd40e031a3c003840b6004 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:27:56 +1200 Subject: [PATCH 01/12] added f-strings and tidied imports Used flynt and isort to clean up imports and replace several format strings with f-strings. --- development/raise_version_number.py | 6 ++-- development/send_to_pypi.py | 9 ++--- docs/conf.py | 3 +- docs/example_pylustrator.py | 2 +- docs/figure1.py | 1 + pylustrator/QLinkableWidgets.py | 19 +++++----- pylustrator/QtGui.py | 28 ++++++++------- pylustrator/QtGuiDrag.py | 32 ++++++++--------- pylustrator/QtShortCuts.py | 7 ++-- pylustrator/__init__.py | 13 ++++--- pylustrator/arc2bez.py | 1 + pylustrator/ax_rasterisation.py | 8 +++-- pylustrator/change_tracker.py | 21 +++++++----- pylustrator/components/align.py | 1 + pylustrator/components/info_dialog.py | 1 + pylustrator/components/matplotlibwidget.py | 11 ++++-- pylustrator/components/plot_layout.py | 11 +++--- pylustrator/components/qitem_properties.py | 35 ++++++++++--------- pylustrator/components/qpos_and_size.py | 12 ++++--- pylustrator/components/tree_view.py | 5 ++- pylustrator/drag_helper.py | 26 ++++++++------ pylustrator/exception_swallower.py | 2 +- pylustrator/helper_functions.py | 40 ++++++++++++++-------- pylustrator/jupyter_cells.py | 1 + pylustrator/lab_colormap.py | 3 +- pylustrator/parse_svg.py | 20 ++++++----- pylustrator/pyjack.py | 5 +-- pylustrator/snap.py | 26 +++++++------- setup.py | 5 +-- tests/base_test_class.py | 9 ++--- tests/test_legend.py | 2 +- tests/test_text.py | 2 +- 32 files changed, 211 insertions(+), 156 deletions(-) diff --git a/development/raise_version_number.py b/development/raise_version_number.py index 9d68fa3..22c6bc1 100644 --- a/development/raise_version_number.py +++ b/development/raise_version_number.py @@ -22,7 +22,7 @@ import os import sys -from release_tools import replace_version, get_setup_properties +from release_tools import get_setup_properties, replace_version properties = get_setup_properties() @@ -51,5 +51,5 @@ os.system(f"git add {file}") # commit changes -os.system("git commit -m \"set version to v%s\"" % new_version) -os.system("git tag \"v%s\"" % new_version) +os.system(f"git commit -m \"set version to v{new_version}\"") +os.system(f"git tag \"v{new_version}\"") diff --git a/development/send_to_pypi.py b/development/send_to_pypi.py index a27437d..3702f5c 100644 --- a/development/send_to_pypi.py +++ b/development/send_to_pypi.py @@ -19,7 +19,8 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -from __future__ import print_function, division +from __future__ import division, print_function + import os import sys @@ -48,13 +49,13 @@ os.system("python setup.py sdist") # the command - command_string = "twine upload dist/pylustrator-%s.tar.gz" % current_version + command_string = f"twine upload dist/pylustrator-{current_version}.tar.gz" # optionally add the username if options.username: - command_string += " --username %s" % options.username + command_string += f" --username {options.username}" # optionally add the password if options.password: - command_string += " --password %s" % options.password + command_string += f" --password {options.password}" # print the command string print(command_string) # and execute it diff --git a/docs/conf.py b/docs/conf.py index 3b7ed8a..a35c589 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,9 +19,9 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -import sys import os import shlex +import sys print(os.getcwd()) @@ -31,6 +31,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import mock + # try to import the modules of the package and mock everything that is not found while True: try: diff --git a/docs/example_pylustrator.py b/docs/example_pylustrator.py index cab654f..3f24da1 100644 --- a/docs/example_pylustrator.py +++ b/docs/example_pylustrator.py @@ -1,11 +1,11 @@ # import matplotlib and numpy as usual import matplotlib.pyplot as plt import numpy as np +from icecream import install # now import pylustrator import pylustrator -from icecream import install install() import matplotlib.pyplot as plt import numpy as np diff --git a/docs/figure1.py b/docs/figure1.py index 41d11cd..fc65922 100644 --- a/docs/figure1.py +++ b/docs/figure1.py @@ -1,4 +1,5 @@ import matplotlib.pyplot as plt + import pylustrator pylustrator.load("plot1.py") diff --git a/pylustrator/QLinkableWidgets.py b/pylustrator/QLinkableWidgets.py index 12d65ab..55cb73b 100644 --- a/pylustrator/QLinkableWidgets.py +++ b/pylustrator/QLinkableWidgets.py @@ -26,10 +26,11 @@ import matplotlib.transforms as transforms import numpy as np from matplotlib.artist import Artist -from matplotlib.figure import Figure -from matplotlib.text import Text from matplotlib.axes import Axes from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets +from matplotlib.figure import Figure +from matplotlib.text import Text + from .helper_functions import main_figure @@ -61,7 +62,7 @@ def set(v): self.setLinkedProperty = set self.getLinkedProperty = get - self.serializeLinkedProperty = lambda x: "." + property_name + " = %s" % x + self.serializeLinkedProperty = lambda x: "." + property_name + f" = {x}" else: def set(v, v_list=None): if v_list is None: @@ -128,7 +129,7 @@ def getAll(): self.setLinkedProperty = set # lambda text: getattr(self.element, "set_"+property_name)(text) self.getLinkedProperty = lambda: getattr(self.element, "get_" + property_name)() self.getLinkedPropertyAll = getAll - self.serializeLinkedProperty = lambda x: ".set_" + property_name + "(%s)" % x + self.serializeLinkedProperty = lambda x: ".set_" + property_name + f"({x})" if condition is None: self.condition = lambda x: True @@ -733,7 +734,7 @@ def OpenDialog(self): def dialog_rejected(self): """ called when the dialog is cancelled """ color = self.current_color - color = color.name() + "%0.2x" % color.alpha() + color = color.name() + f"{color.alpha():002x}" self.setColor(color) self.valueChanged.emit(self.color) @@ -742,7 +743,7 @@ def dialog_changed(self): color = self.dialog.currentColor() # if a color is set, apply it if color.isValid(): - color = color.name() + "%0.2x" % color.alpha() + color = color.name() + f"{color.alpha():002x}" self.setColor(color) self.valueChanged.emit(self.color) @@ -752,7 +753,7 @@ def dialog_finished(self): self.dialog = None # if a color is set, apply it if color.isValid(): - color = color.name() + "%0.2x" % color.alpha() + color = color.name() + f"{color.alpha():002x}" self.setColor(color) self.valueChanged.emit(self.color) @@ -766,7 +767,7 @@ def setColor(self, value: str): self.button.setStyleSheet("background-color: rgba(%d, %d, %d, %d%%);" % ( int(value[1:3], 16), int(value[3:5], 16), int(value[5:7], 16), int(value[7:], 16) * 100 / 255)) else: - self.button.setStyleSheet("background-color: %s;" % (value,)) + self.button.setStyleSheet(f"background-color: {value};") self.color = value def getColor(self) -> str: @@ -782,7 +783,7 @@ def set(self, value): """ set the value (used for the Linkable parent class) """ try: if len(value) == 4: - self.setColor(mpl.colors.to_hex(value) + "%02X" % int(value[-1] * 255)) + self.setColor(mpl.colors.to_hex(value) + f"{int(value[-1] * 255):02X}") else: self.setColor(mpl.colors.to_hex(value)) except ValueError: diff --git a/pylustrator/QtGui.py b/pylustrator/QtGui.py index c1213a5..9fb0db7 100644 --- a/pylustrator/QtGui.py +++ b/pylustrator/QtGui.py @@ -19,24 +19,28 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see +import matplotlib.pyplot as plt +import numpy as np from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets -import numpy as np -import matplotlib.pyplot as plt try: # for matplotlib > 3.0 - from matplotlib.backends.backend_qtagg import (FigureCanvas, FigureManager, NavigationToolbar2QT as NavigationToolbar) + from matplotlib.backends.backend_qtagg import FigureCanvas, FigureManager + from matplotlib.backends.backend_qtagg import \ + NavigationToolbar2QT as NavigationToolbar except ModuleNotFoundError: from matplotlib.backends.backend_qt5agg import (FigureCanvas, FigureManager, NavigationToolbar2QT as NavigationToolbar) -from pylustrator.components.matplotlibwidget import MatplotlibWidget -from matplotlib import _pylab_helpers -from matplotlib.figure import Figure -from matplotlib.artist import Artist + +import sys + import matplotlib as mpl import qtawesome as qta +from matplotlib import _pylab_helpers +from matplotlib.artist import Artist +from matplotlib.figure import Figure -from .QtShortCuts import QDragableColor +from pylustrator.components.matplotlibwidget import MatplotlibWidget -import sys +from .QtShortCuts import QDragableColor def my_excepthook(type, value, tback): @@ -200,7 +204,7 @@ def figureSwapColor(figure: Figure, new_color: str, color_base: str): else: getattr(artist, "set_" + color_type_name)(new_color) artist.figure.change_tracker.addChange(artist, - ".set_" + color_type_name + "(\"%s\")" % (new_color,)) + ".set_" + color_type_name + f"(\"{new_color}\")") continue # use the attributes setter method getattr(artist, "set_" + color_type_name)(cmap(value)) @@ -216,7 +220,7 @@ def figureSwapColor(figure: Figure, new_color: str, color_base: str): else: # use the attributes setter method getattr(artist, "set_" + color_type_name)(new_color) - artist.figure.change_tracker.addChange(artist, ".set_" + color_type_name + "(\"%s\")" % (new_color,)) + artist.figure.change_tracker.addChange(artist, ".set_" + color_type_name + f"(\"{new_color}\")") """ Window """ @@ -415,7 +419,7 @@ def __init__(self, number, *args, **kwargs): QtWidgets.QWidget.__init__(self) # widget layout and elements - self.setWindowTitle("Figure %s" % number) + self.setWindowTitle(f"Figure {number}") self.setWindowIcon(qta.icon("fa5.bar-chart")) self.layout_main = QtWidgets.QHBoxLayout(self) diff --git a/pylustrator/QtGuiDrag.py b/pylustrator/QtGuiDrag.py index a075ffe..37f2abe 100644 --- a/pylustrator/QtGuiDrag.py +++ b/pylustrator/QtGuiDrag.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # QtGuiDrag.py +import os # Copyright (c) 2016-2020, Richard Gerum # # This file is part of Pylustrator. @@ -21,29 +22,24 @@ import sys import traceback -from matplotlib import _pylab_helpers - -import os -import qtawesome as qta import matplotlib.pyplot as plt -from matplotlib.figure import Figure +import qtawesome as qta +from matplotlib import _pylab_helpers from matplotlib.axes._axes import Axes -from matplotlib.text import Text from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets - +from matplotlib.figure import Figure +from matplotlib.text import Text from .ax_rasterisation import rasterizeAxes, restoreAxes -from .change_tracker import setFigureVariableNames -from .drag_helper import DragManager -from .exception_swallower import swallow_get_exceptions - -from .components.qitem_properties import QItemProperties -from .components.tree_view import MyTreeView +from .change_tracker import init_figure, setFigureVariableNames from .components.align import Align -from .components.plot_layout import PlotLayout from .components.info_dialog import InfoDialog +from .components.plot_layout import PlotLayout +from .components.qitem_properties import QItemProperties from .components.qpos_and_size import QPosAndSize -from .change_tracker import init_figure +from .components.tree_view import MyTreeView +from .drag_helper import DragManager +from .exception_swallower import swallow_get_exceptions def my_excepthook(type, value, tback): @@ -399,7 +395,7 @@ def __init__(self, number: int=0): self.plot_layout = PlotLayout(self.signals) # widget layout and elements - self.setWindowTitle("Figure %s - Pylustrator" % number) + self.setWindowTitle(f"Figure {number} - Pylustrator") self.setWindowIcon(QtGui.QIcon(os.path.join(os.path.dirname(__file__), "icons", "logo.ico"))) layout_parent = QtWidgets.QVBoxLayout(self) layout_parent.setContentsMargins(0, 0, 0, 0) @@ -574,9 +570,9 @@ def newfunc(*args): def updateTitle(self): """ update the title of the window to display if it is saved or not """ if self.fig.change_tracker.saved: - self.setWindowTitle("Figure %s - Pylustrator" % self.fig.number) + self.setWindowTitle(f"Figure {self.fig.number} - Pylustrator") else: - self.setWindowTitle("Figure %s* - Pylustrator" % self.fig.number) + self.setWindowTitle(f"Figure {self.fig.number}* - Pylustrator") def closeEvent(self, event: QtCore.QEvent): """ when the window is closed, ask the user to save """ diff --git a/pylustrator/QtShortCuts.py b/pylustrator/QtShortCuts.py index 74d1d53..d52e110 100644 --- a/pylustrator/QtShortCuts.py +++ b/pylustrator/QtShortCuts.py @@ -19,10 +19,9 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets -from matplotlib import pyplot as plt import matplotlib as mpl - +from matplotlib import pyplot as plt +from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets """ Color Chooser """ @@ -211,8 +210,8 @@ def exec(self): def getBackground(self, color: str) -> str: """ convert a colormap to a gradient background """ - import matplotlib.pyplot as plt import matplotlib as mpl + import matplotlib.pyplot as plt try: cmap = plt.get_cmap(color) except: diff --git a/pylustrator/__init__.py b/pylustrator/__init__.py index 207478d..0586b6a 100644 --- a/pylustrator/__init__.py +++ b/pylustrator/__init__.py @@ -19,10 +19,15 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -from .QtGuiDrag import initialize as start -from .helper_functions import fig_text, add_axes, add_image, despine, changeFigureSize, mark_inset, VoronoiPlot, selectRectangle, mark_inset_pos, draw_from_point_to_bbox, draw_from_point_to_point, loadFigureFromFile, add_letter, add_letters -from .QtGui import initialize as StartColorChooser -from .lab_colormap import LabColormap +from .helper_functions import (VoronoiPlot, add_axes, add_image, add_letter, + add_letters, changeFigureSize, despine, + draw_from_point_to_bbox, + draw_from_point_to_point, fig_text) +from .helper_functions import loadFigureFromFile from .helper_functions import loadFigureFromFile as load +from .helper_functions import mark_inset, mark_inset_pos, selectRectangle +from .lab_colormap import LabColormap +from .QtGui import initialize as StartColorChooser +from .QtGuiDrag import initialize as start __version__ = '1.3.0' diff --git a/pylustrator/arc2bez.py b/pylustrator/arc2bez.py index bb7ae37..d2134cb 100644 --- a/pylustrator/arc2bez.py +++ b/pylustrator/arc2bez.py @@ -21,6 +21,7 @@ import numpy as np + def mapToEllipse(pos, rx, ry, cosphi, sinphi, centerx, centery): x, y = pos x *= rx diff --git a/pylustrator/ax_rasterisation.py b/pylustrator/ax_rasterisation.py index 399335a..b8ee595 100644 --- a/pylustrator/ax_rasterisation.py +++ b/pylustrator/ax_rasterisation.py @@ -20,15 +20,19 @@ # along with Pylustrator. If not, see import io + import matplotlib.pyplot as plt + try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes except: from matplotlib.axes._subplots import Axes -from matplotlib.figure import Figure + from typing import List -from .helper_functions import removeContentFromFigure, addContentToFigure +from matplotlib.figure import Figure + +from .helper_functions import addContentToFigure, removeContentFromFigure def stashElements(ax: Axes, names: List[str]): diff --git a/pylustrator/change_tracker.py b/pylustrator/change_tracker.py index f7109bf..c9fcd0a 100644 --- a/pylustrator/change_tracker.py +++ b/pylustrator/change_tracker.py @@ -23,37 +23,40 @@ import sys import traceback from typing import IO -from packaging import version -import numpy as np import matplotlib import matplotlib as mpl import matplotlib.pyplot as plt +import numpy as np from matplotlib import _pylab_helpers from matplotlib.artist import Artist +from packaging import version + try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes except: from matplotlib.axes._subplots import Axes + from matplotlib.collections import Collection from matplotlib.figure import Figure + try: from matplotlib.figure import SubFigure # since matplotlib 3.4.0 except ImportError: SubFigure = None +from matplotlib.legend import Legend from matplotlib.lines import Line2D from matplotlib.patches import Rectangle from matplotlib.text import Text -from matplotlib.legend import Legend + try: from natsort import natsorted except: natsorted = sorted from .exception_swallower import Dummy -from .jupyter_cells import open from .helper_functions import main_figure - +from .jupyter_cells import open """ External overload """ class CustomStackPosition: @@ -180,9 +183,9 @@ def getReference(element: Artist, allow_using_variable_names=True): if name is not None: return name if isinstance(element.number, (float, int)): - return "plt.figure(%s)" % element.number + return f"plt.figure({element.number})" else: - return "plt.figure(\"%s\")" % element.number + return f"plt.figure(\"{element.number}\")" # subfigures are only available in matplotlib>=3.4.0 if version.parse(mpl.__version__) >= version.parse("3.4.0") and isinstance(element, SubFigure): index = element._parent.subfigs.index(element) @@ -247,7 +250,7 @@ def getReference(element: Artist, allow_using_variable_names=True): if isinstance(element, matplotlib.axes._axes.Axes): if element.get_label(): - return getReference(element.figure) + ".ax_dict[\"%s\"]" % escape_string(element.get_label()) + return getReference(element.figure) + f".ax_dict[\"{escape_string(element.get_label())}\"]" index = element.figure.axes.index(element) return getReference(element.figure) + ".axes[%d]" % index @@ -664,7 +667,7 @@ def load(self): fig = self.figure header = [] - header += ["fig = plt.figure(%s)" % self.figure.number] + header += [f"fig = plt.figure({self.figure.number})"] header += ["import matplotlib as mpl"] self.get_reference_cached = {} diff --git a/pylustrator/components/align.py b/pylustrator/components/align.py index b9dd8f7..5b624d8 100644 --- a/pylustrator/components/align.py +++ b/pylustrator/components/align.py @@ -1,4 +1,5 @@ import os + from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets diff --git a/pylustrator/components/info_dialog.py b/pylustrator/components/info_dialog.py index 99299d6..5dedb2a 100644 --- a/pylustrator/components/info_dialog.py +++ b/pylustrator/components/info_dialog.py @@ -1,4 +1,5 @@ import os + from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets diff --git a/pylustrator/components/matplotlibwidget.py b/pylustrator/components/matplotlibwidget.py index 4564984..02b6d8c 100644 --- a/pylustrator/components/matplotlibwidget.py +++ b/pylustrator/components/matplotlibwidget.py @@ -38,11 +38,15 @@ import time import qtawesome as qta -from matplotlib.backends.qt_compat import QtWidgets, QtCore +from matplotlib.backends.qt_compat import QtCore, QtWidgets + try: # for matplotlib > 3.0 - from matplotlib.backends.backend_qtagg import (FigureCanvas, FigureManager, NavigationToolbar2QT as NavigationToolbar) + from matplotlib.backends.backend_qtagg import FigureCanvas, FigureManager + from matplotlib.backends.backend_qtagg import \ + NavigationToolbar2QT as NavigationToolbar except ModuleNotFoundError: from matplotlib.backends.backend_qt5agg import (FigureCanvas, FigureManager, NavigationToolbar2QT as NavigationToolbar) + from matplotlib.figure import Figure @@ -77,6 +81,7 @@ def schedule_draw(self): def draw(self): self.timer.stop() import traceback + #print(traceback.print_stack()) t = time.time() super().draw() @@ -121,7 +126,7 @@ class CanvasWindow(QtWidgets.QWidget): def __init__(self, num="", *args, **kwargs): QtWidgets.QWidget.__init__(self) - self.setWindowTitle("Figure %s" % num) + self.setWindowTitle(f"Figure {num}") self.setWindowIcon(qta.icon("fa5s.bar-chart")) self.layout = QtWidgets.QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) diff --git a/pylustrator/components/plot_layout.py b/pylustrator/components/plot_layout.py index 8e76c9d..fddd14b 100644 --- a/pylustrator/components/plot_layout.py +++ b/pylustrator/components/plot_layout.py @@ -1,11 +1,14 @@ import os -import numpy as np -from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets import matplotlib.transforms as transforms +import numpy as np +from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets from matplotlib.figure import Figure + try: # for matplotlib > 3.0 - from matplotlib.backends.backend_qtagg import (FigureCanvas as Canvas, NavigationToolbar2QT as NavigationToolbar) + from matplotlib.backends.backend_qtagg import FigureCanvas as Canvas + from matplotlib.backends.backend_qtagg import \ + NavigationToolbar2QT as NavigationToolbar except ModuleNotFoundError: from matplotlib.backends.backend_qt5agg import (FigureCanvas as Canvas, NavigationToolbar2QT as NavigationToolbar) @@ -402,7 +405,7 @@ def mouse_move_event(self, event: QtCore.QEvent): self.footer_label.setText("%.2f, %.2f (cm) [%d, %d]" % (pos[0], pos[1], event.x, event.y)) if event.ydata is not None: - self.footer_label2.setText("%.2f, %.2f" % (event.xdata, event.ydata)) + self.footer_label2.setText(f"{event.xdata:.2f}, {event.ydata:.2f}") else: self.footer_label2.setText("") diff --git a/pylustrator/components/qitem_properties.py b/pylustrator/components/qitem_properties.py index bf5bf8b..5af8614 100644 --- a/pylustrator/components/qitem_properties.py +++ b/pylustrator/components/qitem_properties.py @@ -21,26 +21,29 @@ import os from typing import Any -import numpy as np -from packaging import version +import numpy as np import qtawesome as qta from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets +from packaging import version try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes except: from matplotlib.axes._subplots import Axes + import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.artist import Artist from matplotlib.figure import Figure from matplotlib.ticker import AutoLocator -from pylustrator.change_tracker import getReference -from pylustrator.QLinkableWidgets import QColorWidget, CheckWidget, TextWidget, DimensionsWidget, NumberWidget, ComboWidget +from pylustrator.change_tracker import (UndoRedo, add_axes_default, + add_text_default, getReference) from pylustrator.helper_functions import main_figure -from pylustrator.change_tracker import UndoRedo, add_text_default, add_axes_default +from pylustrator.QLinkableWidgets import (CheckWidget, ComboWidget, + DimensionsWidget, NumberWidget, + QColorWidget, TextWidget) class TextPropertiesWidget(QtWidgets.QWidget): @@ -390,8 +393,8 @@ def __init__(self, layout: QtWidgets.QLayout): self.layout = QtWidgets.QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) - from packaging import version import matplotlib as mpl + from packaging import version ncols_name = "ncols" if version.parse(mpl._get_version()) < version.parse("3.6.0"): ncols_name = "ncol" @@ -564,11 +567,11 @@ def parseTickLabel(self, line: str) -> (float, str): _, factor, base, exponent = match.groups() if factor is not None: number = float(factor) * float(base) ** float(exponent) - line = "%s x %s^%s" % (factor, base, exponent) + line = f"{factor} x {base}^{exponent}" else: try: number = float(base) ** float(exponent) - line = "%s^%s" % (base, exponent) + line = f"{base}^{exponent}" except ValueError: try: number = float(line) @@ -634,9 +637,9 @@ def setTarget(self, element: Artist): continue if min <= t <= max: if l != t: - text.append("%s \"%s\"" % (str(t), l_text)) + text.append(f"{str(t)} \"{l_text}\"") else: - text.append("%s" % l_text) + text.append(f"{l_text}") self.input_ticks.setText(",
".join(text)) ticks = getattr(self.element, "get_" + self.axis + "ticks")(minor=True) @@ -650,9 +653,9 @@ def setTarget(self, element: Artist): pass if min <= t <= max: if l != t: - text.append("%s \"%s\"" % (str(t), l_text)) + text.append(f"{str(t)} \"{l_text}\"") else: - text.append("%s" % l_text) + text.append(f"{l_text}") self.input_ticks2.setText(",
".join(text)) elements = [self.element] @@ -727,7 +730,7 @@ def ticksChanged2(self): min, max = getattr(element, "get_" + self.axis + "lim")() if min != self.range[0] or max != self.range[1]: self.fig.change_tracker.addChange(element, - ".set_" + self.axis + "lim(%s, %s)" % (str(min), str(max))) + ".set_" + self.axis + f"lim({str(min)}, {str(max)})") else: self.fig.change_tracker.addChange(element, ".set_" + self.axis + "lim(%s, %s)" % ( @@ -757,7 +760,7 @@ def getFontProperties(self): else: prop_copy[name] = value prop_copy2[name] = value - return (", ".join("%s=%s" % (k, v) for k, v in prop_copy.items())), prop_copy2 + return (", ".join(f"{k}={v}" for k, v in prop_copy.items())), prop_copy2 def fontStateChanged(self): self.ticksChanged() @@ -852,7 +855,7 @@ def redo(): min, max = getattr(element, "get_" + self.axis + "lim")() if min != self.range[0] or max != self.range[1]: self.fig.change_tracker.addChange(element, - ".set_" + self.axis + "lim(%s, %s)" % (str(min), str(max))) + ".set_" + self.axis + f"lim({str(min)}, {str(max)})") else: self.fig.change_tracker.addChange(element, ".set_" + self.axis + "lim(%s, %s)" % ( @@ -1154,7 +1157,7 @@ def addChange(element, command): ".add_axes([0.25, 0.25, 0.5, 0.5], label=\"%s\") # id=%s.new" % ( filename, getReference(axes)), axes, ".new") add_axes_default(axes) - addChange(axes, ".imshow(plt.imread(\"%s\"))" % filename) + addChange(axes, f".imshow(plt.imread(\"{filename}\"))") addChange(axes, '.set_xticks([])') addChange(axes, '.set_yticks([])') if 0: diff --git a/pylustrator/components/qpos_and_size.py b/pylustrator/components/qpos_and_size.py index 95c8943..d7238a1 100644 --- a/pylustrator/components/qpos_and_size.py +++ b/pylustrator/components/qpos_and_size.py @@ -1,19 +1,21 @@ from typing import Optional -from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets import matplotlib as mpl import matplotlib.transforms as transforms -from matplotlib.figure import Figure from matplotlib.artist import Artist +from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets +from matplotlib.figure import Figure + try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes except: from matplotlib.axes._subplots import Axes -from matplotlib.text import Text + import matplotlib.transforms as transforms +from matplotlib.text import Text from pylustrator.helper_functions import changeFigureSize, main_figure -from pylustrator.QLinkableWidgets import DimensionsWidget, ComboWidget +from pylustrator.QLinkableWidgets import ComboWidget, DimensionsWidget class QPosAndSize(QtWidgets.QWidget): @@ -160,7 +162,7 @@ def changeSize(self, value: list): pos.x0, pos.y0, pos.width, pos.height)) for text in self.fig.texts: pos = text.get_position() - self.fig.change_tracker.addChange(text, ".set_position([%f, %f])" % (pos[0], pos[1])) + self.fig.change_tracker.addChange(text, f".set_position([{pos[0]:f}, {pos[1]:f}])") self.fig.selection.update_selection_rectangles() self.fig.canvas.draw() diff --git a/pylustrator/components/tree_view.py b/pylustrator/components/tree_view.py index 454e876..12ad381 100644 --- a/pylustrator/components/tree_view.py +++ b/pylustrator/components/tree_view.py @@ -1,10 +1,9 @@ from typing import Optional -import qtawesome as qta -from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets - import matplotlib as mpl +import qtawesome as qta from matplotlib.artist import Artist +from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets class myTreeWidgetItem(QtGui.QStandardItem): diff --git a/pylustrator/drag_helper.py b/pylustrator/drag_helper.py index 03baf6d..7d79934 100644 --- a/pylustrator/drag_helper.py +++ b/pylustrator/drag_helper.py @@ -19,21 +19,24 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -import numpy as np +import time +from typing import Sequence + import matplotlib.pyplot as plt +import numpy as np from matplotlib.artist import Artist -from matplotlib.figure import Figure from matplotlib.axes import Axes -from matplotlib.text import Text -from matplotlib.patches import Rectangle, Ellipse -from matplotlib.backend_bases import MouseEvent, KeyEvent -from typing import Sequence +from matplotlib.backend_bases import KeyEvent, MouseEvent from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets +from matplotlib.figure import Figure +from matplotlib.patches import Ellipse, Rectangle +from matplotlib.text import Text -from .snap import TargetWrapper, getSnaps, checkSnaps, checkSnapsActive, SnapBase -from .change_tracker import ChangeTracker from pylustrator.change_tracker import UndoRedo -import time + +from .change_tracker import ChangeTracker +from .snap import (SnapBase, TargetWrapper, checkSnaps, checkSnapsActive, + getSnaps) DIR_X0 = 1 DIR_Y0 = 2 @@ -198,9 +201,9 @@ def add_target(self, target: Artist): if 0: rect1 = Rectangle((x0, y0), x1 - x0, y1 - y0, picker=False, figure=self.figure, linestyle="-", edgecolor="w", - facecolor="#FFFFFF00", zorder=900, label="_rect for %s" % str(target)) + facecolor="#FFFFFF00", zorder=900, label=f"_rect for {str(target)}") rect2 = Rectangle((x0, y0), x1 - x0, y1 - y0, picker=False, figure=self.figure, linestyle="--", edgecolor="k", - facecolor="#FFFFFF00", zorder=900, label="_rect2 for %s" % str(target)) + facecolor="#FFFFFF00", zorder=900, label=f"_rect2 for {str(target)}") self.figure.patches.append(rect1) self.figure.patches.append(rect2) self.targets_rects.append(rect1) @@ -262,6 +265,7 @@ def align_points(self, mode: str): if mode == "group": from pylustrator.helper_functions import axes_to_grid + #return axes_to_grid([target.target for target in self.targets], track_changes=True) with UndoRedo([target.target for target in self.targets if isinstance(target.target, Axes)], "Grid Align"): axes_to_grid([target.target for target in self.targets if isinstance(target.target, Axes)], track_changes=False) diff --git a/pylustrator/exception_swallower.py b/pylustrator/exception_swallower.py index e4a82de..6e44d8f 100644 --- a/pylustrator/exception_swallower.py +++ b/pylustrator/exception_swallower.py @@ -19,9 +19,9 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -from matplotlib.figure import Figure from matplotlib.axes._base import _AxesBase from matplotlib.axis import Axis +from matplotlib.figure import Figure class Dummy: diff --git a/pylustrator/helper_functions.py b/pylustrator/helper_functions.py index a54a063..b95a512 100644 --- a/pylustrator/helper_functions.py +++ b/pylustrator/helper_functions.py @@ -20,20 +20,27 @@ # along with Pylustrator. If not, see from __future__ import division + +import traceback + import matplotlib.pyplot as plt -from matplotlib.text import Text import numpy as np -import traceback +from matplotlib.text import Text + from .parse_svg import svgread + try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes except: from matplotlib.axes._subplots import Axes -from matplotlib.figure import Figure -from .pyjack import replace_all_refs + import os from typing import Sequence, Union +from matplotlib.figure import Figure + +from .pyjack import replace_all_refs + def fig_text(x: float, y: float, text: str, unit: str = "cm", *args, **kwargs): """ @@ -205,8 +212,9 @@ def loadFigureFromFile(filename: str, figure: Figure = None, offset: list = None and may not be stable. """ from matplotlib import rcParams - from pylustrator import changeFigureSize + import pylustrator + from pylustrator import changeFigureSize if label == "": label = get_unique_label(figure if figure is not None else plt.gcf(), filename) @@ -262,7 +270,7 @@ def figure(num=None, figsize=None, *args, **kwargs): def __exit__(self, type, value, traceback): from matplotlib.figure import Figure - from matplotlib.transforms import TransformedBbox, Affine2D + from matplotlib.transforms import Affine2D, TransformedBbox plt.figure = self.fig # get the size of the old figure @@ -374,7 +382,9 @@ def convertFromPyplot(old, new): def mark_inset(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequence[int]] = 1, loc2: Union[int, Sequence[int]] = 2, **kwargs): """ like the mark_inset function from matplotlib, but loc can also be a tuple """ - from mpl_toolkits.axes_grid1.inset_locator import TransformedBbox, BboxPatch, BboxConnector + from mpl_toolkits.axes_grid1.inset_locator import (BboxConnector, + BboxPatch, + TransformedBbox) try: loc1a, loc1b = loc1 except: @@ -403,7 +413,8 @@ def mark_inset(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequence[in def draw_from_point_to_bbox(parent_axes: Axes, insert_axes: Axes, point: Sequence, loc=1, **kwargs): """ add a box connector from a point to an axes """ - from mpl_toolkits.axes_grid1.inset_locator import TransformedBbox, BboxConnector, Bbox + from mpl_toolkits.axes_grid1.inset_locator import (Bbox, BboxConnector, + TransformedBbox) rect = TransformedBbox(Bbox([point, point]), parent_axes.transData) # rect = TransformedBbox(Bbox([[1, 0], [1, 0]]), parent_axes.transData) p1 = BboxConnector(rect, insert_axes.bbox, loc, **kwargs) @@ -414,7 +425,8 @@ def draw_from_point_to_bbox(parent_axes: Axes, insert_axes: Axes, point: Sequenc def draw_from_point_to_point(parent_axes: Axes, insert_axes: Axes, point1: Sequence, point2: Sequence, **kwargs): """ add a box connector from a point in on axes to a point in another axes """ - from mpl_toolkits.axes_grid1.inset_locator import TransformedBbox, BboxConnector, Bbox + from mpl_toolkits.axes_grid1.inset_locator import (Bbox, BboxConnector, + TransformedBbox) rect = TransformedBbox(Bbox([point1, point1]), parent_axes.transData) rect2 = TransformedBbox(Bbox([point2, point2]), insert_axes.transData) # rect = TransformedBbox(Bbox([[1, 0], [1, 0]]), parent_axes.transData) @@ -439,10 +451,10 @@ def mark_inset_pos(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequenc def VoronoiPlot(points: Sequence, values: Sequence, vmin: float = None, vmax:float = None, cmap=None): """ plot the voronoi regions of the poins with the given colormap """ - from matplotlib.patches import Polygon + from matplotlib import cm from matplotlib.collections import PatchCollection + from matplotlib.patches import Polygon from scipy.spatial import Voronoi, voronoi_plot_2d - from matplotlib import cm if cmap is None: cmap = cm.get_cmap('viridis') @@ -490,8 +502,8 @@ def selectRectangle(axes: Axes = None): def onselect(eclick, erelease): 'eclick and erelease are matplotlib events at press and release' - print(' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata)) - print(' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata)) + print(f' startposition : ({eclick.xdata:f}, {eclick.ydata:f})') + print(f' endposition : ({erelease.xdata:f}, {erelease.ydata:f})') print(' used button : ', eclick.button) from matplotlib.widgets import RectangleSelector @@ -628,7 +640,7 @@ def axes_to_grid(axes=None, track_changes=False): height, ]) if track_changes is True: - ax.figure.change_tracker.addChange(ax, ".set_position([%f, %f, %f, %f])" % (x_min+axes_indices[i][0] * (width+x_gap), y_min+axes_indices[i][1] * (height + y_gap), width, height)) + ax.figure.change_tracker.addChange(ax, f".set_position([{x_min + axes_indices[i][0] * (width + x_gap):f}, {y_min + axes_indices[i][1] * (height + y_gap):f}, {width:f}, {height:f}])") # make all the plots have the same limits xmin = np.min([ax.get_xlim()[0] for ax in axes]) diff --git a/pylustrator/jupyter_cells.py b/pylustrator/jupyter_cells.py index c9010c7..ec9e52c 100644 --- a/pylustrator/jupyter_cells.py +++ b/pylustrator/jupyter_cells.py @@ -46,6 +46,7 @@ def setJupyterCellText(text: str): def getIpythonCurrentCell() -> str: """ this function returns the text of the current jupyter cell """ import inspect + # get the first stack which has a filename starting with " """ Colormap """ -import numpy as np from typing import Sequence, Union + +import numpy as np from matplotlib.colors import Colormap, ListedColormap, to_rgb diff --git a/pylustrator/parse_svg.py b/pylustrator/parse_svg.py index 020c0fd..eec298a 100644 --- a/pylustrator/parse_svg.py +++ b/pylustrator/parse_svg.py @@ -19,20 +19,22 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see +import base64 +import io +import re +import sys from xml.dom import minidom + import matplotlib.colors as mcolors -import matplotlib.pyplot as plt import matplotlib.patches as mpatches -import matplotlib.transforms as mtransforms import matplotlib.path as mpath -from matplotlib.textpath import TextPath -from matplotlib.font_manager import FontProperties -import sys -import numpy as np -import re -import io -import base64 +import matplotlib.pyplot as plt import matplotlib.text +import matplotlib.transforms as mtransforms +import numpy as np +from matplotlib.font_manager import FontProperties +from matplotlib.textpath import TextPath + from .arc2bez import arcToBezier diff --git a/pylustrator/pyjack.py b/pylustrator/pyjack.py index 270fc45..244c5c7 100644 --- a/pylustrator/pyjack.py +++ b/pylustrator/pyjack.py @@ -12,10 +12,10 @@ NOTE: adapted for pylustrator to be Python3 compatible """ -import sys as _sys import gc as _gc -import types as _types import inspect as _inspect +import sys as _sys +import types as _types _WRAPPER_TYPES = (type(object.__init__), type(object().__init__),) @@ -235,6 +235,7 @@ def replace_all_refs(org_obj, new_obj): else: # debug: import sys + # print(type(referrer), file=sys.stderr) pass diff --git a/pylustrator/snap.py b/pylustrator/snap.py index e832130..cb59b81 100644 --- a/pylustrator/snap.py +++ b/pylustrator/snap.py @@ -19,29 +19,31 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -from typing import List, Tuple, Optional -from packaging import version -from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets +from typing import List, Optional, Tuple import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np from matplotlib.artist import Artist +from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets +from packaging import version + try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes except: from matplotlib.axes._subplots import Axes + from matplotlib.legend import Legend from matplotlib.lines import Line2D -from matplotlib.patches import Rectangle, Ellipse, FancyArrowPatch +from matplotlib.patches import Ellipse, FancyArrowPatch, Rectangle from matplotlib.text import Text + try: from matplotlib.figure import SubFigure # since matplotlib 3.4.0 except ImportError: SubFigure = None from .helper_functions import main_figure - DIR_X0 = 1 DIR_Y0 = 2 DIR_X1 = 4 @@ -208,25 +210,25 @@ def set_positions(self, points: (int, int)): self.target.set_height(points[1][1] - points[0][1]) if self.target.get_label() is None or not self.target.get_label().startswith("_rect"): change_tracker.addChange(self.target, ".set_xy([%f, %f])" % tuple(self.target.get_xy())) - change_tracker.addChange(self.target, ".set_width(%f)" % self.target.get_width()) - change_tracker.addChange(self.target, ".set_height(%f)" % self.target.get_height()) + change_tracker.addChange(self.target, f".set_width({self.target.get_width():f})") + change_tracker.addChange(self.target, f".set_height({self.target.get_height():f})") elif isinstance(self.target, Ellipse): self.target.center = np.mean(points, axis=0) self.target.width = points[1][0] - points[0][0] self.target.height = points[1][1] - points[0][1] change_tracker.addChange(self.target, ".center = (%f, %f)" % tuple(self.target.center)) - change_tracker.addChange(self.target, ".width = %f" % self.target.width) - change_tracker.addChange(self.target, ".height = %f" % self.target.height) + change_tracker.addChange(self.target, f".width = {self.target.width:f}") + change_tracker.addChange(self.target, f".height = {self.target.height:f}") elif isinstance(self.target, FancyArrowPatch): self.target.set_positions(points[0], points[1]) change_tracker.addChange(self.target, - ".set_positions(%s, %s)" % (tuple(points[0]), tuple(points[1]))) + f".set_positions({tuple(points[0])}, {tuple(points[1])})") elif isinstance(self.target, Text): if checkXLabel(self.target): axes = checkXLabel(self.target) axes.xaxis.labelpad = -(points[0][1] - self.target.pad_offset) / self.label_factor change_tracker.addChange(axes, - ".xaxis.labelpad = %f" % axes.xaxis.labelpad) + f".xaxis.labelpad = {axes.xaxis.labelpad:f}") self.target.set_position(points[0]) self.label_y = points[0][1] @@ -234,7 +236,7 @@ def set_positions(self, points: (int, int)): axes = checkYLabel(self.target) axes.yaxis.labelpad = -(points[0][0] - self.target.pad_offset) / self.label_factor change_tracker.addChange(axes, - ".yaxis.labelpad = %f" % axes.yaxis.labelpad) + f".yaxis.labelpad = {axes.yaxis.labelpad:f}") self.target.set_position(points[0]) self.label_x = points[0][0] diff --git a/setup.py b/setup.py index a6abdbd..d9868c2 100644 --- a/setup.py +++ b/setup.py @@ -19,10 +19,11 @@ # You should have received a copy of the GNU General Public License # along with Pylustrator. If not, see -from setuptools import setup - # read the contents of your README file from pathlib import Path + +from setuptools import setup + this_directory = Path(__file__).parent long_description = (this_directory / "README.md").read_text() diff --git a/tests/base_test_class.py b/tests/base_test_class.py index 0c3dec5..c88cac7 100644 --- a/tests/base_test_class.py +++ b/tests/base_test_class.py @@ -1,11 +1,12 @@ -import unittest -import numpy as np import re +import unittest from pathlib import Path -import matplotlib.pyplot as plt -from matplotlib.backend_bases import MouseEvent, KeyEvent from typing import Any +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.backend_bases import KeyEvent, MouseEvent + def ensure_list(obj, count=1): if isinstance(obj, list): diff --git a/tests/test_legend.py b/tests/test_legend.py index b5202c9..3fb283e 100644 --- a/tests/test_legend.py +++ b/tests/test_legend.py @@ -1,5 +1,5 @@ -from matplotlib.backend_bases import KeyEvent from base_test_class import BaseTest +from matplotlib.backend_bases import KeyEvent from matplotlib.legend import Legend diff --git a/tests/test_text.py b/tests/test_text.py index d2e67c8..eccfb78 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -1,5 +1,5 @@ -from matplotlib.backend_bases import KeyEvent from base_test_class import BaseTest, NotInSave +from matplotlib.backend_bases import KeyEvent class TestText(BaseTest): From b379a5ef490439210ec196cab237d4aa63eb5fc6 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:36:44 +1200 Subject: [PATCH 02/12] cleaned up whitespace, unnecessary f-strings, naked excepts Added exception to naked excepts Cleaned up trailing whitespace Removed f-strings where no substituted values occured --- development/raise_version_number.py | 2 +- docs/example_pylustrator.py | 3 +-- pylustrator/QLinkableWidgets.py | 8 ++++---- pylustrator/QtGuiDrag.py | 8 ++++---- pylustrator/QtShortCuts.py | 4 ++-- pylustrator/arc2bez.py | 2 +- pylustrator/ax_rasterisation.py | 2 +- pylustrator/change_tracker.py | 10 +++++----- pylustrator/components/align.py | 2 +- pylustrator/components/matplotlibwidget.py | 4 ++-- pylustrator/components/qitem_properties.py | 4 +--- pylustrator/components/qpos_and_size.py | 6 +++--- pylustrator/components/tree_view.py | 4 ++-- pylustrator/drag_helper.py | 2 +- pylustrator/exception_swallower.py | 2 +- pylustrator/helper_functions.py | 8 ++++---- pylustrator/parse_svg.py | 4 ++-- pylustrator/pyjack.py | 1 - pylustrator/snap.py | 2 +- tests/base_test_class.py | 2 +- tests/test_legend.py | 2 -- 21 files changed, 38 insertions(+), 44 deletions(-) diff --git a/development/raise_version_number.py b/development/raise_version_number.py index 22c6bc1..ea5d43e 100644 --- a/development/raise_version_number.py +++ b/development/raise_version_number.py @@ -49,7 +49,7 @@ for file in files: if replace_version(file, current_version, new_version): os.system(f"git add {file}") - + # commit changes os.system(f"git commit -m \"set version to v{new_version}\"") os.system(f"git tag \"v{new_version}\"") diff --git a/docs/example_pylustrator.py b/docs/example_pylustrator.py index 3f24da1..1461cc7 100644 --- a/docs/example_pylustrator.py +++ b/docs/example_pylustrator.py @@ -23,7 +23,7 @@ y = 2 * np.sin(np.pi * t) a, b = np.random.normal(loc=(5., 3.), scale=(2., 4.), size=(100,2)).T b += a - + fig = plt.figure(1) plt.clf() fig.text(0.5, 0.5, "new", transform=plt.figure(1).transFigure) @@ -89,4 +89,3 @@ # print(name) plt.show() - diff --git a/pylustrator/QLinkableWidgets.py b/pylustrator/QLinkableWidgets.py index 55cb73b..544e132 100644 --- a/pylustrator/QLinkableWidgets.py +++ b/pylustrator/QLinkableWidgets.py @@ -75,10 +75,10 @@ def set(v, v_list=None): if isinstance(self.element, Text) and len(main_figure(self.element).selection.targets) and isinstance(main_figure(self.element).selection.targets[0].target, Axes): for elm in main_figure(self.element).selection.targets: elm = elm.target - if self.element == getattr(getattr(elm, f"get_xaxis")(), "get_label")(): + if self.element == getattr(getattr(elm, "get_xaxis")(), "get_label")(): label_object = "x" break - if self.element == getattr(getattr(elm, f"get_yaxis")(), "get_label")(): + if self.element == getattr(getattr(elm, "get_yaxis")(), "get_label")(): label_object = "y" break @@ -106,10 +106,10 @@ def getAll(): if isinstance(self.element, Text) and len(main_figure(self.element).selection.targets) and isinstance(main_figure(self.element).selection.targets[0].target, Axes): for elm in main_figure(self.element).selection.targets: elm = elm.target - if self.element == getattr(getattr(elm, f"get_xaxis")(), "get_label")(): + if self.element == getattr(getattr(elm, "get_xaxis")(), "get_label")(): label_object = "x" break - if self.element == getattr(getattr(elm, f"get_yaxis")(), "get_label")(): + if self.element == getattr(getattr(elm, "get_yaxis")(), "get_label")(): label_object = "y" break diff --git a/pylustrator/QtGuiDrag.py b/pylustrator/QtGuiDrag.py index 37f2abe..7f5f1ae 100644 --- a/pylustrator/QtGuiDrag.py +++ b/pylustrator/QtGuiDrag.py @@ -424,16 +424,16 @@ def updateChangesSignal(undo, redo, undo_text, redo_text): self.undo_act.setText(f"Undo: {undo_text}") button_undo.setToolTip(f"Undo: {undo_text}") else: - self.undo_act.setText(f"Undo") - button_undo.setToolTip(f"Undo") + self.undo_act.setText("Undo") + button_undo.setToolTip("Undo") button_redo.setDisabled(redo) self.redo_act.setDisabled(redo) if redo_text != "": self.redo_act.setText(f"Redo: {redo_text}") button_redo.setToolTip(f"Redo: {redo_text}") else: - self.redo_act.setText(f"Redo") - button_redo.setToolTip(f"Redo") + self.redo_act.setText("Redo") + button_redo.setToolTip("Redo") self.update_changes_signal.connect(updateChangesSignal) diff --git a/pylustrator/QtShortCuts.py b/pylustrator/QtShortCuts.py index d52e110..fb4dfde 100644 --- a/pylustrator/QtShortCuts.py +++ b/pylustrator/QtShortCuts.py @@ -48,7 +48,7 @@ def getBackground(self) -> str: try: cmap = plt.get_cmap(self.color) - except: + except Exception: return "" text = "background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, " N = 10 @@ -214,7 +214,7 @@ def getBackground(self, color: str) -> str: import matplotlib.pyplot as plt try: cmap = plt.get_cmap(color) - except: + except Exception: return "" text = "background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, " N = 10 diff --git a/pylustrator/arc2bez.py b/pylustrator/arc2bez.py index d2134cb..8ababf8 100644 --- a/pylustrator/arc2bez.py +++ b/pylustrator/arc2bez.py @@ -55,7 +55,7 @@ def vectorAngle(ux, uy, vx, vy): if dot > 1: dot = 1 - + if dot < -1: dot = -1 diff --git a/pylustrator/ax_rasterisation.py b/pylustrator/ax_rasterisation.py index b8ee595..4ff59f9 100644 --- a/pylustrator/ax_rasterisation.py +++ b/pylustrator/ax_rasterisation.py @@ -25,7 +25,7 @@ try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes -except: +except Exception: from matplotlib.axes._subplots import Axes from typing import List diff --git a/pylustrator/change_tracker.py b/pylustrator/change_tracker.py index c9fcd0a..0a21134 100644 --- a/pylustrator/change_tracker.py +++ b/pylustrator/change_tracker.py @@ -34,7 +34,7 @@ try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes -except: +except Exception: from matplotlib.axes._subplots import Axes from matplotlib.collections import Collection @@ -51,7 +51,7 @@ try: from natsort import natsorted -except: +except Exception: natsorted = sorted from .exception_swallower import Dummy @@ -345,13 +345,13 @@ def get_describtion_string(self, element, exclude_default=True): # if the text is deleted we do not need to store all properties if not element.get_visible() or element.get_text() == "": if getattr(element, "is_new_text", False): - return element.axes or element.figure, f".text(0, 0, "", visible=False)" + return element.axes or element.figure, ".text(0, 0, "", visible=False)" else: is_label = np.any([ax.xaxis.get_label() == element or ax.yaxis.get_label() == element for ax in element.figure.axes]) if is_label: - return element, f".set(text='')" - return element, f".set(visible=False)" + return element, ".set(text='')" + return element, ".set(visible=False)" # properties to store properties = ["position", "text", "ha", "va", "fontsize", "color", "style", "weight", "fontname", "rotation"] diff --git a/pylustrator/components/align.py b/pylustrator/components/align.py index 5b624d8..507e22c 100644 --- a/pylustrator/components/align.py +++ b/pylustrator/components/align.py @@ -46,4 +46,4 @@ def execute_action(self, act: str): self.fig.canvas.draw() def setFigure(self, fig): - self.fig = fig \ No newline at end of file + self.fig = fig diff --git a/pylustrator/components/matplotlibwidget.py b/pylustrator/components/matplotlibwidget.py index 02b6d8c..fd6f3a6 100644 --- a/pylustrator/components/matplotlibwidget.py +++ b/pylustrator/components/matplotlibwidget.py @@ -64,7 +64,7 @@ def __init__(self, parent=None, num=1, size=None, dpi=100, figure=None, *args, * self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) self.updateGeometry() - + self.manager = FigureManager(self, 1) self.manager._cidgcf = self.figure @@ -91,7 +91,7 @@ def draw(self): self.quick_draw = False else: self.quick_draw = True - + def show(self): self.draw() diff --git a/pylustrator/components/qitem_properties.py b/pylustrator/components/qitem_properties.py index 5af8614..7475a01 100644 --- a/pylustrator/components/qitem_properties.py +++ b/pylustrator/components/qitem_properties.py @@ -29,7 +29,7 @@ try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes -except: +except Exception: from matplotlib.axes._subplots import Axes import matplotlib as mpl @@ -1363,5 +1363,3 @@ def setElement(self, element: Artist): self.input_font_properties.hide() self.targetChanged.emit(element) - - diff --git a/pylustrator/components/qpos_and_size.py b/pylustrator/components/qpos_and_size.py index d7238a1..ab740d3 100644 --- a/pylustrator/components/qpos_and_size.py +++ b/pylustrator/components/qpos_and_size.py @@ -8,7 +8,7 @@ try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes -except: +except Exception: from matplotlib.axes._subplots import Axes import matplotlib.transforms as transforms @@ -267,5 +267,5 @@ def setElement(self, element: Artist): self.input_position.setValue((pos.x0, pos.y0)) self.input_transform.setEnabled(True) self.input_position.setEnabled(True) - except: - self.input_position.setDisabled(True) \ No newline at end of file + except Exception: + self.input_position.setDisabled(True) diff --git a/pylustrator/components/tree_view.py b/pylustrator/components/tree_view.py index 12ad381..1a2db77 100644 --- a/pylustrator/components/tree_view.py +++ b/pylustrator/components/tree_view.py @@ -135,7 +135,7 @@ def eventFilter(self, object: QtWidgets.QWidget, event: QtCore.QEvent): try: item = index.model().itemFromIndex(index) entry = item.entry - except: + except Exception: item = None entry = None @@ -389,4 +389,4 @@ def deleteEntry(self, entry: Artist): if parent_item: name = self.getNameOfEntry(parent_entry) if name is not None: - parent_item.setLabel(name) \ No newline at end of file + parent_item.setLabel(name) diff --git a/pylustrator/drag_helper.py b/pylustrator/drag_helper.py index 7d79934..18fc02d 100644 --- a/pylustrator/drag_helper.py +++ b/pylustrator/drag_helper.py @@ -907,4 +907,4 @@ class MyEllipse(MyItem, QtWidgets.QGraphicsEllipseItem): class MyEvent: def __init__(self, x, y): self.x = x - self.y = y \ No newline at end of file + self.y = y diff --git a/pylustrator/exception_swallower.py b/pylustrator/exception_swallower.py index 6e44d8f..2862e59 100644 --- a/pylustrator/exception_swallower.py +++ b/pylustrator/exception_swallower.py @@ -128,4 +128,4 @@ def get_legend(*args, **kwargs): return Dummy() return leg - _AxesBase.get_legend = get_legend \ No newline at end of file + _AxesBase.get_legend = get_legend diff --git a/pylustrator/helper_functions.py b/pylustrator/helper_functions.py index b95a512..3d94cc3 100644 --- a/pylustrator/helper_functions.py +++ b/pylustrator/helper_functions.py @@ -31,7 +31,7 @@ try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes -except: +except Exception: from matplotlib.axes._subplots import Axes import os @@ -387,12 +387,12 @@ def mark_inset(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequence[in TransformedBbox) try: loc1a, loc1b = loc1 - except: + except Exception: loc1a = loc1 loc1b = loc1 try: loc2a, loc2b = loc2 - except: + except Exception: loc2a = loc2 loc2b = loc2 rect = TransformedBbox(inset_axes.viewLim, parent_axes.transData) @@ -677,4 +677,4 @@ def main_figure(artist): if artist.figure == artist: return artist else: - return main_figure(artist.figure) \ No newline at end of file + return main_figure(artist.figure) diff --git a/pylustrator/parse_svg.py b/pylustrator/parse_svg.py index eec298a..05f07ee 100644 --- a/pylustrator/parse_svg.py +++ b/pylustrator/parse_svg.py @@ -130,7 +130,7 @@ def apply_style(style: dict, patch: mpatches.Patch) -> dict: def readColor(value): try: return mcolors.to_rgb(value) - except: + except Exception: # matplotlib cannot handle html colors in the form #000 if len(value) == 4 and value[0] == "#": return readColor("#"+value[1]*2+value[2]*2+value[3]*2) @@ -193,7 +193,7 @@ def readColor(value): elif key == "stroke-width": try: patch.set_linewidth(svgUnitToMpl(value)) - except: + except Exception: pass elif key == "stroke-linecap": try: diff --git a/pylustrator/pyjack.py b/pylustrator/pyjack.py index 244c5c7..9cc056b 100644 --- a/pylustrator/pyjack.py +++ b/pylustrator/pyjack.py @@ -312,4 +312,3 @@ def restore(self): import doctest doctest.testmod(optionflags=524) - diff --git a/pylustrator/snap.py b/pylustrator/snap.py index cb59b81..1d2f871 100644 --- a/pylustrator/snap.py +++ b/pylustrator/snap.py @@ -30,7 +30,7 @@ try: # starting from mpl version 3.6.0 from matplotlib.axes import Axes -except: +except Exception: from matplotlib.axes._subplots import Axes from matplotlib.legend import Legend diff --git a/tests/base_test_class.py b/tests/base_test_class.py index c88cac7..7e3e02e 100644 --- a/tests/base_test_class.py +++ b/tests/base_test_class.py @@ -343,4 +343,4 @@ def change_property2(self, property_name_list, value_list, call, get_obj_list, l # the output should still be the same self.assertEqual(text, self.get_script_text(), - f"Saved differently. Property '{property_name_list}'. [{test_run}]") \ No newline at end of file + f"Saved differently. Property '{property_name_list}'. [{test_run}]") diff --git a/tests/test_legend.py b/tests/test_legend.py index 3fb283e..fb3299d 100644 --- a/tests/test_legend.py +++ b/tests/test_legend.py @@ -95,5 +95,3 @@ def test_legend_properties(self): lambda _: fig.window.input_properties.input_legend_properties.widgets[ "title_fontsize"].setValue(23), get_legend, line_command, test_run, get_function=lambda: get_legend().get_title().get_fontsize()) - - From efbf8a815ea33042b120043853554b0dedf6180e Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:38:16 +1200 Subject: [PATCH 03/12] python 3 update don't need to inherit from object --- pylustrator/drag_helper.py | 2 +- pylustrator/pyjack.py | 2 +- pylustrator/snap.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pylustrator/drag_helper.py b/pylustrator/drag_helper.py index 18fc02d..21c7a2b 100644 --- a/pylustrator/drag_helper.py +++ b/pylustrator/drag_helper.py @@ -46,7 +46,7 @@ blit = False -class GrabFunctions(object): +class GrabFunctions(): """ basic functionality used by all grabbers """ figure = None target = None diff --git a/pylustrator/pyjack.py b/pylustrator/pyjack.py index 9cc056b..f18e64a 100644 --- a/pylustrator/pyjack.py +++ b/pylustrator/pyjack.py @@ -256,7 +256,7 @@ def _get_self(): return _func_code_map[code] -class _PyjackFunc(object): pass +class _PyjackFunc(): pass class _PyjackFuncCode(_PyjackFunc): diff --git a/pylustrator/snap.py b/pylustrator/snap.py index 1d2f871..8b55573 100644 --- a/pylustrator/snap.py +++ b/pylustrator/snap.py @@ -83,7 +83,7 @@ def new_setter(*args, **kwargs): -class TargetWrapper(object): +class TargetWrapper(): """ a wrapper to add unified set and get position methods for any matplotlib artist """ target = None From ef0a6473a6b6f360697e31bfd22f27f6c0e44d90 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:39:31 +1200 Subject: [PATCH 04/12] removed u-prefix The u prefix for strings is no longer necessary in Python >=3.0 --- docs/conf.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a35c589..e6827b7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -80,9 +80,9 @@ master_doc = 'index' # General information about the project. -project = u'pylustrator' -copyright = u'2018, Richard Gerum' -author = u'Richard Gerum' +project = 'pylustrator' +copyright = '2018, Richard Gerum' +author = 'Richard Gerum' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -261,8 +261,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'pylustrator.tex', u'pylustrator Documentation', - u'Richard Gerum', 'manual'), + (master_doc, 'pylustrator.tex', 'pylustrator Documentation', + 'Richard Gerum', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -291,7 +291,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'pylustrator', u'Pylustrator Documentation', + (master_doc, 'pylustrator', 'Pylustrator Documentation', [author], 1) ] @@ -305,7 +305,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'pylustrator', u'Pylustrator Documentation', + (master_doc, 'pylustrator', 'Pylustrator Documentation', author, 'pylustrator', 'One line description of project.', 'Miscellaneous'), ] From 9870235e93cbea7391a1be8f1d3004b005a69ce2 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:41:36 +1200 Subject: [PATCH 05/12] fixed indent line was indented too far --- pylustrator/parse_svg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylustrator/parse_svg.py b/pylustrator/parse_svg.py index 05f07ee..52df443 100644 --- a/pylustrator/parse_svg.py +++ b/pylustrator/parse_svg.py @@ -268,7 +268,7 @@ def plt_patch(node: minidom.Element, trans_parent_trans: mtransforms.Transform, p.trans_node = parseTransformation(node.getAttribute("transform")) if not no_draw and not styleNoDisplay(style): - plt.gca().add_patch(p) + plt.gca().add_patch(p) if node.getAttribute("id") != "": ids[node.getAttribute("id")] = patch return patch From 18d01893596c2d17487497ccc0e4597b80b1e733 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:42:23 +1200 Subject: [PATCH 06/12] use sys.exit() exit() more for REPL --- docs/example_pylustrator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/example_pylustrator.py b/docs/example_pylustrator.py index 1461cc7..7bf6a86 100644 --- a/docs/example_pylustrator.py +++ b/docs/example_pylustrator.py @@ -1,4 +1,5 @@ # import matplotlib and numpy as usual +import sys import matplotlib.pyplot as plt import numpy as np from icecream import install @@ -56,7 +57,7 @@ plt.figure(1).axes[2].spines[['right', 'top']].set_visible(False) #% end: automatic generated code from pylustrator plt.show() - exit() + sys.exit() # activate pylustrator pylustrator.start() From 3ba0775ce382bdb0244227a4c338feb764cb9481 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:45:48 +1200 Subject: [PATCH 07/12] replace deprecated optparse Now using argparse --- development/send_to_pypi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development/send_to_pypi.py b/development/send_to_pypi.py index 3702f5c..5f90aff 100644 --- a/development/send_to_pypi.py +++ b/development/send_to_pypi.py @@ -35,7 +35,7 @@ current_version = pylustrator.__version__ - from optparse import OptionParser + from argparse import OptionParser parser = OptionParser() parser.add_option("-u", "--username", action="store", dest="username") From 905a78ce1b1cade7f798c0bd08f7b487130565e4 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:47:29 +1200 Subject: [PATCH 08/12] tidy warning, and be more accurate with rsplot --- pylustrator/pyjack.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pylustrator/pyjack.py b/pylustrator/pyjack.py index f18e64a..1eb31b6 100644 --- a/pylustrator/pyjack.py +++ b/pylustrator/pyjack.py @@ -30,7 +30,8 @@ def proxy1(): return data _CELLTYPE = int # type(proxy0(None).func_closure[0]) """ -class PyjackException(Exception): pass +class PyjackException(Exception): + pass def connect(fn, proxyfn): @@ -223,9 +224,9 @@ def replace_all_refs(org_obj, new_obj): 'func_defaults', 'func_closure']: orgattr = getattr(referrer, key) if orgattr is org_obj: - localsmap[key.split('func_')[-1]] = new_obj + localsmap[key.rsplit('func_', maxsplit=1)[-1]] = new_obj else: - localsmap[key.split('func_')[-1]] = orgattr + localsmap[key.rsplit('func_', maxsplit=1)[-1]] = orgattr localsmap['argdefs'] = localsmap['defaults'] del localsmap['defaults'] newfn = _types.FunctionType(**localsmap) @@ -256,7 +257,8 @@ def _get_self(): return _func_code_map[code] -class _PyjackFunc(): pass +class _PyjackFunc(): + pass class _PyjackFuncCode(_PyjackFunc): From a32980873ede28624a3da39a7ecb1a22aa98e3b3 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:48:31 +1200 Subject: [PATCH 09/12] fix singleton comparison --- pylustrator/change_tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylustrator/change_tracker.py b/pylustrator/change_tracker.py index 0a21134..7688160 100644 --- a/pylustrator/change_tracker.py +++ b/pylustrator/change_tracker.py @@ -417,7 +417,7 @@ def get_describtion_string(self, element, exclude_default=True): else: default = None pass - if (prop == "fontsize" or prop == "title_fontsize") and (default == "medium" or default == None): + if (prop == "fontsize" or prop == "title_fontsize") and (default == "medium" or default is None): if value == plt.rcParams["font.size"]: continue if prop == "title_fontsize" and "title" not in kwargs: From da556560697b5a3967a6050bde0bfa2617518f48 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 11:52:48 +1200 Subject: [PATCH 10/12] tidy some imports simplifying and removing one redundant one --- pylustrator/QLinkableWidgets.py | 2 +- pylustrator/components/plot_layout.py | 2 +- pylustrator/components/qpos_and_size.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pylustrator/QLinkableWidgets.py b/pylustrator/QLinkableWidgets.py index 544e132..f7119a7 100644 --- a/pylustrator/QLinkableWidgets.py +++ b/pylustrator/QLinkableWidgets.py @@ -23,7 +23,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt -import matplotlib.transforms as transforms +from matplotlib import transforms import numpy as np from matplotlib.artist import Artist from matplotlib.axes import Axes diff --git a/pylustrator/components/plot_layout.py b/pylustrator/components/plot_layout.py index fddd14b..9c5eb1d 100644 --- a/pylustrator/components/plot_layout.py +++ b/pylustrator/components/plot_layout.py @@ -1,6 +1,6 @@ import os -import matplotlib.transforms as transforms +from matplotlib import transforms import numpy as np from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets from matplotlib.figure import Figure diff --git a/pylustrator/components/qpos_and_size.py b/pylustrator/components/qpos_and_size.py index ab740d3..97bcccd 100644 --- a/pylustrator/components/qpos_and_size.py +++ b/pylustrator/components/qpos_and_size.py @@ -1,7 +1,7 @@ from typing import Optional import matplotlib as mpl -import matplotlib.transforms as transforms +from matplotlib import transforms from matplotlib.artist import Artist from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets from matplotlib.figure import Figure @@ -11,7 +11,6 @@ except Exception: from matplotlib.axes._subplots import Axes -import matplotlib.transforms as transforms from matplotlib.text import Text from pylustrator.helper_functions import changeFigureSize, main_figure From 6aab2e5c1fe3277fed580db50c338804787fd655 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 12:00:01 +1200 Subject: [PATCH 11/12] simplified if statements, used dict literals with min where possible used dict literals --- pylustrator/QtGuiDrag.py | 4 ++-- pylustrator/arc2bez.py | 6 ++---- pylustrator/components/qitem_properties.py | 2 +- pylustrator/drag_helper.py | 2 +- tests/base_test_class.py | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pylustrator/QtGuiDrag.py b/pylustrator/QtGuiDrag.py index 7f5f1ae..2cabf01 100644 --- a/pylustrator/QtGuiDrag.py +++ b/pylustrator/QtGuiDrag.py @@ -76,7 +76,7 @@ def wrapped_text(*args, **kwargs): element = text(*args, fontdict=kwargs["fontdict"] if "fontdict" in kwargs else None) from pylustrator.change_tracker import getReference stack_position = traceback.extract_stack()[-2] - element._pylustrator_reference = dict(reference=getReference(element), stack_position=stack_position) + element._pylustrator_reference = {"reference": getReference(element), "stack_position": stack_position} old_args = {} properties_to_save = ["position", "text", "ha", "va", "fontsize", "color", "style", "weight", "fontname", "rotation"] for name in properties_to_save: @@ -87,7 +87,7 @@ def wrapped_text(*args, **kwargs): old_args["position"] = None old_args["text"] = None old_values = getattr(element, "_pylustrator_old_values", []) - old_values.append(dict(stack_position=stack_position, old_args=old_args)) + old_values.append({"stack_position": stack_position, "old_args": old_args}) element._pylustrator_old_values = old_values if "fontdict" in kwargs: diff --git a/pylustrator/arc2bez.py b/pylustrator/arc2bez.py index 8ababf8..d065efb 100644 --- a/pylustrator/arc2bez.py +++ b/pylustrator/arc2bez.py @@ -55,8 +55,7 @@ def vectorAngle(ux, uy, vx, vy): if dot > 1: dot = 1 - - if dot < -1: + elif dot < -1: dot = -1 return sign * np.arccos(dot) @@ -71,8 +70,7 @@ def getArcCenter (px, py, cx, cy, rx, ry, radicant = (rxsq * rysq) - (rxsq * pypsq) - (rysq * pxpsq) - if radicant < 0: - radicant = 0 + radicant = max(0, radicant) radicant /= (rxsq * pypsq) + (rysq * pxpsq) radicant = np.sqrt(radicant) * (-1 if largeArcFlag == sweepFlag else 1) diff --git a/pylustrator/components/qitem_properties.py b/pylustrator/components/qitem_properties.py index 7475a01..fcece3b 100644 --- a/pylustrator/components/qitem_properties.py +++ b/pylustrator/components/qitem_properties.py @@ -1205,7 +1205,7 @@ def buttonAddAnnotationClicked(self): """ when the button 'add annoations' is clicked """ text = self.element.annotate("New Annotation", (self.element.get_xlim()[0], self.element.get_ylim()[0]), (np.mean(self.element.get_xlim()), np.mean(self.element.get_ylim())), - arrowprops=dict(arrowstyle="->")) + arrowprops={"arrowstyle": '->'}) self.fig.change_tracker.addChange(self.element, ".annotate('New Annotation', %s, %s, arrowprops=dict(arrowstyle='->')) # id=%s.new" % ( text.xy, text.get_position(), getReference(text)), diff --git a/pylustrator/drag_helper.py b/pylustrator/drag_helper.py index 21c7a2b..34b79e0 100644 --- a/pylustrator/drag_helper.py +++ b/pylustrator/drag_helper.py @@ -668,7 +668,7 @@ def make_dragable(self, target: Artist): """ make an artist draggable """ target.set_picker(True) if isinstance(target, Text): - target.set_bbox(dict(facecolor="none", edgecolor="none")) + target.set_bbox({"facecolor": 'none', "edgecolor": 'none'}) def get_picked_element(self, event: MouseEvent, element: Artist = None, picked_element: Artist = None, last_selected: Artist = None): """ get the picked element that an event refers to. diff --git a/tests/base_test_class.py b/tests/base_test_class.py index 7e3e02e..b1c162e 100644 --- a/tests/base_test_class.py +++ b/tests/base_test_class.py @@ -154,7 +154,7 @@ def check_saved_property(self, property_name, line_command, value2, test_run="") if value2 is NotInSave: return if property_name == "visible" and line_command.endswith(".text("): - kwargs = dict(visible=False) + kwargs = {"visible": False} else: raise err if line_command.endswith(".text("): From bb79a5e94feb94304774f9a85bd51d3cef8db257 Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Fri, 26 May 2023 14:54:30 +1200 Subject: [PATCH 12/12] fixed a bunch of flake8 warnings - space after # in comments - trailing commas - tidy whitespace around brackets, symbols - clean up docstrings - spacing between functions - docstrings punctuated - alignment of hanging lines - spaces around operators - indentation of blocks --- development/add_copyright_notice.py | 4 +- development/raise_version_number.py | 2 +- docs/conf.py | 100 ++++++------ docs/example_pylustrator.py | 14 +- pylustrator/QLinkableWidgets.py | 141 ++++++++-------- pylustrator/QtGui.py | 40 ++--- pylustrator/QtGuiDrag.py | 60 +++---- pylustrator/QtShortCuts.py | 84 +++++----- pylustrator/arc2bez.py | 20 +-- pylustrator/ax_rasterisation.py | 20 +-- pylustrator/change_tracker.py | 109 ++++++++----- pylustrator/components/align.py | 2 +- pylustrator/components/figure_previews.py | 6 +- pylustrator/components/matplotlibwidget.py | 3 +- pylustrator/components/plot_layout.py | 71 ++++---- pylustrator/components/qitem_properties.py | 135 ++++++++-------- pylustrator/components/qpos_and_size.py | 25 ++- pylustrator/components/tree_view.py | 46 +++--- pylustrator/drag_helper.py | 179 +++++++++++---------- pylustrator/exception_swallower.py | 30 ++-- pylustrator/helper_functions.py | 92 +++++------ pylustrator/jupyter_cells.py | 17 +- pylustrator/lab_colormap.py | 26 +-- pylustrator/parse_svg.py | 91 ++++++----- pylustrator/pyjack.py | 14 +- pylustrator/snap.py | 95 +++++------ setup.py | 2 +- tests/base_test_class.py | 15 +- tests/test_axes.py | 30 ++-- tests/test_legend.py | 6 +- tests/test_text.py | 4 +- 31 files changed, 767 insertions(+), 716 deletions(-) diff --git a/development/add_copyright_notice.py b/development/add_copyright_notice.py index 95fff49..1d0905c 100644 --- a/development/add_copyright_notice.py +++ b/development/add_copyright_notice.py @@ -12,7 +12,7 @@ if file.endswith(".py"): print(file) full_filename = os.path.join(root, file) - with open(full_filename+".tmp", "w") as fp2: + with open(full_filename + ".tmp", "w") as fp2: fp2.write(notice.format(file)) with open(full_filename, "r") as fp1: start = False @@ -23,4 +23,4 @@ fp2.write(line) continue - shutil.move(full_filename+".tmp", full_filename) + shutil.move(full_filename + ".tmp", full_filename) diff --git a/development/raise_version_number.py b/development/raise_version_number.py index ea5d43e..89c9a06 100644 --- a/development/raise_version_number.py +++ b/development/raise_version_number.py @@ -43,7 +43,7 @@ print(f"setting {package_name} version number from {current_version} to {new_version}") -files = ["setup.py", "meta.yaml", "docs/conf.py", package_name+"/__init__.py"] +files = ["setup.py", "meta.yaml", "docs/conf.py", package_name + "/__init__.py"] # Let's go for file in files: diff --git a/docs/conf.py b/docs/conf.py index e6827b7..dc40dbf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,7 +52,7 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -74,7 +74,7 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -102,9 +102,9 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -112,27 +112,27 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -147,96 +147,96 @@ html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] -#html_theme = 'alabaster' +# html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'pylustratordoc' @@ -245,16 +245,16 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', +# 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', +# 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. -#'preamble': '', +# 'preamble': '', # Latex figure (float) alignment -#'figure_align': 'htbp', +# 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples @@ -267,23 +267,23 @@ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -292,11 +292,11 @@ # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'pylustrator', 'Pylustrator Documentation', - [author], 1) + [author], 1), ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -311,13 +311,13 @@ ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/docs/example_pylustrator.py b/docs/example_pylustrator.py index 7bf6a86..f46edef 100644 --- a/docs/example_pylustrator.py +++ b/docs/example_pylustrator.py @@ -22,7 +22,7 @@ np.random.seed(1) t = np.arange(0.0, 2, 0.001) y = 2 * np.sin(np.pi * t) - a, b = np.random.normal(loc=(5., 3.), scale=(2., 4.), size=(100,2)).T + a, b = np.random.normal(loc=(5., 3.), scale=(2., 4.), size=(100, 2)).T b += a fig = plt.figure(1) @@ -39,13 +39,13 @@ plt.subplot(133) plt.bar(0, np.mean(a), label="a") plt.bar(1, np.mean(b), label="b") - #plt.legend() + # plt.legend() plt.xticks plt.figure(1).axes[0].set(position=[0.125, 0.11, 0.2279, 0.77], xlim=(-0.09995, 3.), xlabel='blaa', xticks=[0., 1., 2., 3.], xticklabels=['0', '1', '2', '3'], ylim=(-3., 3.), ylabel='fooo', yticks=[-3., -2., -1., 0., 1., 2., 3.], yticklabels=['−3', '−2', '−1', '0', '1', '2', '3']) plt.figure(1).axes[0].spines[['right', 'top']].set_visible(False) - #% start: automatic generated code from pylustrator + # % start: automatic generated code from pylustrator plt.figure(1).ax_dict = {ax.get_label(): ax for ax in plt.figure(1).axes} import matplotlib as mpl getattr(plt.figure(1), '_pylustrator_init', lambda: ...)() @@ -55,7 +55,7 @@ plt.figure(1).axes[1].spines[['right', 'top']].set_visible(False) plt.figure(1).axes[2].set(ylim=(0., 8.82)) plt.figure(1).axes[2].spines[['right', 'top']].set_visible(False) - #% end: automatic generated code from pylustrator + # % end: automatic generated code from pylustrator plt.show() sys.exit() # activate pylustrator @@ -65,7 +65,7 @@ np.random.seed(1) t = np.arange(0.0, 2, 0.001) y = 2 * np.sin(np.pi * t) -a, b = np.random.normal(loc=(5., 3.), scale=(2., 4.), size=(100,2)).T +a, b = np.random.normal(loc=(5., 3.), scale=(2., 4.), size=(100, 2)).T b += a plt.figure(1) @@ -84,8 +84,8 @@ plt.bar(1, np.mean(b), label="B") -#plt.figure(1).axes[0].set(position=[0.213022, 0.498889, 0.227941, 0.381111], xlim=[0.7, 1448], xticks=[0.1, 0.001], xticklabels=["A", "B"]) -#for name in dir(plt.figure(1).axes[0]): +# plt.figure(1).axes[0].set(position=[0.213022, 0.498889, 0.227941, 0.381111], xlim=[0.7, 1448], xticks=[0.1, 0.001], xticklabels=["A", "B"]) +# for name in dir(plt.figure(1).axes[0]): # if name.startswith("set_"): # print(name) diff --git a/pylustrator/QLinkableWidgets.py b/pylustrator/QLinkableWidgets.py index f7119a7..a3ad9dc 100644 --- a/pylustrator/QLinkableWidgets.py +++ b/pylustrator/QLinkableWidgets.py @@ -35,8 +35,7 @@ class Linkable: - """ a class that automatically links a widget with the property of a matplotlib artist - """ + """A class that automatically links a widget with the property of a matplotlib artist.""" def link(self, property_name: str, signal: QtCore.Signal = None, condition: callable = None, direct: bool = False): self.element = None @@ -66,7 +65,7 @@ def set(v): else: def set(v, v_list=None): if v_list is None: - v = [v]+[v]*len(main_figure(self.element).selection.targets) + v = [v] + [v] * len(main_figure(self.element).selection.targets) else: v = v_list @@ -140,7 +139,7 @@ def getAll(): signal.connect(self.setTarget) def setTarget(self, element: Artist): - """ set the target for the widget """ + """Set the target for the widget.""" self.element = element try: self.set(self.getLinkedProperty()) @@ -151,7 +150,7 @@ def setTarget(self, element: Artist): self.show() def updateLink(self): - """ update the linked property """ + """Update the linked property.""" old_value = self.getLinkedPropertyAll() try: @@ -176,6 +175,7 @@ def undo(): for elem, property_name, value in old_value: getattr(elem, "set_" + property_name, None)(value) save_change(elem) + def redo(): for elem, property_name, value in new_value: getattr(elem, "set_" + property_name, None)(value) @@ -192,15 +192,15 @@ def redo(): main_figure(self.element).signals.figure_selection_property_changed.emit() def set(self, value): - """ set the value (to be overloaded) """ + """Set the value (to be overloaded).""" pass def get(self): - """ get the value """ - return None + """Get the value.""" + return def getSerialized(self): - """ serialize the value for saving as a command """ + """Serialize the value for saving as a command.""" return "" @@ -218,11 +218,11 @@ def __init__(self): valueChanged : a signal that is emitted when the value is changed by the user """ QtWidgets.QLineEdit.__init__(self) - #self.setMaximumWidth(50) + # self.setMaximumWidth(50) self.textChanged.connect(self.emitValueChanged) def emitValueChanged(self): - """ connected to the textChanged signal """ + """Connected to the textChanged signal.""" if self.send_signal: try: value = self.value() @@ -233,7 +233,7 @@ def emitValueChanged(self): pass def value(self) -> Optional[float]: - """ return the value of the input field """ + """Return the value of the input field.""" try: return float(self.text()) except ValueError: @@ -243,7 +243,7 @@ def value(self) -> Optional[float]: return None def setValue(self, value: float): - """ set the value of the input field """ + """Set the value of the input field.""" self.send_signal = False try: self.setText(str(value)) @@ -260,7 +260,7 @@ class DimensionsWidget(QtWidgets.QWidget, Linkable): noSignal = False def __init__(self, layout: QtWidgets.QLayout, text: str, join: str, unit: str, free: bool = False): - """ a widget that lets the user input a pair of dimensions (e.g. widh and height) + """A widget that lets the user input a pair of dimensions (e.g. widh and height). Args: layout: the layout to which to add the widget @@ -307,37 +307,37 @@ def __init__(self, layout: QtWidgets.QLayout, text: str, join: str, unit: str, f self.editingFinished = self.valueChanged def setLabel(self, text: str): - """ set the text of the label """ + """ Set the text of the label. """ self.text.setText(text) def setUnit(self, unit: str): - """ Sets the text for the unit for the values """ + """ Sets the text for the unit for the values. """ self.input1.setSuffix(" " + unit) self.input2.setSuffix(" " + unit) def setTransform(self, transform: mpl.transforms.Transform): - """ set the transform for the units """ + """ Set the transform for the units. """ self.transform = transform def onValueChangedX(self): - """ called when the value was changed -> emit the value changed signal """ + """ Called when the value was changed -> emit the value changed signal. """ if not self.noSignal: self.valueChangedX.emit(self.value()[0]) self.valueChanged.emit(tuple(self.value())) def onValueChangedY(self): - """ called when the value was changed -> emit the value changed signal """ + """ Called when the value was changed -> emit the value changed signal. """ if not self.noSignal: self.valueChangedY.emit(self.value()[1]) self.valueChanged.emit(tuple(self.value())) def onValueChanged(self): - """ called when the value was changed -> emit the value changed signal """ + """ Called when the value was changed -> emit the value changed signal. """ if not self.noSignal: self.valueChanged.emit(tuple(self.value())) def setValue(self, values: tuple, signal=False): - """ set the two values """ + """ Set the two values. """ self.noSignal = True if self.transform: values = self.transform.transform(values) @@ -348,22 +348,22 @@ def setValue(self, values: tuple, signal=False): self.onValueChanged() def value(self): - """ get the value """ + """ Get the value. """ tuple = (self.input1.value(), self.input2.value()) if self.transform: tuple = self.transform.inverted().transform(tuple) return tuple def get(self) -> tuple: - """ get the value (used for the Linkable parent class) """ + """ Get the value (used for the Linkable parent class). """ return self.value() def set(self, value: tuple): - """ set both values (used for the Linkable parent class) """ + """ Set both values (used for the Linkable parent class). """ self.setValue(value) def getSerialized(self) -> str: - """ serialize the values """ + """ Serialize the values. """ return ", ".join([str(i) for i in self.get()]) @@ -373,7 +373,7 @@ class TextWidget(QtWidgets.QWidget, Linkable): last_text = None def __init__(self, layout: QtWidgets.QLayout, text: str, multiline: bool = False, horizontal: bool = True, allow_literal_decoding=False): - """ a text input widget with a label. + """ A text input widget with a label. Args: layout: the layout to which to add the widget @@ -403,16 +403,16 @@ def __init__(self, layout: QtWidgets.QLayout, text: str, multiline: bool = False self.layout.addWidget(self.input1) def valueChangeEvent(self): - """ an event that is triggered when the text in the input field is changed """ + """ An event that is triggered when the text in the input field is changed. """ if not self.noSignal and self.input1.text() != self.last_text: self.editingFinished.emit() def setLabel(self, text: str): - """ set the text of the label """ + """ Set the text of the label. """ self.label.setLabel(text) def setText(self, text: str, signal=False): - """ set contents of the text input widget """ + """ Set contents of the text input widget. """ self.noSignal = True text = text.replace("\n", "\\n") self.last_text = text @@ -425,13 +425,13 @@ def setText(self, text: str, signal=False): self.editingFinished.emit() def text(self) -> str: - """ return the text """ + """ Return the text. """ text = self.input1.text() return text.replace("\\n", "\n") def get(self) -> str: import ast - """ get the value (used for the Linkable parent class) """ + """ Get the value (used for the Linkable parent class). """ if self.allow_literal_decoding: try: return ast.literal_eval(self.text()) @@ -440,13 +440,14 @@ def get(self) -> str: return self.text() def set(self, value: str): - """ set the value (used for the Linkable parent class) """ + """ Set the value (used for the Linkable parent class). """ self.setText(str(value)) def getSerialized(self) -> str: - """ serialize the value (used for the Linkable parent class) """ + """ Serialize the value (used for the Linkable parent class). """ return "\"" + str(self.get()) + "\"" + class NumberWidget(QtWidgets.QWidget, Linkable): editingFinished = QtCore.Signal() noSignal = False @@ -478,16 +479,16 @@ def __init__(self, layout: QtWidgets.QLayout, text: str, min: float = None, use_ self.layout.addWidget(self.input1) def valueChangeEvent(self): - """ when the value of the spin box changes """ + """ When the value of the spin box changes. """ if not self.noSignal: self.editingFinished.emit() def setLabel(self, text: str): - """ set the text label """ + """ Set the text label. """ self.label.setLabel(text) def setValue(self, text: float, signal=False): - """ set the value of the spin box """ + """ Set the value of the spin box. """ self.noSignal = True self.input1.setValue(text) self.noSignal = False @@ -495,20 +496,20 @@ def setValue(self, text: float, signal=False): self.editingFinished.emit() def value(self) -> float: - """ get the value of the spin box """ + """ Get the value of the spin box. """ text = self.input1.value() return text def get(self) -> float: - """ get the value (used for the Linkable parent class) """ + """ Get the value (used for the Linkable parent class). """ return self.value() def set(self, value: float): - """ set the value (used for the Linkable parent class) """ + """ Set the value (used for the Linkable parent class). """ self.setValue(value) def getSerialized(self) -> str: - """ serialize the value (used for the Linkable parent class) """ + """ Serialize the value (used for the Linkable parent class). """ return str(self.get()) @@ -517,7 +518,7 @@ class ComboWidget(QtWidgets.QWidget, Linkable): noSignal = False def __init__(self, layout: QtWidgets.QLayout, text: str, values: Sequence): - """ A combo box widget with a label + """ A combo box widget with a label. Args: layout: the layout to which to add the widget @@ -541,16 +542,16 @@ def __init__(self, layout: QtWidgets.QLayout, text: str, values: Sequence): self.layout.addWidget(self.input1) def valueChangeEvent(self): - """ called when the value has changed """ + """ Called when the value has changed. """ if not self.noSignal: self.editingFinished.emit() def setLabel(self, text: str): - """ set the text of the label """ + """ Set the text of the label. """ self.label.setLabel(text) def setText(self, text: str, signal=False): - """ set the value of the combo box """ + """ Set the value of the combo box. """ self.noSignal = True index = self.values.index(text) self.input1.setCurrentIndex(index) @@ -559,20 +560,20 @@ def setText(self, text: str, signal=False): self.editingFinished.emit() def text(self) -> str: - """ get the value of the combo box """ + """ Get the value of the combo box. """ index = self.input1.currentIndex() return self.values[index] def get(self) -> str: - """ get the value (used for the Linkable parent class) """ + """ Get the value (used for the Linkable parent class). """ return self.text() def set(self, value: str): - """ set the value (used for the Linkable parent class) """ + """ Set the value (used for the Linkable parent class). """ self.setText(value) def getSerialized(self) -> str: - """ serialize the value (used for the Linkable parent class) """ + """ Serialize the value (used for the Linkable parent class). """ return "\"" + str(self.get()) + "\"" @@ -582,7 +583,7 @@ class CheckWidget(QtWidgets.QWidget, Linkable): noSignal = False def __init__(self, layout: QtWidgets.QLabel, text: str): - """ a widget that contains a checkbox with a label + """ A widget that contains a checkbox with a label. Args: layout: the layout to which to add the widget @@ -601,13 +602,13 @@ def __init__(self, layout: QtWidgets.QLabel, text: str): self.layout.addWidget(self.input1) def onStateChanged(self): - """ when the state of the checkbox changes """ + """ When the state of the checkbox changes. """ if not self.noSignal: self.stateChanged.emit(self.input1.isChecked()) self.editingFinished.emit() def setChecked(self, state: bool, signal=False): - """ set the value of the check box """ + """ Set the value of the check box. """ self.noSignal = True self.input1.setChecked(state) self.noSignal = False @@ -616,19 +617,19 @@ def setChecked(self, state: bool, signal=False): self.editingFinished.emit() def isChecked(self) -> bool: - """ get the value of the checkbox """ + """ Get the value of the checkbox. """ return self.input1.isChecked() def get(self) -> bool: - """ set the value (used for the Linkable parent class) """ + """ Set the value (used for the Linkable parent class). """ return self.isChecked() def set(self, value: bool): - """ get the value (used for the Linkable parent class) """ + """ Get the value (used for the Linkable parent class). """ self.setChecked(value) def getSerialized(self) -> str: - """ serialize the value (used for the Linkable parent class) """ + """ Serialize the value (used for the Linkable parent class). """ return "True" if self.get() else "False" @@ -637,7 +638,7 @@ class RadioWidget(QtWidgets.QWidget): noSignal = False def __init__(self, layout: QtWidgets.QLayout, texts: Sequence[str]): - """ a group of radio buttons + """ A group of radio buttons. Args: layout: the layout to which to add the widget @@ -660,14 +661,14 @@ def __init__(self, layout: QtWidgets.QLayout, texts: Sequence[str]): self.radio_buttons[0].setChecked(True) def onToggled(self, checked: int): - """ called when a radio button is toggled """ + """ Called when a radio button is toggled. """ if checked: self.checked = np.argmax([radio.isChecked() for radio in self.radio_buttons]) if not self.noSignal: self.stateChanged.emit(self.checked, self.texts[self.checked]) def setState(self, state: int): - """ set the state of the widget """ + """ Set the state of the widget. """ self.noSignal = True for index, radio in enumerate(self.radio_buttons): radio.setChecked(state == index) @@ -675,7 +676,7 @@ def setState(self, state: int): self.noSignal = False def getState(self) -> int: - """ get the state of the widget """ + """ Get the state of the widget. """ return self.checked @@ -683,7 +684,7 @@ class QColorWidget(QtWidgets.QWidget, Linkable): valueChanged = QtCore.Signal(str) def __init__(self, layout: QtWidgets.QLayout, text: str = None, value: str = None): - """ A colored button what acts as an color input + """ A colored button what acts as an color input. Args: layout: the layout to which to add the widget @@ -712,7 +713,7 @@ def __init__(self, layout: QtWidgets.QLayout, text: str = None, value: str = Non self.editingFinished = self.valueChanged def changeEvent(self, event): - """ when the widget is enabled """ + """ When the widget is enabled. """ if event.type() == QtCore.QEvent.EnabledChange: if not self.isEnabled(): self.button.setStyleSheet("background-color: #f0f0f0;") @@ -720,7 +721,7 @@ def changeEvent(self, event): self.setColor(self.color) def OpenDialog(self): - """ open a color chooser dialog """ + """ Open a color chooser dialog. """ # get new color from color picker self.current_color = QtGui.QColor(*tuple(int(x) for x in mpl.colors.to_rgba_array(self.getColor())[0] * 255)) self.dialog = QtWidgets.QColorDialog(self.current_color, self.parent()) @@ -732,14 +733,14 @@ def OpenDialog(self): self.dialog.rejected.connect(self.dialog_rejected) def dialog_rejected(self): - """ called when the dialog is cancelled """ + """ Called when the dialog is cancelled. """ color = self.current_color color = color.name() + f"{color.alpha():002x}" self.setColor(color) self.valueChanged.emit(self.color) def dialog_changed(self): - """ called when the value in the dialog changes """ + """ Called when the value in the dialog changes. """ color = self.dialog.currentColor() # if a color is set, apply it if color.isValid(): @@ -748,7 +749,7 @@ def dialog_changed(self): self.valueChanged.emit(self.color) def dialog_finished(self): - """ called when the dialog is finished with a click on 'ok' """ + """ Called when the dialog is finished with a click on 'ok'. """ color = self.dialog.selectedColor() self.dialog = None # if a color is set, apply it @@ -758,7 +759,7 @@ def dialog_finished(self): self.valueChanged.emit(self.color) def setColor(self, value: str): - """ set the color """ + """ Set the color. """ # display and save the new color if value is None: value = "#FF0000FF" @@ -771,16 +772,16 @@ def setColor(self, value: str): self.color = value def getColor(self) -> str: - """ get the color value """ + """ Get the color value. """ # return the color return self.color def get(self): - """ get the value (used for the Linkable parent class) """ + """ Get the value (used for the Linkable parent class). """ return self.getColor() def set(self, value): - """ set the value (used for the Linkable parent class) """ + """ Set the value (used for the Linkable parent class). """ try: if len(value) == 4: self.setColor(mpl.colors.to_hex(value) + f"{int(value[-1] * 255):02X}") @@ -790,5 +791,5 @@ def set(self, value): self.setColor(None) def getSerialized(self) -> str: - """ serialize the value (used for the Linkable parent class) """ + """ Serialize the value (used for the Linkable parent class). """ return "\"" + self.color + "\"" diff --git a/pylustrator/QtGui.py b/pylustrator/QtGui.py index 9fb0db7..ddea4f9 100644 --- a/pylustrator/QtGui.py +++ b/pylustrator/QtGui.py @@ -49,13 +49,13 @@ def my_excepthook(type, value, tback): sys.excepthook = my_excepthook -""" Matplotlib overload """ +""" Matplotlib overload. """ figures = {} app = None def initialize(): - """ patch figure and show to display the color chooser GUI """ + """ Patch figure and show to display the color chooser GUI. """ global app if app is None: app = QtWidgets.QApplication(sys.argv) @@ -64,7 +64,7 @@ def initialize(): def show(): - """ the patched show to display the color choose gui """ + """ The patched show to display the color choose gui. """ global figures # iterate over figures for figure in figures: @@ -77,7 +77,7 @@ def show(): def figure(num=None, figsize=None, *args, **kwargs): - """ the patched figure to initialize to color chooser GUI """ + """ The patched figure to initialize to color chooser GUI. """ global figures # if num is not defined create a new number if num is None: @@ -98,11 +98,11 @@ def figure(num=None, figsize=None, *args, **kwargs): return canvas.figure -""" Figure list functions """ +""" Figure list functions. """ def addChildren(color_artists: list, parent: Artist): - """ find all the children of an Artist that use a color """ + """ Find all the children of an Artist that use a color. """ for artist in parent.get_children(): # ignore empty texts if isinstance(artist, mpl.text.Text) and artist.get_text() == "": @@ -177,13 +177,13 @@ def addChildren(color_artists: list, parent: Artist): def figureListColors(figure: Figure): - """ add all artist with colors to a list in the figure """ + """ Add all artist with colors to a list in the figure. """ figure.color_artists = {} addChildren(figure.color_artists, figure) def figureSwapColor(figure: Figure, new_color: str, color_base: str): - """ swap two colors of a figure """ + """ Swap two colors of a figure. """ if getattr(figure, "color_artists", None) is None: figureListColors(figure) changed_cmaps = [] @@ -223,13 +223,13 @@ def figureSwapColor(figure: Figure, new_color: str, color_base: str): artist.figure.change_tracker.addChange(artist, ".set_" + color_type_name + f"(\"{new_color}\")") -""" Window """ +""" Window. """ class ColorChooserWidget(QtWidgets.QWidget): trigger_no_update = False - def __init__(self, parent: QtWidgets, canvas: FigureCanvas, signals: "Signals"=None): + def __init__(self, parent: QtWidgets, canvas: FigureCanvas, signals: "Signals" = None): """ A widget to display all curently used colors and let the user switch them. Args: @@ -269,7 +269,7 @@ def __init__(self, parent: QtWidgets, canvas: FigureCanvas, signals: "Signals"=N self.layout_buttons.addWidget(self.button_load) self.canvas = canvas - #self.updateColors() + # self.updateColors() # add a text widget to allow easy copy and paste self.colors_text_widget = QtWidgets.QTextEdit() @@ -284,11 +284,11 @@ def setCanvas(self, canvas): self.canvas = canvas def saveColors(self): - """ save the colors to a .txt file """ + """ Save the colors to a .txt file. """ options = QtWidgets.QFileDialog.Options() # options |= QtWidgets.QFileDialog.DontUseNativeDialog - path = QtWidgets.QFileDialog.getSaveFileName(self, "Save Color File", getattr(self, "last_save_folder", None),"Text Files (*.txt);;All Files (*)", options=options) + path = QtWidgets.QFileDialog.getSaveFileName(self, "Save Color File", getattr(self, "last_save_folder", None), "Text Files (*.txt);;All Files (*)", options=options) if isinstance(path, tuple): path = str(path[0]) @@ -301,7 +301,7 @@ def saveColors(self): fp.write(self.colors_text_widget.toPlainText()) def loadColors(self): - """ load a list of colors from a .txt file """ + """ Load a list of colors from a .txt file. """ options = QtWidgets.QFileDialog.Options() # options |= QtWidgets.QFileDialog.DontUseNativeDialog @@ -319,7 +319,7 @@ def loadColors(self): self.colors_text_widget.setText(fp.read()) def addColorButton(self, color: str, basecolor: str = None): - """ add a button for the given color """ + """ Add a button for the given color. """ try: button = QDragableColor(mpl.colors.to_hex(color)) except ValueError: @@ -332,7 +332,7 @@ def addColorButton(self, color: str, basecolor: str = None): self.color_buttons_list.append(button) def colorChanged(self, c, color_base): - """ update a color when it is changed + """ Update a color when it is changed. if colors are swapped then first change both colors and then update the text list of colors """ @@ -345,12 +345,12 @@ def colorChanged(self, c, color_base): self.updateColorsText() def resetSwapcounter(self, _): - """ when a color changed using the color picker the swap counter is reset """ + """ When a color changed using the color picker the swap counter is reset. """ self.swap_counter = 0 self.updateColorsText() def updateColorsText(self): - """ update the text list of colors """ + """ Update the text list of colors. """ # add recursively all artists of the figure figureListColors(self.canvas.figure) self.color_artists = list(self.canvas.figure.color_artists) @@ -380,7 +380,7 @@ def colorToText(color): self.canvas.updateGeometry() def colorsTextChanged(self): - """ when the colors in the text widget changed + """ When the colors in the text widget changed. after loading new colors or manually editing the text field """ if self.trigger_no_update: @@ -400,7 +400,7 @@ def colorsTextChanged(self): self.color_buttons_list[index].setColor(color) def color_selected(self, new_color: str, color_base: str): - """ switch two colors """ + """ Switch two colors. """ if color_base is None: return figureSwapColor(self.canvas.figure, new_color, color_base) diff --git a/pylustrator/QtGuiDrag.py b/pylustrator/QtGuiDrag.py index 2cabf01..9108c3a 100644 --- a/pylustrator/QtGuiDrag.py +++ b/pylustrator/QtGuiDrag.py @@ -48,12 +48,14 @@ def my_excepthook(type, value, tback): sys.excepthook = my_excepthook -""" Matplotlib overload """ +""" Matplotlib overload. """ figures = {} app = None keys_for_lines = {} no_save_allowed = False + + def initialize(use_global_variable_names=False, use_exception_silencer=False, disable_save=False): """ This will overload the commands ``plt.figure()`` and ``plt.show()``. @@ -120,8 +122,8 @@ def wrapped_text(*args, **kwargs): plt.show = show patchColormapsWithMetaInfo() - #stack_call_position = traceback.extract_stack()[-2] - #stack_call_position.filename + # stack_call_position = traceback.extract_stack()[-2] + # stack_call_position.filename plt.keys_for_lines = keys_for_lines @@ -134,8 +136,9 @@ def savefig(self, filename, *args, **kwargs): Figure.savefig = savefig + def pyl_show(hide_window: bool = False): - """ the function overloads the matplotlib show function. + """The function overloads the matplotlib show function. It opens a DragManager window instead of the default matplotlib window. """ global figures, app @@ -153,10 +156,10 @@ def pyl_show(hide_window: bool = False): fig = _pylab_helpers.Gcf.figs[figure_number].canvas.figure # get variable names that point to this figure - #if setting_use_global_variable_names: + # if setting_use_global_variable_names: # setFigureVariableNames(figure_number) # get the window - #window = _pylab_helpers.Gcf.figs[figure].canvas.window_pylustrator + # window = _pylab_helpers.Gcf.figs[figure].canvas.window_pylustrator # warn about ticks not fitting tick labels warnAboutTicks(fig) # add dragger @@ -173,7 +176,7 @@ def pyl_show(hide_window: bool = False): def show(hide_window: bool = False): - """ the function overloads the matplotlib show function. + """ The function overloads the matplotlib show function. It opens a DragManager window instead of the default matplotlib window. """ global figures @@ -188,7 +191,7 @@ def show(hide_window: bool = False): if setting_use_global_variable_names: setFigureVariableNames(figure) # get the window - #window = _pylab_helpers.Gcf.figs[figure].canvas.window_pylustrator + # window = _pylab_helpers.Gcf.figs[figure].canvas.window_pylustrator window = PlotWindow() window.setFigure(_pylab_helpers.Gcf.figs[figure].canvas.figure) # warn about ticks not fitting tick labels @@ -209,7 +212,7 @@ def show(hide_window: bool = False): class CmapColor(list): - """ a color like object that has the colormap as metadata """ + """ A color like object that has the colormap as metadata. """ def setMeta(self, value, cmap): self.value = value @@ -217,7 +220,7 @@ def setMeta(self, value, cmap): def patchColormapsWithMetaInfo(): - """ all colormaps now return color with metadata from which colormap the color came from """ + """ All colormaps now return color with metadata from which colormap the color came from. """ from matplotlib.colors import Colormap cm_call = Colormap.__call__ @@ -233,7 +236,7 @@ def new_call(self, *args, **kwargs): def figure(num=None, figsize=None, force_add=False, *args, **kwargs): - """ overloads the matplotlib figure call and wraps the Figure in a PlotWindow """ + """ Overloads the matplotlib figure call and wraps the Figure in a PlotWindow. """ global figures # if num is not defined create a new number if num is None: @@ -258,7 +261,7 @@ def figure(num=None, figsize=None, force_add=False, *args, **kwargs): def warnAboutTicks(fig): - """ warn if the tick labels and tick values do not match, to prevent users from accidentally setting wrong tick values """ + """ Warn if the tick labels and tick values do not match, to prevent users from accidentally setting wrong tick values. """ import sys for index, ax in enumerate(fig.axes): ticks = ax.get_yticks() @@ -281,7 +284,7 @@ def warnAboutTicks(fig): print("Warning tick and label differ", t, l, "for axes", ax_name, file=sys.stderr) -""" Window """ +""" Window. """ class Signals(QtWidgets.QWidget): @@ -324,7 +327,7 @@ def undo(): undo_act.triggered.connect(undo) self.menu_edit.addAction(undo_act) - #self.preview.addFigure(figure) + # self.preview.addFigure(figure) def selectionProperyChanged(self): self.fig.selection.update_selection_rectangles() @@ -376,8 +379,8 @@ def undo(self): def redo(self): self.fig.figure_dragger.redo() - def __init__(self, number: int=0): - """ The main window of pylustrator + def __init__(self, number: int = 0): + """ The main window of pylustrator. Args: number: the id of the figure @@ -391,7 +394,6 @@ def __init__(self, number: int=0): self.signals.canvas_changed.connect(self.setCanvas) self.signals.figure_selection_property_changed.connect(self.selectionProperyChanged) - self.plot_layout = PlotLayout(self.signals) # widget layout and elements @@ -448,14 +450,14 @@ def updateChangesSignal(undo, redo, undo_text, redo_text): self.layout_main.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) layout_parent.addWidget(self.layout_main) - #self.preview = FigurePreviews(self) - #self.layout_main.addWidget(self.preview) + # self.preview = FigurePreviews(self) + # self.layout_main.addWidget(self.preview) # widget = QtWidgets.QWidget() self.layout_tools = QtWidgets.QVBoxLayout(widget) widget.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - #widget.setMaximumWidth(350) - #widget.setMinimumWidth(350) + # widget.setMaximumWidth(350) + # widget.setMinimumWidth(350) self.layout_main.addWidget(widget) if 0: @@ -496,7 +498,7 @@ def updateChangesSignal(undo, redo, undo_text, redo_text): self.layout_main.setStretchFactor(2, 0) def rasterize(self, rasterize: bool): - """ convert the figur elements to an image """ + """ Convert the figur elements to an image. """ if len(self.fig.selection.targets): self.fig.figure_dragger.select_element(None) if rasterize: @@ -508,13 +510,13 @@ def rasterize(self, rasterize: bool): self.fig.canvas.draw() def actionSave(self): - """ save the code for the figure """ + """ Save the code for the figure. """ self.fig.change_tracker.save() for _last_saved_figure, args, kwargs in getattr(self.fig, "_last_saved_figure", []): self.fig.savefig(_last_saved_figure, *args, **kwargs) def actionSaveImage(self): - """ save figure as an image """ + """ Save figure as an image. """ path = QtWidgets.QFileDialog.getSaveFileName(self, "Save Image", getattr(self.fig, "_last_saved_figure", [(None,)])[0][0], "Images (*.png *.jpg *.pdf)") if isinstance(path, tuple): @@ -530,16 +532,16 @@ def actionSaveImage(self): print("Saved plot image as", path) def showInfo(self): - """ show the info dialog """ + """ Show the info dialog. """ self.info_dialog = InfoDialog(self) self.info_dialog.show() def showEvent(self, event: QtCore.QEvent): - """ when the window is shown """ + """ When the window is shown. """ self.colorWidget.updateColorsText() def update(self): - """ update the tree view """ + """ Update the tree view. """ # self.input_size.setValue(np.array(self.fig.get_size_inches())*2.54) def wrap(func): @@ -568,14 +570,14 @@ def newfunc(*args): self.signals.figure_element_selected.emit(self.fig) def updateTitle(self): - """ update the title of the window to display if it is saved or not """ + """ Update the title of the window to display if it is saved or not. """ if self.fig.change_tracker.saved: self.setWindowTitle(f"Figure {self.fig.number} - Pylustrator") else: self.setWindowTitle(f"Figure {self.fig.number}* - Pylustrator") def closeEvent(self, event: QtCore.QEvent): - """ when the window is closed, ask the user to save """ + """ When the window is closed, ask the user to save. """ if not self.fig.change_tracker.saved and not no_save_allowed: reply = QtWidgets.QMessageBox.question(self, 'Warning - Pylustrator', 'The figure has not been saved. ' 'All data will be lost.\nDo you want to save it?', diff --git a/pylustrator/QtShortCuts.py b/pylustrator/QtShortCuts.py index fb4dfde..493684c 100644 --- a/pylustrator/QtShortCuts.py +++ b/pylustrator/QtShortCuts.py @@ -23,10 +23,11 @@ from matplotlib import pyplot as plt from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets -""" Color Chooser """ +""" Color Chooser. """ + class QDragableColor(QtWidgets.QLabel): - """ a color widget that can be dragged onto another QDragableColor widget to exchange the two colors. + """ A color widget that can be dragged onto another QDragableColor widget to exchange the two colors. Alternatively it can be right-clicked to select either a color or a colormap through their respective menus. The button can represent either a single color or a colormap. """ @@ -35,7 +36,7 @@ class QDragableColor(QtWidgets.QLabel): color_changed_by_color_picker = QtCore.Signal(bool) def __init__(self, value: str): - """ initialize with a color """ + """ Initialize with a color. """ super().__init__(value) import matplotlib.pyplot as plt self.maps = plt.colormaps() @@ -44,8 +45,7 @@ def __init__(self, value: str): self.setColor(value, True) def getBackground(self) -> str: - """ get the background of the color button """ - + """ Get the background of the color button. """ try: cmap = plt.get_cmap(self.color) except Exception: @@ -59,23 +59,23 @@ def getBackground(self) -> str: return text def setColor(self, value: str, no_signal=False): - """ set the current color """ + """ Set the current color. """ # display and save the new color self.color = value self.setText(value) self.color_changed.emit(value) if value in self.maps: - self.setStyleSheet("text-align: center; border: 2px solid black; padding: 0.1em; "+self.getBackground()) + self.setStyleSheet("text-align: center; border: 2px solid black; padding: 0.1em; " + self.getBackground()) else: self.setStyleSheet(f"text-align: center; background-color: {value}; border: 2px solid black; padding: 0.1em; ") def getColor(self) -> str: - """ get the current color """ + """ Get the current color. """ # return the color return self.color def mousePressEvent(self, event): - """ when a mouse button is pressed """ + """ When a mouse button is pressed. """ # a left mouse button lets the user drag the color if event.button() == QtCore.Qt.LeftButton: drag = QtGui.QDrag(self) @@ -90,7 +90,7 @@ def mousePressEvent(self, event): self.setText(self.color) self.setDisabled(False) if self.color in self.maps: - self.setStyleSheet("text-align: center; border: 2px solid black; padding: 0.1em; "+self.getBackground()) + self.setStyleSheet("text-align: center; border: 2px solid black; padding: 0.1em; " + self.getBackground()) else: self.setStyleSheet(f"text-align: center; background-color: {self.color}; border: 2px solid black; padding: 0.1em; ") # a right mouse button opens a color choose menu @@ -98,29 +98,29 @@ def mousePressEvent(self, event): self.openDialog() def dragEnterEvent(self, event): - """ when a color widget is dragged over the current widget """ + """ When a color widget is dragged over the current widget. """ if event.mimeData().hasFormat("text/plain") and event.source() != self: event.acceptProposedAction() if self.color in self.maps: - self.setStyleSheet("border: 2px solid red; padding: 0.1em; "+self.getBackground()) + self.setStyleSheet("border: 2px solid red; padding: 0.1em; " + self.getBackground()) else: self.setStyleSheet(f"background-color: {self.color}; border: 2px solid red; padding: 0.1em; ") def dragLeaveEvent(self, event): - """ when the color widget which is dragged leaves the area of this widget """ + """ When the color widget which is dragged leaves the area of this widget. """ if self.color in self.maps: - self.setStyleSheet("border: 2px solid black; padding: 0.1em; "+self.getBackground()) + self.setStyleSheet("border: 2px solid black; padding: 0.1em; " + self.getBackground()) else: self.setStyleSheet(f"background-color: {self.color}; border: 2px solid black; padding: 0.1em; ") def dropEvent(self, event): - """ when a color widget is dropped here, exchange the two colors """ + """ When a color widget is dropped here, exchange the two colors. """ color = event.source().getColor() event.source().setColor(self.getColor()) self.setColor(color) def openDialog(self): - """ open a color choosed dialog """ + """ Open a color choosed dialog. """ if self.color in self.maps: dialog = ColorMapChoose(self.parent(), self.color) colormap, selected = dialog.exec() @@ -138,13 +138,13 @@ def openDialog(self): self.color_changed_by_color_picker.emit(True) - class ColorMapChoose(QtWidgets.QDialog): - """ A dialog to select a colormap """ + """ A dialog to select a colormap. """ + result = "" def __init__(self, parent: QtWidgets.QWidget, map): - """ initialize the dialog with all the colormap of matplotlib """ + """ Initialize the dialog with all the colormap of matplotlib. """ QtWidgets.QDialog.__init__(self, parent) main_layout = QtWidgets.QVBoxLayout(self) self.layout = QtWidgets.QHBoxLayout() @@ -164,25 +164,25 @@ def __init__(self, parent: QtWidgets.QWidget, map): # http://matplotlib.org/examples/color/colormaps_reference.html cmaps = [('Perceptually Uniform Sequential', [ 'viridis', 'plasma', 'inferno', 'magma']), - ('Sequential', [ - 'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', - 'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', - 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']), - ('Sequential (2)', [ - 'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink', - 'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia', - 'hot', 'afmhot', 'gist_heat', 'copper']), - ('Diverging', [ - 'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', - 'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']), - ('Qualitative', [ - 'Pastel1', 'Pastel2', 'Paired', 'Accent', - 'Dark2', 'Set1', 'Set2', 'Set3', - 'tab10', 'tab20', 'tab20b', 'tab20c']), - ('Miscellaneous', [ - 'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', - 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', 'hsv', - 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])] + ('Sequential', [ + 'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', + 'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', + 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']), + ('Sequential (2)', [ + 'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink', + 'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia', + 'hot', 'afmhot', 'gist_heat', 'copper']), + ('Diverging', [ + 'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', + 'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']), + ('Qualitative', [ + 'Pastel1', 'Pastel2', 'Paired', 'Accent', + 'Dark2', 'Set1', 'Set2', 'Set3', + 'tab10', 'tab20', 'tab20b', 'tab20c']), + ('Miscellaneous', [ + 'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', + 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', 'hsv', + 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])] for cmap_category, cmap_list in cmaps: layout = QtWidgets.QVBoxLayout() @@ -191,7 +191,7 @@ def __init__(self, parent: QtWidgets.QWidget, map): label.setFixedWidth(150) for cmap in cmap_list: button = QtWidgets.QPushButton(cmap) - button.setStyleSheet("text-align: center; border: 2px solid black; "+self.getBackground(cmap)) + button.setStyleSheet("text-align: center; border: 2px solid black; " + self.getBackground(cmap)) button.clicked.connect(lambda _, cmap=cmap: self.buttonClicked(cmap)) self.buttons.append(button) layout.addWidget(button) @@ -199,17 +199,17 @@ def __init__(self, parent: QtWidgets.QWidget, map): self.layout.addLayout(layout) def buttonClicked(self, text: str): - """ the used as selected a colormap, we are done """ + """ The used as selected a colormap, we are done. """ self.result = text self.done(1) def exec(self): - """ execute the dialog and return the result """ + """ Execute the dialog and return the result. """ result = QtWidgets.QDialog.exec(self) return self.result, result == 1 def getBackground(self, color: str) -> str: - """ convert a colormap to a gradient background """ + """ Convert a colormap to a gradient background. """ import matplotlib as mpl import matplotlib.pyplot as plt try: diff --git a/pylustrator/arc2bez.py b/pylustrator/arc2bez.py index d065efb..d19cda5 100644 --- a/pylustrator/arc2bez.py +++ b/pylustrator/arc2bez.py @@ -42,10 +42,10 @@ def approxUnitArc(ang1, ang2): y2 = np.sin(ang1 + ang2) return [ - [x1 - y1 * a, y1 + x1 * a], - [x2 + y2 * a, y2 - x2 * a], - [x2, y2] - ] + [x1 - y1 * a, y1 + x1 * a], + [x2 + y2 * a, y2 - x2 * a], + [x2, y2], + ] def vectorAngle(ux, uy, vx, vy): @@ -61,8 +61,8 @@ def vectorAngle(ux, uy, vx, vy): return sign * np.arccos(dot) -def getArcCenter (px, py, cx, cy, rx, ry, - largeArcFlag, sweepFlag, sinphi, cosphi, pxp, pyp): +def getArcCenter(px, py, cx, cy, rx, ry, + largeArcFlag, sweepFlag, sinphi, cosphi, pxp, pyp): rxsq = np.power(rx, 2) rysq = np.power(ry, 2) pxpsq = np.power(pxp, 2) @@ -90,15 +90,15 @@ def getArcCenter (px, py, cx, cy, rx, ry, ang2 = vectorAngle(vx1, vy1, vx2, vy2) if sweepFlag == 0 and ang2 > 0: - ang2 -= np.pi*2 + ang2 -= np.pi * 2 if sweepFlag == 1 and ang2 < 0: - ang2 += np.pi*2 + ang2 += np.pi * 2 return centerx, centery, ang1, ang2 -def arcToBezier(pos1, pos2, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFlag = 0): +def arcToBezier(pos1, pos2, rx, ry, xAxisRotation=0, largeArcFlag=0, sweepFlag=0): px, py = pos1 cx, cy = pos2 curves = [] @@ -125,7 +125,7 @@ def arcToBezier(pos1, pos2, rx, ry, xAxisRotation = 0, largeArcFlag = 0, sweepFl ry *= np.sqrt(lambda_) centerx, centery, ang1, ang2 = getArcCenter(px, py, cx, cy, rx, ry, - largeArcFlag, sweepFlag, sinphi, cosphi, pxp, pyp) + largeArcFlag, sweepFlag, sinphi, cosphi, pxp, pyp) # If 'ang2' == 90.0000000001, then `ratio` will evaluate to # 1.0000000001. This causes `segments` to be greater than one, which is an diff --git a/pylustrator/ax_rasterisation.py b/pylustrator/ax_rasterisation.py index 4ff59f9..64beebb 100644 --- a/pylustrator/ax_rasterisation.py +++ b/pylustrator/ax_rasterisation.py @@ -36,7 +36,7 @@ def stashElements(ax: Axes, names: List[str]): - """ remove elements from a figure and store them""" + """ Remove elements from a figure and store them.""" for attribute in names: element = getattr(ax, attribute) setattr(ax, "pylustrator_" + attribute, element) @@ -44,7 +44,7 @@ def stashElements(ax: Axes, names: List[str]): def popStashedElements(ax: Axes, names: List[str]): - """ add elements to a figure that were previously removed from it """ + """Add elements to a figure that were previously removed from it.""" for attribute in names: element_list = getattr(ax, attribute) if isinstance(element_list, list): @@ -57,7 +57,7 @@ def popStashedElements(ax: Axes, names: List[str]): def rasterizeAxes(fig: Figure): - """ replace contents of a figure with a rasterized image of it """ + """ Replace contents of a figure with a rasterized image of it. """ restoreAxes(fig) parts = removeContentFromFigure(fig) @@ -78,23 +78,23 @@ def rasterizeAxes(fig: Figure): bbox = ax.get_position() sx = im.shape[1] sy = im.shape[0] - x1, x2 = int(bbox.x0*sx+1), int(bbox.x1*sx-1) - y2, y1 = sy-int(bbox.y0*sy+1), sy-int(bbox.y1*sy-1) + x1, x2 = int(bbox.x0 * sx + 1), int(bbox.x1 * sx - 1) + y2, y1 = sy - int(bbox.y0 * sy + 1), sy - int(bbox.y1 * sy - 1) im2 = im[y1:y2, x1:x2] stashElements(ax, ["lines", "images", "patches"]) sx2 = ax.get_xlim()[1] - ax.get_xlim()[0] sy2 = ax.get_ylim()[1] - ax.get_ylim()[0] - x1_offset = 1/sx/bbox.width*sx2 - x2_offset = 1/sx/bbox.width*sx2 + x1_offset = 1 / sx / bbox.width * sx2 + x2_offset = 1 / sx / bbox.width * sx2 y1_offset = 1 / sy / bbox.height * sy2 y2_offset = 1 / sy / bbox.height * sy2 xlim = ax.get_xlim() ylim = ax.get_ylim() - ax.pylustrator_rasterized = ax.imshow(im2, extent=[ax.get_xlim()[0]+x1_offset, ax.get_xlim()[1]-x2_offset-x1_offset, - ax.get_ylim()[0]+y1_offset, ax.get_ylim()[1]-y2_offset-y1_offset], aspect="auto") + ax.pylustrator_rasterized = ax.imshow(im2, extent=[ax.get_xlim()[0] + x1_offset, ax.get_xlim()[1] - x2_offset - x1_offset, + ax.get_ylim()[0] + y1_offset, ax.get_ylim()[1] - y2_offset - y1_offset], aspect="auto") ax.set_xlim(xlim) ax.set_ylim(ylim) @@ -104,7 +104,7 @@ def rasterizeAxes(fig: Figure): def restoreAxes(fig: Figure): - """ restore contents of a figure """ + """Restore contents of a figure.""" list_axes = fig.axes for ax in list_axes: im = getattr(ax, "pylustrator_rasterized", None) diff --git a/pylustrator/change_tracker.py b/pylustrator/change_tracker.py index 7688160..1a97440 100644 --- a/pylustrator/change_tracker.py +++ b/pylustrator/change_tracker.py @@ -58,13 +58,18 @@ from .helper_functions import main_figure from .jupyter_cells import open -""" External overload """ +""" External overload. """ + + class CustomStackPosition: filename = None lineno = None + def __init__(self, filename, lineno): self.filename = filename self.lineno = lineno + + custom_stack_position = None custom_prepend = "" custom_append = "" @@ -75,27 +80,34 @@ def __init__(self, filename, lineno): ("\r", "\\r"), ("\"", "\\\""), ] + + def escape_string(str): for pair in escape_pairs: str = str.replace(pair[0], pair[1]) return str + def unescape_string(str): for pair in escape_pairs: str = str.replace(pair[1], pair[0]) return str + def to_str(v): if isinstance(v, list) and len(v) and isinstance(v[0], float): - return "["+", ".join(np.format_float_positional(a, 4, fractional=False, trim=".") for a in v)+"]" + return "[" + ", ".join(np.format_float_positional(a, 4, fractional=False, trim=".") for a in v) + "]" elif isinstance(v, tuple) and len(v) and isinstance(v[0], float): - return "("+", ".join(np.format_float_positional(a, 4, fractional=False, trim=".") for a in v)+")" + return "(" + ", ".join(np.format_float_positional(a, 4, fractional=False, trim=".") for a in v) + ")" elif isinstance(v, float): return np.format_float_positional(v, 4, fractional=False, trim=".") return repr(v) + + def kwargs_to_string(kwargs): return ', '.join(f'{k}={to_str(v)}' for k, v in kwargs.items()) + class UndoRedo: def __init__(self, elements, name): self.elements = list(elements) @@ -116,6 +128,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.figure.signals.figure_selection_property_changed.emit() self.change_tracker.addEdit([self.undo, self.redo, self.name]) + def init_figure(fig): for axes in fig.axes: add_axes_default(axes) @@ -124,6 +137,7 @@ def init_figure(fig): for text in fig.texts: add_text_default(text) + def add_text_default(element): # properties to store properties = ["position", "text", "ha", "va", "fontsize", "color", "style", "weight", "fontname", "rotation"] @@ -139,13 +153,15 @@ def add_text_default(element): if getattr(element, "is_new_text", False): old_args["position"] = None old_args["text"] = None + element._pylustrator_old_args = old_args + def add_axes_default(element): properties = ["position", "xlim", "xlabel", "xticks", "xticklabels", "xscale", "ylim", "ylabel", "yticks", "yticklabels", "yscale", - "zorder" + "zorder", ] if getattr(element, "_pylustrator_old_args", None) is None: old_args = {} @@ -163,7 +179,7 @@ def add_axes_default(element): old_args["yticks-locator"] = element.get_yaxis().major.locator old_args["yticklabels"] = [t.get_text() for t in old_args["yticklabels"]] old_args["grid"] = getattr(element.xaxis, "_gridOnMajor", False) or getattr(element.xaxis, "_major_tick_kw", {"gridOn": False})['gridOn'] - old_args["spines"] = {s: v.get_visible() for s,v in element.spines.items()} + old_args["spines"] = {s: v.get_visible() for s, v in element.spines.items()} old_args["xticks-minor"] = list(element.get_xticks(minor=True)) old_args["xticklabels-minor"] = [t.get_text() for t in element.get_xticklabels(minor=True)] @@ -173,8 +189,9 @@ def add_axes_default(element): add_text_default(element.get_xaxis().get_label()) add_text_default(element.get_yaxis().get_label()) + def getReference(element: Artist, allow_using_variable_names=True): - """ get the code string that represents the given Artist. """ + """Get the code string that represents the given Artist.""" if element is None: return "" if isinstance(element, Figure): @@ -260,7 +277,7 @@ def getReference(element: Artist, allow_using_variable_names=True): def setFigureVariableNames(figure: Figure): - """ get the global variable names that refer to the given figure """ + """Get the global variable names that refer to the given figure.""" import inspect mpl_figure = _pylab_helpers.Gcf.figs[figure].canvas.figure calling_globals = inspect.stack()[2][0].f_globals @@ -269,14 +286,15 @@ def setFigureVariableNames(figure: Figure): for name, val in calling_globals.items() if isinstance(val, mpl.figure.Figure) and hash(val) == hash(mpl_figure) ] - #print("fig_names", fig_names) + # print("fig_names", fig_names) if len(fig_names): globals()[fig_names[0]] = mpl_figure setattr(mpl_figure, "_variable_name", fig_names[0]) class ChangeTracker: - """ a class that records a list of the change to the figure """ + """A class that records a list of the change to the figure.""" + changes = None saved = True @@ -306,7 +324,7 @@ def __init__(self, figure: Figure, no_save): self.load() def addChange(self, command_obj: Artist, command: str, reference_obj: Artist = None, reference_command: str = None): - """ add a change """ + """Add a change.""" command = command.replace("\n", "\\n") if reference_obj is None: reference_obj = command_obj @@ -324,9 +342,10 @@ def get_element_restore_function(self, elements): description_strings.extend(desc) else: description_strings.append(desc) + def restore(): for element, string in description_strings: - #function, arguments = re.match(r"\.([^(]*)\((.*)\)", string) + # function, arguments = re.match(r"\.([^(]*)\((.*)\)", string) print(f"eval {getReference(element)}{string}") eval(f"{getReference(element)}{string}") if isinstance(element, Text): @@ -337,7 +356,7 @@ def restore(): self.addNewAxesChange(element) else: raise NotImplementedError - #getattr(element, function)(eval(arg)) + # getattr(element, function)(eval(arg)) return restore def get_describtion_string(self, element, exclude_default=True): @@ -430,14 +449,14 @@ def get_describtion_string(self, element, exclude_default=True): properties = ["position", "xscale", "xlabel", "xticks", "xticklabels", "xlim", "yscale", "ylabel", "yticks", "yticklabels", "ylim", - "zorder" + "zorder", ] # get current property values kwargs = {} for prop in properties: value = getattr(element, f"get_{prop}")() - #if self.text_properties_defaults[prop] != value or not exclude_default: + # if self.text_properties_defaults[prop] != value or not exclude_default: kwargs[prop] = value pos = element.get_position() @@ -483,7 +502,7 @@ def get_describtion_string(self, element, exclude_default=True): # the grid has_grid = getattr(element.xaxis, "_gridOnMajor", False) or \ - getattr(element.xaxis, "_major_tick_kw", {"gridOn": False})['gridOn'] + getattr(element.xaxis, "_major_tick_kw", {"gridOn": False})['gridOn'] if has_grid != element._pylustrator_old_args["grid"] or not exclude_default: desc_strings.append([element, f".grid({has_grid})"]) @@ -511,6 +530,7 @@ def get_describtion_string(self, element, exclude_default=True): return desc_strings text_properties_defaults = None + def addNewTextChange(self, element): command_parent, command = self.get_describtion_string(element) @@ -540,7 +560,7 @@ def addNewLegendChange(self, element): del self.changes[reference_obj, reference_command] # store the changes - #if not element.get_visible() and getattr(element, "is_new_text", False): + # if not element.get_visible() and getattr(element, "is_new_text", False): # return main_figure(element).change_tracker.addChange(command_parent, command) @@ -554,7 +574,7 @@ def addNewAxesChange(self, element): del self.changes[reference_obj, reference_command] # store the changes - #if not element.get_visible() and getattr(element, "is_new_text", False): + # if not element.get_visible() and getattr(element, "is_new_text", False): # return for command_parent, command in desc_strings: if command.endswith(".set()"): @@ -567,12 +587,12 @@ def changeCountChanged(self): if self.last_edit >= 0 and len(self.edits[self.last_edit]) > 2: name_undo = self.edits[self.last_edit][2] name_redo = "" - if self.last_edit < len(self.edits) - 1 and len(self.edits[self.last_edit+1]) > 2: - name_redo = self.edits[self.last_edit+1][2] + if self.last_edit < len(self.edits) - 1 and len(self.edits[self.last_edit + 1]) > 2: + name_redo = self.edits[self.last_edit + 1][2] self.update_changes_signal.emit(self.last_edit < 0, self.last_edit >= len(self.edits) - 1, name_undo, name_redo) def removeElement(self, element: Artist): - """ remove an Artis from the figure """ + """ Remove an Artis from the figure.""" # create_key = key+".new" created_by_pylustrator = (element, ".new") in self.changes # delete changes related to this element @@ -612,41 +632,41 @@ def undo(): self.figure.selection.remove_target(element) def addEdit(self, edit: list): - """ add an edit to the stored list of edits """ + """Add an edit to the stored list of edits.""" if self.last_edit < len(self.edits) - 1: self.edits = self.edits[:self.last_edit + 1] self.edits.append(edit) self.last_edit = len(self.edits) - 1 self.last_edit = len(self.edits) - 1 - #print("addEdit", len(self.edits), self.last_edit) + # print("addEdit", len(self.edits), self.last_edit) self.changeCountChanged() def backEdit(self): - """ undo an edit in the list """ + """Undo an edit in the list.""" if self.last_edit < 0: - #print("no backEdit", len(self.edits), self.last_edit) + # print("no backEdit", len(self.edits), self.last_edit) return edit = self.edits[self.last_edit] edit[0]() self.last_edit -= 1 self.figure.canvas.draw() - #print("backEdit", len(self.edits), self.last_edit) + # print("backEdit", len(self.edits), self.last_edit) self.changeCountChanged() def forwardEdit(self): - """ redo an edit """ + """Redo an edit.""" if self.last_edit >= len(self.edits) - 1: - #print("no forwardEdit", len(self.edits), self.last_edit) + # print("no forwardEdit", len(self.edits), self.last_edit) return edit = self.edits[self.last_edit + 1] edit[1]() self.last_edit += 1 self.figure.canvas.draw() - #print("forwardEdit", len(self.edits), self.last_edit) + # print("forwardEdit", len(self.edits), self.last_edit) self.changeCountChanged() def load(self): - """ load a set of changes from a script file. The changes are the code that pylustrator generated """ + """Load a set of changes from a script file. The changes are the code that pylustrator generated.""" regex = re.compile(r"(\.[^\(= ]*)(.*)") command_obj_regexes = [getReference(self.figure), r"plt\.figure\([^)]*\)", @@ -755,12 +775,12 @@ def load(self): self.get_reference_cached[reference_obj] = reference_obj_str - #print("---", [reference_obj, reference_command], (command_obj, command + parameter)) + # print("---", [reference_obj, reference_command], (command_obj, command + parameter)) self.changes[reference_obj, reference_command] = (command_obj, command + parameter) self.sorted_changes() def sorted_changes(self): - """ sort the changes by their priority. For example setting to logscale needs to be executed before xlim. """ + """ Sort the changes by their priority. For example setting to logscale needs to be executed before xlim. """ def getRef(obj): try: return getReference(obj) @@ -810,7 +830,7 @@ def getRef(obj): return output def save(self): - """ save the changes to the .py file """ + """ Save the changes to the .py file. """ # if saving is disabled if self.no_save is True: return @@ -831,7 +851,7 @@ def save(self): if line.startswith("fig.add_axes"): output.append(header[1]) output.append("#% end: automatic generated code from pylustrator" + custom_append) - print("\n"+"\n".join(output)+"\n") + print("\n" + "\n".join(output) + "\n") block_id = getReference(self.figure) block = getTextFromFile(block_id, stack_position) @@ -848,7 +868,7 @@ def save(self): def getTextFromFile(block_id: str, stack_pos: traceback.FrameSummary): - """ get the text which corresponds to the block_id (e.g. which figure) at the given position sepcified by stack_pos. """ + """ Get the text which corresponds to the block_id (e.g. which figure) at the given position sepcified by stack_pos. """ block_id = lineToId(block_id) block = None @@ -870,7 +890,7 @@ def getTextFromFile(block_id: str, stack_pos: traceback.FrameSummary): # if there is a new pylustrator block elif line.strip().startswith(custom_prepend + "#% start:"): block = Block(line) - start_lineno = lineno-1 + start_lineno = lineno - 1 # if we are currently reading a block, continue with the next line if block is not None and not block.finished: @@ -884,34 +904,35 @@ def getTextFromFile(block_id: str, stack_pos: traceback.FrameSummary): class Block: - """ an object to represent the code block generated by a pylustrator save """ + """ An object to represent the code block generated by a pylustrator save. """ + id = None finished = False def __init__(self, line: str): - """ initialize the block with its first line """ + """ Initialize the block with its first line.""" self.text = line self.size = 1 self.indent = getIndent(line) def add(self, line: str): - """ add a line to the block """ + """ Add a line to the block. """ if self.id is None: self.id = lineToId(line) self.text += line self.size += 1 def end(self): - """ end the block """ + """ End the block. """ self.finished = True def __iter__(self): - """ iterate over all the lines of the block """ + """ Iterate over all the lines of the block. """ return iter(self.text.split("\n")) def getIndent(line: str): - """ get the indent part of a line of code """ + """ Get the indent part of a line of code. """ i = 0 for i in range(len(line)): if line[i] != " " and line[i] != "\t": @@ -921,7 +942,7 @@ def getIndent(line: str): def addLineCounter(fp: IO): - """ wrap a file pointer to store th line numbers """ + """ Wrap a file pointer to store th line numbers. """ fp.lineno = 0 write = fp.write @@ -933,7 +954,7 @@ def write_with_linenumbers(line: str): def lineToId(line: str): - """ get the id of a line, e.g. part which specifies which figure it refers to """ + """ Get the id of a line, e.g. part which specifies which figure it refers to. """ line = line.strip() line = line.split(".ax_dict")[0] if line.startswith("fig = "): @@ -942,7 +963,7 @@ def lineToId(line: str): def insertTextToFile(new_block: str, stack_pos: traceback.FrameSummary, figure_id_line: str): - """ insert a text block into a file """ + """ Insert a text block into a file. """ figure_id_line = lineToId(figure_id_line) block = None written = False diff --git a/pylustrator/components/align.py b/pylustrator/components/align.py index 507e22c..efc37c9 100644 --- a/pylustrator/components/align.py +++ b/pylustrator/components/align.py @@ -40,7 +40,7 @@ def __init__(self, layout: QtWidgets.QLayout, signals: "Signals"): self.layout.addStretch() def execute_action(self, act: str): - """ execute an alignment action """ + """ Execute an alignment action. """ self.fig.selection.align_points(act) self.fig.selection.update_selection_rectangles() self.fig.canvas.draw() diff --git a/pylustrator/components/figure_previews.py b/pylustrator/components/figure_previews.py index 0331bbd..b0e4fc9 100644 --- a/pylustrator/components/figure_previews.py +++ b/pylustrator/components/figure_previews.py @@ -24,9 +24,9 @@ def addFigure(self, figure): pix.fill(QtGui.QColor("#666666")) target_width = 150 - target_height = 150*9/16 + target_height = 150 * 9 / 16 w, h = figure.get_size_inches() - figure.savefig("tmp.png", dpi=min([target_width/w, target_height/h])) + figure.savefig("tmp.png", dpi=min([target_width / w, target_height / h])) button.setStyleSheet("background:#d1d1d1") button.setMaximumWidth(150) button.setMaximumHeight(150) @@ -34,6 +34,6 @@ def addFigure(self, figure): pix.load("tmp.png") # scale pixmap to fit in label'size and keep ratio of pixmap - #pix = pix.scaled(160, 90, QtCore.Qt.KeepAspectRatio) + # pix = pix.scaled(160, 90, QtCore.Qt.KeepAspectRatio) button.setPixmap(pix) button.mousePressEvent = lambda e: self.parent.setFigure(figure) diff --git a/pylustrator/components/matplotlibwidget.py b/pylustrator/components/matplotlibwidget.py index fd6f3a6..3c6eaaa 100644 --- a/pylustrator/components/matplotlibwidget.py +++ b/pylustrator/components/matplotlibwidget.py @@ -72,6 +72,7 @@ def __init__(self, parent=None, num=1, size=None, dpi=100, figure=None, *args, * self.timer.setInterval(300) self.timer.timeout.connect(self.draw) timer = None + def schedule_draw(self): if self.quick_draw is True: return super().draw() @@ -82,7 +83,7 @@ def draw(self): self.timer.stop() import traceback - #print(traceback.print_stack()) + # print(traceback.print_stack()) t = time.time() super().draw() duration = time.time() - t diff --git a/pylustrator/components/plot_layout.py b/pylustrator/components/plot_layout.py index 9c5eb1d..4ba9bfa 100644 --- a/pylustrator/components/plot_layout.py +++ b/pylustrator/components/plot_layout.py @@ -26,6 +26,7 @@ def mouseReleaseEvent(self, e): if self.grabber_pressed: self.grabber_pressed.mouseReleaseEvent(e) + class MyView(QtWidgets.QGraphicsView): grabber_found = False grabber_pressed = None @@ -60,15 +61,19 @@ def mousePressEvent(self, e): e.ignore() self.canvas_canvas.mousePressEvent(e) + class MyEvent: def __init__(self, x, y): self.x = x self.y = y + + class MyRect(QtWidgets.QGraphicsRectItem): w = 10 + def __init__(self, x, y, grabber): self.grabber = grabber - super().__init__(x-self.w/2, y-self.w/2, self.w, self.w) + super().__init__(x - self.w / 2, y - self.w / 2, self.w, self.w) def mousePressEvent(self, e): super().mousePressEvent(e) @@ -82,6 +87,7 @@ def mouseReleaseEvent(self, e): p = e.scenePos() self.grabber.button_release_event(MyEvent(p.x(), self.h - p.y())) + class Canvas(QtWidgets.QWidget): fitted_to_view = False footer_label = None @@ -90,8 +96,7 @@ class Canvas(QtWidgets.QWidget): canvas = None def __init__(self, signals: "Signals"): - """ The wrapper around the matplotlib canvas to create a more image editor like canvas with background and side rulers - """ + """The wrapper around the matplotlib canvas to create a more image editor like canvas with background and side rulers.""" super().__init__() signals.figure_changed.connect(self.setFigure) @@ -170,7 +175,7 @@ def setFooters(self, footer, footer2): self.footer_label2 = footer2 def updateRuler(self): - """ update the ruler around the figure to show the dimensions """ + """Update the ruler around the figure to show the dimensions.""" trans = transforms.Affine2D().scale(1. / 2.54, 1. / 2.54) + self.fig.dpi_scale_trans l = 20 l1 = 20 @@ -220,7 +225,7 @@ def updateRuler(self): positions = np.hstack([np.arange(0, start_x, -dx)[::-1], np.arange(0, end_x, dx)]) for i, pos_cm in enumerate(positions): - #for i, pos_cm in enumerate(np.arange(start_x, end_x, dx)): + # for i, pos_cm in enumerate(np.arange(start_x, end_x, dx)): x = (trans.transform((pos_cm, 0))[0] + offset) if pos_cm % big_lines == 0: painterX.drawLine(int(x), int(l - l1 - 1), int(x), int(l - 1)) @@ -248,8 +253,8 @@ def updateRuler(self): big_lines = 1 medium_lines = 0.5 - pix_per_cm = trans.transform((0, 1))[1]-trans.transform((0, 0))[1] - big_lines = int(np.ceil(self.fontMetrics().height()*5/pix_per_cm)) + pix_per_cm = trans.transform((0, 1))[1] - trans.transform((0, 0))[1] + big_lines = int(np.ceil(self.fontMetrics().height() * 5 / pix_per_cm)) medium_lines = big_lines / 2 dy = big_lines / 10 @@ -261,7 +266,7 @@ def updateRuler(self): text = str("%d" % np.round(pos_cm)) o = 0 for ti, t in enumerate(text): - painterY.drawText(int(o), int(y + 3 + self.fontMetrics().height()*ti), + painterY.drawText(int(o), int(y + 3 + self.fontMetrics().height() * ti), int(o + self.fontMetrics().width("0")), int(self.fontMetrics().height()), QtCore.Qt.AlignCenter, t) elif pos_cm % medium_lines == 0: @@ -301,13 +306,13 @@ def updateRuler(self): self.canvas_border.setMaximumSize(w + 2, h + 2) def fitToView(self, change_dpi: bool = False): - """ fit the figure to the view """ + """ Fit the figure to the view. """ self.fitted_to_view = True if change_dpi: w, h = self.canvas.get_width_height() factor = min((self.canvas_canvas.width() - 30) / w, (self.canvas_canvas.height() - 30) / h) self.fig.set_dpi(self.fig.get_dpi() * factor) - #self.fig.canvas.draw() + # self.fig.canvas.draw() self.canvas.updateGeometry() w, h = self.canvas.get_width_height() @@ -318,7 +323,7 @@ def fitToView(self, change_dpi: bool = False): int((self.canvas_canvas.height() - h) / 2 + 10)) self.updateRuler() - #self.fig.canvas.draw() + # self.fig.canvas.draw() else: w, h = self.canvas.get_width_height() @@ -330,24 +335,24 @@ def fitToView(self, change_dpi: bool = False): self.updateRuler() def canvas_key_press(self, event: QtCore.QEvent): - """ when a key in the canvas widget is pressed """ + """ When a key in the canvas widget is pressed. """ if event.key == "control": self.control_modifier = True def canvas_key_release(self, event: QtCore.QEvent): - """ when a key in the canvas widget is released """ + """ When a key in the canvas widget is released. """ if event.key == "control": self.control_modifier = False def moveCanvasCanvas(self, offset_x: float, offset_y: float): - """ when the canvas is panned """ + """ When the canvas is panned. """ p = self.canvas_container.pos() self.canvas_container.move(int(p.x() + offset_x), int(p.y() + offset_y)) self.updateRuler() def scroll_event(self, event: QtCore.QEvent): - """ when the mouse wheel is used to zoom the figure """ + """ When the mouse wheel is used to zoom the figure. """ if self.control_modifier: new_dpi = self.fig.get_dpi() + 10 * event.step # prevent zoom to be too far out @@ -377,24 +382,24 @@ def scroll_event(self, event: QtCore.QEvent): bb = self.fig.axes[0].get_position() def resizeEvent(self, event: QtCore.QEvent): - """ when the window is resized """ + """ When the window is resized. """ if self.fitted_to_view: self.fitToView(True) else: self.updateRuler() def showEvent(self, event: QtCore.QEvent): - """ when the window is shown """ + """ When the window is shown. """ self.fitToView(True) self.updateRuler() def button_press_event(self, event: QtCore.QEvent): - """ when a mouse button is pressed """ + """ When a mouse button is pressed. """ if event.button == 2: self.drag = np.array([event.x, event.y]) def mouse_move_event(self, event: QtCore.QEvent): - """ when the mouse is moved """ + """ When the mouse is moved. """ if self.drag is not None: pos = np.array([event.x, event.y]) offset = pos - self.drag @@ -410,12 +415,12 @@ def mouse_move_event(self, event: QtCore.QEvent): self.footer_label2.setText("") def button_release_event(self, event: QtCore.QEvent): - """ when the mouse button is released """ + """ When the mouse button is released. """ if event.button == 2: self.drag = None def keyPressEvent(self, event: QtCore.QEvent): - """ when a key is pressed """ + """ When a key is pressed. """ if event.key() == QtCore.Qt.Key_Control: self.control_modifier = True if event.key() == QtCore.Qt.Key_Left: @@ -431,27 +436,26 @@ def keyPressEvent(self, event: QtCore.QEvent): self.fitToView(True) def keyReleaseEvent(self, event: QtCore.QEvent): - """ when a key is released """ + """ When a key is released. """ if event.key() == QtCore.Qt.Key_Control: self.control_modifier = False def updateFigureSize(self): - """ update the size of the figure """ + """ Update the size of the figure. """ w, h = self.canvas.get_width_height() self.canvas_container.setMinimumSize(w, h) self.canvas_container.setMaximumSize(w, h) def changedFigureSize(self, size: tuple): - """ change the size of the figure """ + """ Change the size of the figure. """ self.fig.set_size_inches(np.array(size) / 2.54) self.fig.canvas.draw() - class ToolBar(QtWidgets.QToolBar): def __init__(self, canvas: Canvas, figure: Figure): - """ A widget that displays a toolbar similar to the default Matplotlib toolbar (for the zoom and pan tool) + """ A widget that displays a toolbar similar to the default Matplotlib toolbar (for the zoom and pan tool). Args: canvas: the canvas of the figure @@ -492,8 +496,8 @@ def __init__(self, canvas: Canvas, figure: Figure): self.prev_active = 'DRAG' def icon(self, name: str): - """ get an icon with the given filename """ - pm = QtGui.QPixmap(os.path.join(os.path.dirname(__file__), "..","icons", name)) + """ Get an icon with the given filename. """ + pm = QtGui.QPixmap(os.path.join(os.path.dirname(__file__), "..", "icons", name)) if hasattr(pm, 'setDevicePixelRatio'): try: # older mpl < 3.5.0 pm.setDevicePixelRatio(self.canvas._dpi_ratio) @@ -503,12 +507,12 @@ def icon(self, name: str): return QtGui.QIcon(pm) def setSelect(self): - """ select the pylustrator selection and drag tool """ + """ Select the pylustrator selection and drag tool. """ self.fig.figure_dragger.activate() - if self.prev_active=="PAN": + if self.prev_active == "PAN": self.navi_toolbar.pan() - elif self.prev_active=="ZOOM": + elif self.prev_active == "ZOOM": self.navi_toolbar.zoom() self.prev_active = 'DRAG' @@ -516,7 +520,7 @@ def setSelect(self): self.navi_toolbar._active = 'DRAG' def setPan(self): - """ select the mpl pan tool """ + """ Select the mpl pan tool. """ if self.prev_active == "DRAG": self.fig.figure_dragger.deactivate() @@ -526,7 +530,7 @@ def setPan(self): self.prev_active = 'PAN' def setZoom(self): - """ select the mpl zoom tool """ + """ Select the mpl zoom tool. """ if self.prev_active == "DRAG": self.fig.figure_dragger.deactivate() @@ -535,6 +539,7 @@ def setZoom(self): self.prev_active = 'ZOOM' + class PlotLayout(QtWidgets.QWidget): toolbar = None diff --git a/pylustrator/components/qitem_properties.py b/pylustrator/components/qitem_properties.py index fcece3b..084b4ad 100644 --- a/pylustrator/components/qitem_properties.py +++ b/pylustrator/components/qitem_properties.py @@ -52,7 +52,7 @@ class TextPropertiesWidget(QtWidgets.QWidget): target_list = None def __init__(self, layout: QtWidgets.QLayout): - """ A widget to edit the properties of a Matplotlib text + """ A widget to edit the properties of a Matplotlib text. Args: layout: the layout to which to add the widget @@ -67,7 +67,7 @@ def __init__(self, layout: QtWidgets.QLayout): align_group = QtWidgets.QButtonGroup(self) for align in self.align_names: button = QtWidgets.QPushButton(qta.icon("fa5s.align-" + align), "") - button.setToolTip("align "+align) + button.setToolTip("align " + align) button.setCheckable(True) button.clicked.connect(lambda x, name=align: self.changeAlign(name)) self.layout.addWidget(button) @@ -97,13 +97,13 @@ def __init__(self, layout: QtWidgets.QLayout): self.layout.addWidget(self.label) self.label.clicked.connect(self.selectFont) - #self.button_delete = QtWidgets.QPushButton(qta.icon("fa5s.trash"), "") - #self.button_delete.clicked.connect(self.delete) - #self.button_delete.setToolTip("delete") - #self.layout.addWidget(self.button_delete) + # self.button_delete = QtWidgets.QPushButton(qta.icon("fa5s.trash"), "") + # self.button_delete.clicked.connect(self.delete) + # self.button_delete.setToolTip("delete") + # self.layout.addWidget(self.button_delete) def convertMplWeightToQtWeight(self, weight: str) -> int: - """ convert a font weight string to a weight enumeration of Qt """ + """ Convert a font weight string to a weight enumeration of Qt. """ weight_dict = {'normal': QtGui.QFont.Normal, 'bold': QtGui.QFont.Bold, 'heavy': QtGui.QFont.ExtraBold, 'light': QtGui.QFont.Light, 'ultrabold': QtGui.QFont.Black, 'ultralight': QtGui.QFont.ExtraLight} if weight in weight_dict: @@ -111,7 +111,7 @@ def convertMplWeightToQtWeight(self, weight: str) -> int: return weight_dict["normal"] def convertQtWeightToMplWeight(self, weight: int) -> str: - """ convert a Qt weight value to a string for use in matmplotlib """ + """ Convert a Qt weight value to a string for use in matmplotlib. """ weight_dict = {QtGui.QFont.Normal: 'normal', QtGui.QFont.Bold: 'bold', QtGui.QFont.ExtraBold: 'heavy', QtGui.QFont.Light: 'light', QtGui.QFont.Black: 'ultrabold', QtGui.QFont.ExtraLight: 'ultralight'} if weight in weight_dict: @@ -119,7 +119,7 @@ def convertQtWeightToMplWeight(self, weight: int) -> str: return "normal" def selectFont(self): - """ open a font select dialog """ + """ Open a font select dialog. """ font0 = QtGui.QFont() font0.setFamily(self.target.get_fontname()) font0.setWeight(self.convertMplWeightToQtWeight(self.target.get_weight())) @@ -140,7 +140,7 @@ def selectFont(self): self.setTarget(self.target_list) def setTarget(self, element: Artist): - """ set the target artist for this widget """ + """ Set the target artist for this widget. """ if isinstance(element, list): self.target_list = element element = element[0] @@ -163,7 +163,7 @@ def setTarget(self, element: Artist): self.target = element def delete(self): - """ delete the target text """ + """ Delete the target text. """ if self.target is not None: fig = main_figure(self.target) fig.change_tracker.removeElement(self.target) @@ -172,35 +172,35 @@ def delete(self): fig.canvas.draw() def changeWeight(self, checked: bool): - """ set bold or normal """ + """ Set bold or normal. """ if self.target: with UndoRedo(self.target_list, "Change weight"): for element in self.target_list: element.set_weight("bold" if checked else "normal") def changeStyle(self, checked: bool): - """ set italic or normal """ + """ Set italic or normal. """ if self.target: with UndoRedo(self.target_list, "Change style"): for element in self.target_list: element.set_style("italic" if checked else "normal") def changeColor(self, color: str): - """ set the text color """ + """ Set the text color. """ if self.target: with UndoRedo(self.target_list, "Change color"): for element in self.target_list: element.set_color(color) def changeAlign(self, align: str): - """ set the text algin """ + """ Set the text align. """ if self.target: with UndoRedo(self.target_list, "Change alignment"): for element in self.target_list: element.set_ha(align) def changeFontSize(self, value: int): - """ set the font size """ + """ Set the font size. """ if self.target: with UndoRedo(self.target_list, "Change font size"): for element in self.target_list: @@ -214,7 +214,7 @@ class TextPropertiesWidget2(QtWidgets.QWidget): target_list = None def __init__(self, layout: QtWidgets.QLayout): - """ A widget to edit the properties of a Matplotlib text + """ A widget to edit the properties of a Matplotlib text. Args: layout: the layout to which to add the widget @@ -271,7 +271,7 @@ def __init__(self, layout: QtWidgets.QLayout): self.propertiesChanged.connect(lambda: self.target and main_figure(self.target).signals.figure_selection_property_changed.emit()) def convertMplWeightToQtWeight(self, weight: str) -> int: - """ convert a font weight string to a weight enumeration of Qt """ + """ Convert a font weight string to a weight enumeration of Qt. """ weight_dict = {'normal': QtGui.QFont.Normal, 'bold': QtGui.QFont.Bold, 'heavy': QtGui.QFont.ExtraBold, 'light': QtGui.QFont.Light, 'ultrabold': QtGui.QFont.Black, 'ultralight': QtGui.QFont.ExtraLight} if weight in weight_dict: @@ -279,7 +279,7 @@ def convertMplWeightToQtWeight(self, weight: str) -> int: return weight_dict["normal"] def convertQtWeightToMplWeight(self, weight: int) -> str: - """ convert a Qt weight value to a string for use in matmplotlib """ + """ Convert a Qt weight value to a string for use in matmplotlib. """ weight_dict = {QtGui.QFont.Normal: 'normal', QtGui.QFont.Bold: 'bold', QtGui.QFont.ExtraBold: 'heavy', QtGui.QFont.Light: 'light', QtGui.QFont.Black: 'ultrabold', QtGui.QFont.ExtraLight: 'ultralight'} if weight in weight_dict: @@ -287,7 +287,7 @@ def convertQtWeightToMplWeight(self, weight: int) -> str: return "normal" def selectFont(self): - """ open a font select dialog """ + """ Open a font select dialog. """ font0 = QtGui.QFont() font0.setFamily(self.target.get_fontname()) font0.setWeight(self.convertMplWeightToQtWeight(self.target.get_weight())) @@ -305,11 +305,11 @@ def selectFont(self): self.properties["fontstyle"] = style self.propertiesChanged.emit() - #main_figure(self.target).canvas.draw() + # main_figure(self.target).canvas.draw() self.setTarget(self.target_list) def setTarget(self, element: Artist): - """ set the target artist for this widget """ + """ Set the target artist for this widget. """ self.noSignal = True try: if len(element) == 0: @@ -333,7 +333,7 @@ def setTarget(self, element: Artist): self.button_color.setColor(element.get_color()) for name, name2, type_, default_ in self.property_names: - value = getattr(element, "get_"+name2)() + value = getattr(element, "get_" + name2)() self.properties[name] = value self.target = element @@ -341,7 +341,7 @@ def setTarget(self, element: Artist): self.noSignal = False def delete(self): - """ delete the target text """ + """ Delete the target text. """ if self.target is not None: fig = main_figure(self.target) fig.change_tracker.removeElement(self.target) @@ -350,27 +350,27 @@ def delete(self): fig.canvas.draw() def changeWeight(self, checked: bool): - """ set bold or normal """ + """ Set bold or normal. """ self.properties["fontweight"] = "bold" if checked else "normal" self.propertiesChanged.emit() def changeStyle(self, checked: bool): - """ set italic or normal """ + """ Set italic or normal. """ self.properties["fontstyle"] = "italic" if checked else "normal" self.propertiesChanged.emit() def changeColor(self, color: str): - """ set the text color """ + """ Set the text color. """ self.properties["color"] = color self.propertiesChanged.emit() def changeAlign(self, align: str): - """ set the text algin """ + """ Set the text align. """ self.properties["horizontalalignment"] = align self.propertiesChanged.emit() def changeFontSize(self, value: int): - """ set the font size """ + """ Set the font size. """ if self.noSignal: return self.properties["fontsize"] = value @@ -383,7 +383,7 @@ class LegendPropertiesWidget(QtWidgets.QWidget): target_list = None def __init__(self, layout: QtWidgets.QLayout): - """ A widget that allows to change to properties of a matplotlib legend + """ A widget that allows to change to properties of a matplotlib legend. Args: layout: the layout to which to add the widget @@ -445,7 +445,7 @@ def __init__(self, layout: QtWidgets.QLayout): if icon is not None and getattr(widget, "label", None): from pathlib import Path pix = QtGui.QPixmap(str(Path(__file__).parent.parent / "icons" / icon)) - pix = pix.scaledToWidth(int(28*QtGui.QGuiApplication.primaryScreen().logicalDotsPerInch()/96), QtCore.Qt.SmoothTransformation) + pix = pix.scaledToWidth(int(28 * QtGui.QGuiApplication.primaryScreen().logicalDotsPerInch() / 96), QtCore.Qt.SmoothTransformation) widget.setToolTip(name) widget.label.setToolTip(name) widget.label.setPixmap(pix) @@ -453,7 +453,7 @@ def __init__(self, layout: QtWidgets.QLayout): self.widgets[name] = widget def changePropertiy(self, name: str, value: Any): - """ change the property with the given name to the provided value """ + """ Change the property with the given name to the provided value. """ if self.target is None: return @@ -461,6 +461,7 @@ def changePropertiy(self, name: str, value: Any): self.properties[name] = value new_properties = self.properties.copy() target = self.target + def setProperties(properties): nonlocal target bbox = target.get_frame().get_bbox() @@ -485,7 +486,7 @@ def redo(): main_figure(target).change_tracker.addEdit([undo, redo, f"Legend {name}"]) def setTarget(self, element: Artist): - """ set the target artist for this widget """ + """ Set the target artist for this widget. """ if isinstance(element, list): self.target_list = element element = element[0] @@ -519,7 +520,7 @@ def setTarget(self, element: Artist): class QTickEdit(QtWidgets.QWidget): def __init__(self, axis: str, signal_target_changed: QtCore.Signal): - """ A widget to change the tick properties + """ A widget to change the tick properties. Args: axis: whether to use the "x" or "y" axis @@ -546,7 +547,7 @@ def __init__(self, axis: str, signal_target_changed: QtCore.Signal): self.input_scale = ComboWidget(self.layout, axis + "-Scale", ["linear", "log", "symlog", "logit"]) self.input_scale.editingFinished.connect(self.scaleChanged) - #self.input_scale.link(axis + "scale", signal_target_changed) + # self.input_scale.link(axis + "scale", signal_target_changed) self.input_font = TextPropertiesWidget2(self.layout) self.input_font.propertiesChanged.connect(self.fontStateChanged) @@ -559,7 +560,7 @@ def __init__(self, axis: str, signal_target_changed: QtCore.Signal): self.button_ok.clicked.connect(self.hide) def parseTickLabel(self, line: str) -> (float, str): - """ interpret the tick value specified in line """ + """ Interpret the tick value specified in line. """ import re line = line.replace("−", "-") match = re.match(r"\$\\mathdefault{(([-.\d]*)\\times)?([-.\d]+)\^{([-.\d]+)}}\$", line) @@ -585,7 +586,7 @@ def parseTickLabel(self, line: str) -> (float, str): return number, line def formatTickLabel(self, line: str) -> (float, str): - """ interpret the tick label specified in line""" + """ Interpret the tick label specified in line.""" import re line = line.replace("−", "-") match = re.match(r"\s*(([-.\d]*)\s*x)?\s*([-.\d]+)\s*\^\s*([-.\d]+)\s*\"(.+)?\"", line) @@ -618,7 +619,7 @@ def formatTickLabel(self, line: str) -> (float, str): return number, line def setTarget(self, element: Artist): - """ set the target Artist for this widget""" + """ Set the target Artist for this widget.""" self.element = element self.fig = main_figure(element) min, max = getattr(self.element, "get_" + self.axis + "lim")() @@ -668,7 +669,7 @@ def setTarget(self, element: Artist): self.input_font.setTarget(ticks) def parseTicks(self, string: str): - """ parse a list of given ticks """ + """ Parse a list of given ticks. """ try: ticks = [] labels = [] @@ -693,13 +694,13 @@ def parseTicks(self, string: str): return ticks, labels def str(self, object: Any): - """ serialize an object and interpret nan values """ + """ Serialize an object and interpret nan values. """ if str(object) == "nan": return "np.nan" return str(object) def ticksChanged2(self): - """ when the minor ticks changed """ + """ When the minor ticks changed. """ ticks, labels = self.parseTicks(self.input_ticks2.text()) elements = [self.element] @@ -753,7 +754,7 @@ def getFontProperties(self): value = self.input_font.properties[name] if default_ is not None and value == default_: continue - #if default_ is None and value == plt.rcParams["legend." + name]: + # if default_ is None and value == plt.rcParams["legend." + name]: # continue if type_ == str: prop_copy[name] = '"' + value + '"' @@ -764,10 +765,10 @@ def getFontProperties(self): def fontStateChanged(self): self.ticksChanged() - #fig.change_tracker.addChange(axes, ".legend(%s)" % (", ".join("%s=%s" % (k, v) for k, v in prop_copy.items()))) + # fig.change_tracker.addChange(axes, ".legend(%s)" % (", ".join("%s=%s" % (k, v) for k, v in prop_copy.items()))) def scaleChanged(self): - """ when the scale changed """ + """ When the scale changed. """ elements = [self.element] elements += [element.target for element in main_figure(self.element).selection.targets if element.target != self.element and isinstance(element.target, Axes)] @@ -779,7 +780,7 @@ def scaleChanged(self): element.set(**kwargs) def ticksChanged(self): - """ when the major ticks changed """ + """ When the major ticks changed. """ ticks, labels = self.parseTicks(self.input_ticks.text()) elements = [self.element] @@ -791,7 +792,7 @@ def ticksChanged(self): current_ticks = getattr(elem, "get_" + self.axis + "ticks")() current_ticklabels = [t.get_text() for t in getattr(elem, "get_" + self.axis + "ticklabels")()] if len(current_ticks) != len(ticks) or (current_ticks != ticks).any() or \ - len(current_ticklabels) != len(labels) or current_ticklabels != labels: + len(current_ticklabels) != len(labels) or current_ticklabels != labels: changed = True if changed is False: return @@ -874,7 +875,7 @@ class QAxesProperties(QtWidgets.QWidget): targetChanged_wrapped = QtCore.Signal(object) def __init__(self, layout: QtWidgets.QLayout, axis: str, signal_target_changed: QtCore.Signal): - """ a widget to change the properties of an axes (label, limits) + """ A widget to change the properties of an axes (label, limits). Args: layout: the layout to which to add this widget @@ -915,12 +916,12 @@ def wrapTargetLabel(axis_object): self.tick_edit = QTickEdit(axis, signal_target_changed) def showTickWidget(self): - """ open the tick edit dialog """ + """ Open the tick edit dialog. """ self.tick_edit.setTarget(self.element) self.tick_edit.show() def setTarget(self, element: Artist): - """ set the target Artist of this widget """ + """ Set the target Artist of this widget. """ self.element = element if isinstance(element, Axes): @@ -933,7 +934,7 @@ class QAxesProperties(QtWidgets.QWidget): targetChanged_wrapped = QtCore.Signal(object) def __init__(self, layout: QtWidgets.QLayout, axis: str, signal_target_changed: QtCore.Signal): - """ a widget to change the properties of an axes (label, limits) + """ A widget to change the properties of an axes (label, limits). Args: layout: the layout to which to add this widget @@ -965,12 +966,12 @@ def __init__(self, layout: QtWidgets.QLayout, axis: str, signal_target_changed: self.tick_edit = QTickEdit(axis, signal_target_changed) def showTickWidget(self): - """ open the tick edit dialog """ + """ Open the tick edit dialog. """ self.tick_edit.setTarget(self.element) self.tick_edit.show() def setTarget(self, element: Artist): - """ set the target Artist of this widget """ + """ Set the target Artist of this widget. """ self.element = element if isinstance(element, Axes): @@ -1000,13 +1001,14 @@ def saveLim(self): for element in elements: element.set(**{f"{self.axis}lim": limits}) + class QItemProperties(QtWidgets.QWidget): targetChanged = QtCore.Signal(object) valueChanged = QtCore.Signal(tuple) element = None def __init__(self, layout: QtWidgets.QLayout, signals: "Signals"): - """ a widget that holds all the properties to set and the tree view + """ A widget that holds all the properties to set and the tree view. Args: layout: the layout to which to add the widget @@ -1124,7 +1126,7 @@ def __init__(self, layout: QtWidgets.QLayout, signals: "Signals"): self.setMinimumWidth(100) def select_element(self, element: Artist): - """ select an element """ + """ Select an element. """ if element is None: self.setElement(self.fig) else: @@ -1134,7 +1136,7 @@ def setFigure(self, fig): self.fig = fig def buttonAddImageClicked(self): - """ when the button 'add image' is clicked """ + """ When the button 'add image' is clicked. """ fig = self.fig def addChange(element, command): @@ -1177,7 +1179,7 @@ def addChange(element, command): self.input_text.input1.setFocus() def buttonAddTextClicked(self): - """ when the button 'add text' is clicked """ + """ When the button 'add text' is clicked. """ if isinstance(self.element, Axes): text = self.element.text(0.5, 0.5, "New Text", transform=self.element.transAxes) text.is_new_text = True @@ -1202,7 +1204,7 @@ def buttonAddTextClicked(self): self.input_text.input1.setFocus() def buttonAddAnnotationClicked(self): - """ when the button 'add annoations' is clicked """ + """ When the button 'add annoations' is clicked. """ text = self.element.annotate("New Annotation", (self.element.get_xlim()[0], self.element.get_ylim()[0]), (np.mean(self.element.get_xlim()), np.mean(self.element.get_ylim())), arrowprops={"arrowstyle": '->'}) @@ -1220,9 +1222,9 @@ def buttonAddAnnotationClicked(self): self.input_text.input1.setFocus() def buttonAddRectangleClicked(self): - """ when the button 'add rectangle' is clicked """ + """ When the button 'add rectangle' is clicked. """ p = mpl.patches.Rectangle((self.element.get_xlim()[0], self.element.get_ylim()[0]), - width=np.mean(self.element.get_xlim()), height=np.mean(self.element.get_ylim()), ) + width=np.mean(self.element.get_xlim()), height=np.mean(self.element.get_ylim())) self.element.add_patch(p) self.fig.change_tracker.addChange(self.element, @@ -1239,7 +1241,7 @@ def buttonAddRectangleClicked(self): self.input_text.input1.setFocus() def buttonAddArrowClicked(self): - """ when the button 'add arrow' is clicked """ + """ When the button 'add arrow' is clicked. """ p = mpl.patches.FancyArrowPatch((self.element.get_xlim()[0], self.element.get_ylim()[0]), (np.mean(self.element.get_xlim()), np.mean(self.element.get_ylim())), arrowstyle="Simple,head_length=10,head_width=10,tail_width=2", @@ -1260,9 +1262,9 @@ def buttonAddArrowClicked(self): self.input_text.input1.setFocus() def buttonDespineClicked(self): - """ despine the target """ - + """ Despine the target. """ elements = [element.target for element in main_figure(self.element).selection.targets if isinstance(element.target, Axes)] + def is_despined(elem): return elem.spines['right'].get_visible() and elem.spines['top'].get_visible() despined = [is_despined(elem) for elem in elements] @@ -1275,7 +1277,7 @@ def is_despined(elem): element.spines[spine].set_visible(new_value) def buttonGridClicked(self): - """ toggle the grid of the target """ + """ Toggle the grid of the target. """ elements = [element.target for element in main_figure(self.element).selection.targets if isinstance(element.target, Axes)] has_grid = getattr(self.element.xaxis, "_gridOnMajor", False) or getattr(self.element.xaxis, "_major_tick_kw", {"gridOn": False})['gridOn'] @@ -1288,6 +1290,7 @@ def set_false(): for element in elements: element.grid(False) self.fig.change_tracker.addChange(element, ".grid(False)") + def set_true(): for element in elements: element.grid(True) @@ -1302,7 +1305,7 @@ def set_true(): self.fig.canvas.draw() def buttonLegendClicked(self): - """ add a legend to the target """ + """ Add a legend to the target. """ self.element.legend() self.fig.change_tracker.addChange(self.element, ".legend()") self.fig.figure_dragger.make_dragable(self.element.get_legend()) @@ -1310,7 +1313,7 @@ def buttonLegendClicked(self): self.signals.figure_element_child_created.emit(self.element) def changePickable(self): - """ make the target pickable """ + """ Make the target pickable. """ if self.input_picker.isChecked(): self.element._draggable.connect() else: @@ -1318,7 +1321,7 @@ def changePickable(self): self.signals.figure_element_child_created.emit(self.element) def setElement(self, element: Artist): - """ set the target Artist of this widget """ + """ Set the target Artist of this widget. """ self.label.setText(str(element)) self.element = element try: diff --git a/pylustrator/components/qpos_and_size.py b/pylustrator/components/qpos_and_size.py index 97bcccd..d87c8d5 100644 --- a/pylustrator/components/qpos_and_size.py +++ b/pylustrator/components/qpos_and_size.py @@ -24,7 +24,7 @@ class QPosAndSize(QtWidgets.QWidget): scale_type = 0 def __init__(self, layout: QtWidgets.QLayout, signals: "Signals"): - """ a widget that holds all the properties to set and the tree view + """ A widget that holds all the properties to set and the tree view. Args: layout: the layout to which to add the widget @@ -60,7 +60,7 @@ def setFigure(self, figure): self.fig = figure def select_element(self, element): - """ select an element """ + """ Select an element. """ if element is None: self.setElement(self.fig) else: @@ -70,23 +70,23 @@ def selection_moved(self): self.setElement(self.element) def changeTransform(self): - """ change the transform and the units of the position and size widgets """ + """ Change the transform and the units of the position and size widgets. """ name = self.input_transform.text() - self.transform_index = ["cm", "in", "px", "none"].index(name)#transform_index + self.transform_index = ["cm", "in", "px", "none"].index(name) # transform_index if name == "none": name = "" self.input_shape.setUnit(name) self.input_position.setUnit(name) self.setElement(self.element) - def changeTransform2(self):#, state: int, name: str): - """ when the dimension change type is changed from 'scale' to 'bottom right' or 'bottom left' """ + def changeTransform2(self): # , state: int, name: str): + """ When the dimension change type is changed from 'scale' to 'bottom right' or 'bottom left'. """ name = self.input_shape_transform.text() self.scale_type = ["scale", "bottom right", "top left"].index(name) - #self.scale_type = state + # self.scale_type = state def changePos(self, value_x: float, value_y: float): - """ change the position of an axes """ + """ Change the position of an axes. """ elements = [self.element] elements += [element.target for element in main_figure(self.element).selection.targets] @@ -141,7 +141,7 @@ def undo(): self.fig.canvas.draw() def changeSize(self, value: list): - """ change the size of an axes or figure """ + """ Change the size of an axes or figure. """ if isinstance(self.element, Figure): if self.scale_type == 0: @@ -207,9 +207,8 @@ def undo(): self.fig.signals.figure_selection_property_changed.emit() self.fig.canvas.draw() - def getTransform(self, element: Artist) -> Optional[mpl.transforms.Transform]: - """ get the transform of an Artist """ + """ Get the transform of an Artist. """ if isinstance(element, Figure): if self.transform_index == 0: return transforms.Affine2D().scale(2.54, 2.54) @@ -233,8 +232,8 @@ def getTransform(self, element: Artist) -> Optional[mpl.transforms.Transform]: return None def setElement(self, element: Artist): - """ set the target Artist of this widget """ - #self.label.setText(str(element)) + """ Set the target Artist of this widget. """ + # self.label.setText(str(element)) self.element = element self.input_shape_transform.setDisabled(True) diff --git a/pylustrator/components/tree_view.py b/pylustrator/components/tree_view.py index 1a2db77..1cd5c0c 100644 --- a/pylustrator/components/tree_view.py +++ b/pylustrator/components/tree_view.py @@ -8,18 +8,18 @@ class myTreeWidgetItem(QtGui.QStandardItem): def __init__(self, parent: QtWidgets.QWidget = None): - """ a tree view item to display the contents of the figure """ + """ A tree view item to display the contents of the figure. """ QtGui.QStandardItem.__init__(self, parent) def __lt__(self, otherItem: QtGui.QStandardItem): - """ how to sort the items """ + """ How to sort the items. """ if self.sort is None: return 0 return self.sort < otherItem.sort class MyTreeView(QtWidgets.QTreeView): - #item_selected = lambda x, y: 0 + # item_selected = lambda x, y: 0 item_clicked = lambda x, y: 0 item_activated = lambda x, y: 0 item_hoverEnter = lambda x, y: 0 @@ -34,7 +34,7 @@ def item_selected(self, x): self.fig.figure_dragger.select_element(x) def __init__(self, signals: "Signals", layout: QtWidgets.QLayout): - """ A tree view to display the contents of a figure + """ A tree view to display the contents of a figure. Args: parent: the parent widget @@ -42,7 +42,7 @@ def __init__(self, signals: "Signals", layout: QtWidgets.QLayout): fig: the target figure """ super().__init__() - #self.setMaximumWidth(300) + # self.setMaximumWidth(300) signals.figure_changed.connect(self.setFigure) signals.figure_element_selected.connect(self.select_element) @@ -77,7 +77,7 @@ def __init__(self, signals: "Signals", layout: QtWidgets.QLayout): self.item_lookup = {} def select_element(self, element: Artist): - """ select an element """ + """ Select an element. """ if element is None: self.setCurrentIndex(self.fig) else: @@ -94,7 +94,7 @@ def setFigure(self, fig): self.setCurrentIndex(self.fig) def selectionChanged(self, selection: QtCore.QItemSelection, y: QtCore.QItemSelection): - """ when the selection in the tree view changes """ + """ When the selection in the tree view changes. """ try: entry = selection.indexes()[0].model().itemFromIndex(selection.indexes()[0]).entry except IndexError: @@ -104,7 +104,7 @@ def selectionChanged(self, selection: QtCore.QItemSelection, y: QtCore.QItemSele self.item_selected(entry) def setCurrentIndex(self, entry: Artist): - """ set the currently selected entry """ + """ Set the currently selected entry. """ while entry: item = self.getItemFromEntry(entry) if item is not None: @@ -119,17 +119,17 @@ def setCurrentIndex(self, entry: Artist): return def treeClicked(self, index: QtCore.QModelIndex): - """ upon selecting one of the tree elements """ + """ Upon selecting one of the tree elements. """ data = index.model().itemFromIndex(index).entry return self.item_clicked(data) def treeActivated(self, index: QtCore.QModelIndex): - """ upon selecting one of the tree elements """ + """ Upon selecting one of the tree elements. """ data = index.model().itemFromIndex(index).entry return self.item_activated(data) def eventFilter(self, object: QtWidgets.QWidget, event: QtCore.QEvent): - """ event filter for tree view port to handle mouse over events and marker highlighting""" + """ Event filter for tree view port to handle mouse over events and marker highlighting.""" if event.type() == QtCore.QEvent.HoverMove: index = self.indexAt(event.pos()) try: @@ -156,24 +156,24 @@ def eventFilter(self, object: QtWidgets.QWidget, event: QtCore.QEvent): return False def queryToExpandEntry(self, entry: Artist) -> list: - """ when expanding a tree item """ + """ When expanding a tree item. """ if entry is None: return [self.fig] return entry.get_children() def getParentEntry(self, entry: Artist) -> Artist: - """ get the parent of an item """ + """ Get the parent of an item. """ return getattr(entry, "tree_parent", None) def getNameOfEntry(self, entry: Artist) -> str: - """ convert an entry to a string """ + """ Convert an entry to a string. """ try: return str(entry) except AttributeError: return "unknown" def getIconOfEntry(self, entry: Artist) -> QtGui.QIcon: - """ get the icon of an entry """ + """ Get the icon of an entry. """ if getattr(entry, "_draggable", None): if entry._draggable.connected: return qta.icon("fa5.hand-paper-o") @@ -183,11 +183,11 @@ def getEntrySortRole(self, entry: Artist): return None def getKey(self, entry: Artist) -> Artist: - """ get the key of an entry, which is the entry itself """ + """ Get the key of an entry, which is the entry itself. """ return entry def getItemFromEntry(self, entry: Artist) -> Optional[QtWidgets.QTreeWidgetItem]: - """ get the tree view item for the given artist """ + """ Get the tree view item for the given artist. """ if entry is None: return None key = self.getKey(entry) @@ -197,12 +197,12 @@ def getItemFromEntry(self, entry: Artist) -> Optional[QtWidgets.QTreeWidgetItem] return None def setItemForEntry(self, entry: Artist, item: QtWidgets.QTreeWidgetItem): - """ store a new artist and tree view widget pair """ + """ Store a new artist and tree view widget pair. """ key = self.getKey(entry) self.item_lookup[key] = item def expand(self, entry: Artist, force_reload: bool = True): - """ expand the children of a tree view item """ + """ Expand the children of a tree view item. """ query = self.queryToExpandEntry(entry) parent_item = self.getItemFromEntry(entry) parent_entry = entry @@ -244,7 +244,7 @@ def expand(self, entry: Artist, force_reload: bool = True): self.addChild(parent_item, entry) def addChild(self, parent_item: QtWidgets.QWidget, entry: Artist, row=None): - """ add a child to a tree view node """ + """ Add a child to a tree view node. """ if parent_item is None: parent_item = self.model @@ -280,7 +280,7 @@ def addChild(self, parent_item: QtWidgets.QWidget, entry: Artist, row=None): return item def TreeExpand(self, index): - """ expand a tree view node """ + """ Expand a tree view node. """ # Get item and entry item = index.model().itemFromIndex(index) entry = item.entry @@ -297,7 +297,7 @@ def TreeExpand(self, index): thread.start() def updateEntry(self, entry: Artist, update_children: bool = False, insert_before: Artist = None, insert_after: Artist = None): - """ update a tree view node """ + """ Update a tree view node. """ # get the tree view item for the database entry item = self.getItemFromEntry(entry) # if we haven't one yet, we have to create it @@ -366,7 +366,7 @@ def updateEntry(self, entry: Artist, update_children: bool = False, insert_befor self.expand(entry, force_reload=True) def deleteEntry(self, entry: Artist): - """ delete an entry from the tree """ + """ Delete an entry from the tree. """ # get the tree view item for the database entry item = self.getItemFromEntry(entry) if item is None: diff --git a/pylustrator/drag_helper.py b/pylustrator/drag_helper.py index 34b79e0..d7c648c 100644 --- a/pylustrator/drag_helper.py +++ b/pylustrator/drag_helper.py @@ -47,7 +47,8 @@ class GrabFunctions(): - """ basic functionality used by all grabbers """ + """ Basic functionality used by all grabbers. """ + figure = None target = None dir = None @@ -63,13 +64,13 @@ def __init__(self, parent, dir: int, no_height=False): self.no_height = no_height def on_motion(self, evt: MouseEvent): - """ callback when the object is moved """ + """ Callback when the object is moved. """ if self.got_artist: self.movedEvent(evt) self.moved = True def button_press_event(self, evt: MouseEvent): - """ when the mouse is pressed """ + """ When the mouse is pressed. """ self.got_artist = True self.moved = False @@ -77,14 +78,14 @@ def button_press_event(self, evt: MouseEvent): self.clickedEvent(evt) def button_release_event(self, event: MouseEvent): - """ when the mouse is released """ + """When the mouse is released.""" if self.got_artist: self.got_artist = False self.figure.canvas.mpl_disconnect(self._c1) self.releasedEvent(event) def clickedEvent(self, event: MouseEvent): - """ when the mouse is clicked """ + """When the mouse is clicked.""" self.parent.start_move() self.mouse_xy = (event.x, event.y) @@ -105,7 +106,7 @@ def clickedEvent(self, event: MouseEvent): self.time = time.time() def releasedEvent(self, event: MouseEvent): - """ when the mouse is released """ + """When the mouse is released.""" for snap in self.snaps: snap.remove() self.snaps = [] @@ -119,7 +120,7 @@ def releasedEvent(self, event: MouseEvent): pass def movedEvent(self, event: MouseEvent): - """ when the mouse is moved """ + """When the mouse is moved.""" if len(self.targets) == 0: return @@ -143,6 +144,7 @@ def movedEvent(self, event: MouseEvent): else: self.figure.canvas.schedule_draw() + class GrabbableRectangleSelection(GrabFunctions): grabbers = None @@ -162,7 +164,6 @@ def __init__(self, figure: Figure, graphics_scene=None): self.graphics_scene_snapparent = QtWidgets.QGraphicsRectItem(0, 0, 0, 0, self.graphics_scene) figure._pyl_graphics_scene_snapparent = self.graphics_scene_snapparent - GrabFunctions.__init__(self, self, DIR_X0 | DIR_X1 | DIR_Y0 | DIR_Y1, no_height=True) self.addGrabber(0, 0, DIR_X0 | DIR_Y0, GrabberGenericRound) @@ -182,7 +183,7 @@ def __init__(self, figure: Figure, graphics_scene=None): self.hide_grabber() def add_target(self, target: Artist): - """ add an artist to the selection """ + """ Add an artist to the selection. """ target = TargetWrapper(target) new_points = np.array(target.get_positions()) @@ -227,7 +228,7 @@ def add_target(self, target: Artist): self.update_extent() def update_extent(self): - """ updates the extend of the selection to all the selected elements """ + """ Updates the extend of the selection to all the selected elements. """ points = None for target in self.targets: new_points = np.array(target.get_positions()) @@ -248,9 +249,9 @@ def update_extent(self): self.positions[2] = np.max(points[:, 0]) self.positions[3] = np.max(points[:, 1]) - if self.positions[2]-self.positions[0] < 0.01: + if self.positions[2] - self.positions[0] < 0.01: self.positions[0], self.positions[2] = self.positions[0] - 0.01, self.positions[0] + 0.01 - if self.positions[3]-self.positions[1] < 0.01: + if self.positions[3] - self.positions[1] < 0.01: self.positions[1], self.positions[3] = self.positions[1] - 0.01, self.positions[1] + 0.01 if self.do_target_scale(): @@ -259,14 +260,14 @@ def update_extent(self): self.hide_grabber() def align_points(self, mode: str): - """ a function to apply the alignment options, e.g. align all selected elements at the top or with equal spacing. """ + """A function to apply the alignment options, e.g. align all selected elements at the top or with equal spacing.""" if len(self.targets) == 0: return if mode == "group": from pylustrator.helper_functions import axes_to_grid - #return axes_to_grid([target.target for target in self.targets], track_changes=True) + # return axes_to_grid([target.target for target in self.targets], track_changes=True) with UndoRedo([target.target for target in self.targets if isinstance(target.target, Axes)], "Grid Align"): axes_to_grid([target.target for target in self.targets if isinstance(target.target, Axes)], track_changes=False) @@ -298,7 +299,7 @@ def distribute(y: int): positions.append(np.min(new_points[:, y])) order = np.argsort(positions) spaces = np.diff(self.positions[y::2])[0] - np.sum(sizes) - spaces /= max([(len(self.targets)-1), 1]) + spaces /= max([(len(self.targets) - 1), 1]) pos = np.min(self.positions[y::2]) for index in order: target = self.targets[index] @@ -339,14 +340,14 @@ def distribute(y: int): self.figure.signals.figure_selection_moved.emit() def update_selection_rectangles(self, use_previous_offset=False): - """ update the selection visualisation """ + """ Update the selection visualisation. """ if len(self.targets) == 0: return if 0: for index, target in enumerate(self.targets): new_points = np.array(target.get_positions()) for i in range(2): - rect = self.targets_rects[index*2+i] + rect = self.targets_rects[index * 2 + i] rect.set_xy(new_points[0]) rect.set_width(new_points[1][0] - new_points[0][0]) rect.set_height(new_points[1][1] - new_points[0][1]) @@ -367,25 +368,25 @@ def update_selection_rectangles(self, use_previous_offset=False): rect.setRect(x0, y0, w0, h0) def remove_target(self, target: Artist): - """ remove an artist from the current selection """ + """ Remove an artist from the current selection. """ targets_non_wrapped = [t.target for t in self.targets] if target not in targets_non_wrapped: return index = targets_non_wrapped.index(target) self.targets.pop(index) - rect1 = self.targets_rects.pop(index*2) - rect2 = self.targets_rects.pop(index*2) + rect1 = self.targets_rects.pop(index * 2) + rect2 = self.targets_rects.pop(index * 2) rect1.scene().removeItem(rect1) rect2.scene().removeItem(rect2) - #self.figure.patches.remove(rect1) - #self.figure.patches.remove(rect2) + # self.figure.patches.remove(rect1) + # self.figure.patches.remove(rect2) if len(self.targets) == 0: self.clear_targets() else: self.update_extent() def update_grabber(self): - """ update the position of the grabber elements """ + """ Update the position of the grabber elements. """ if self.do_target_scale(): for grabber in self.grabbers: grabber.updatePos() @@ -393,66 +394,66 @@ def update_grabber(self): self.hide_grabber() def hide_grabber(self): - """ hide the grabber elements """ + """ Hide the grabber elements. """ for grabber in self.grabbers: grabber.set_xy((-100, -100)) def clear_targets(self): - """ remove all elements from the selection """ + """ Remove all elements from the selection. """ for rect in self.targets_rects: self.graphics_scene.scene().removeItem(rect) - #self.figure.patches.remove(rect) + # self.figure.patches.remove(rect) self.targets_rects = [] self.targets = [] self.hide_grabber() def do_target_scale(self) -> bool: - """ if any of the elements in the selection allows scaling """ + """ If any of the elements in the selection allows scaling. """ return np.any([target.do_scale for target in self.targets]) def do_change_aspect_ratio(self) -> bool: - """ if any of the element sin the selection wants to perserve its aspect ratio """ + """ If any of the element sin the selection wants to perserve its aspect ratio. """ return np.any([target.fixed_aspect for target in self.targets]) def width(self) -> float: - """ the width of the current selection """ - return (self.p2-self.p1)[0] + """ The width of the current selection. """ + return (self.p2 - self.p1)[0] def height(self) -> float: - """ the height of the current selection """ - return (self.p2-self.p1)[1] + """ The height of the current selection. """ + return (self.p2 - self.p1)[1] def size(self) -> (float, float): - """ the size of the current selection (width and height)""" - return self.p2-self.p1 + """ The size of the current selection (width and height).""" + return self.p2 - self.p1 def get_trans_matrix(self): - """ the transformation matrix for the current displacement and scaling of the selection """ + """ The transformation matrix for the current displacement and scaling of the selection. """ x, y = self.p1 w, h = self.size() return np.array([[w, 0, x], [0, h, y], [0, 0, 1]], dtype=float) def get_inv_trans_matrix(self): - """ the inverse transformation for the current displacement and scaling of the selection """ + """ The inverse transformation for the current displacement and scaling of the selection. """ x, y = self.p1 w, h = self.size() - return np.array([[1./w, 0, -x/w], [0, 1./h, -y/h], [0, 0, 1]], dtype=float) + return np.array([[1. / w, 0, - x / w], [0, 1. / h, -y / h], [0, 0, 1]], dtype=float) def transform(self, pos: Sequence) -> np.ndarray: - """ apply the current transformation to a point """ + """ Apply the current transformation to a point. """ return np.dot(self.get_trans_matrix(), [pos[0], pos[1], 1.0]) def inv_transform(self, pos: Sequence) -> np.ndarray: - """ apply the inverse current transformation to a point """ + """ Apply the inverse current transformation to a point. """ return np.dot(self.get_inv_trans_matrix(), [pos[0], pos[1], 1.0]) def get_pos(self, pos: Sequence) -> np.ndarray: - """ transform a point """ + """ Transform a point. """ return self.transform(pos) def get_save_point(self) -> callable: - """ gather the current positions in a restore point for the undo function """ + """ Gather the current positions in a restore point for the undo function. """ targets = [target.target for target in self.targets] positions = [target.get_positions() for target in self.targets] @@ -465,7 +466,7 @@ def undo(): return undo def start_move(self): - """ start to move a grabber """ + """ Start to move a grabber. """ self.start_p1 = self.p1.copy() self.start_p2 = self.p2.copy() self.hide_grabber() @@ -474,7 +475,7 @@ def start_move(self): self.store_start = self.get_save_point() def end_move(self): - """ a grabber move stopped """ + """ A grabber move stopped. """ self.update_grabber() self.store_end = self.get_save_point() @@ -483,38 +484,38 @@ def end_move(self): self.figure.change_tracker.addEdit([self.store_start, self.store_end, "Move"]) def addOffset(self, pos: Sequence, dir: int, keep_aspect_ratio: bool = True): - """ move the whole selection (e.g. for the use of the arrow keys) """ + """ Move the whole selection (e.g. for the use of the arrow keys). """ pos = list(pos) self.old_inv_transform = self.get_inv_trans_matrix() if (keep_aspect_ratio or self.do_change_aspect_ratio()) and not (dir & DIR_X0 and dir & DIR_X1 and dir & DIR_Y0 and dir & DIR_Y1): if (dir & DIR_X0 and dir & DIR_Y0) or (dir & DIR_X1 and dir & DIR_Y1): - dx = pos[1]*self.width()/self.height() - dy = pos[0]*self.height()/self.width() + dx = pos[1] * self.width() / self.height() + dy = pos[0] * self.height() / self.width() if abs(dx) < abs(dy): pos[0] = dx else: pos[1] = dy elif (dir & DIR_X0 and dir & DIR_Y1) or (dir & DIR_X1 and dir & DIR_Y0): - dx = -pos[1]*self.width()/self.height() - dy = -pos[0]*self.height()/self.width() + dx = -pos[1] * self.width() / self.height() + dy = -pos[0] * self.height() / self.width() if abs(dx) < abs(dy): pos[0] = dx else: pos[1] = dy elif dir & DIR_X0 or dir & DIR_X1: - dy = pos[0]*self.height()/self.width() + dy = pos[0] * self.height() / self.width() if dir & DIR_X0: - self.p1[1] = self.start_p1[1] + dy/2 - self.p2[1] = self.start_p2[1] - dy/2 + self.p1[1] = self.start_p1[1] + dy / 2 + self.p2[1] = self.start_p2[1] - dy / 2 else: self.p1[1] = self.start_p1[1] - dy / 2 self.p2[1] = self.start_p2[1] + dy / 2 elif dir & DIR_Y0 or dir & DIR_Y1: - dx = pos[1]*self.width()/self.height() + dx = pos[1] * self.width() / self.height() if dir & DIR_Y0: - self.p1[0] = self.start_p1[0] + dx/2 - self.p2[0] = self.start_p2[0] - dx/2 + self.p1[0] = self.start_p1[0] + dx / 2 + self.p2[0] = self.start_p2[0] - dx / 2 else: self.p1[0] = self.start_p1[0] - dx / 2 self.p2[0] = self.start_p2[0] + dx / 2 @@ -533,37 +534,37 @@ def addOffset(self, pos: Sequence, dir: int, keep_aspect_ratio: bool = True): self.transform_target(transform, target) self.update_selection_rectangles(True) - #for rect in self.targets_rects: + # for rect in self.targets_rects: # self.transform_target(transform, TargetWrapper(rect)) def move(self, pos: Sequence[float], dir: int, snaps: Sequence[SnapBase], keep_aspect_ratio: bool = False, ignore_snaps: bool = False): - """ called from a grabber to move the selection. """ + """ Called from a grabber to move the selection. """ self.addOffset(pos, dir, keep_aspect_ratio) self.has_moved = True if not ignore_snaps: offx, offy = checkSnaps(snaps) - self.addOffset((pos[0]-offx, pos[1]-offy), dir, keep_aspect_ratio) + self.addOffset((pos[0] - offx, pos[1] - offy), dir, keep_aspect_ratio) offx, offy = checkSnaps(self.snaps) checkSnapsActive(snaps) def apply_transform(self, transform: np.ndarray, point: Sequence[float]): - """ apply the given transformation to a point""" + """ Apply the given transformation to a point.""" point = np.array(point) point = np.hstack((point, np.ones((point.shape[0], 1)))).T return np.dot(transform, point)[:2].T def transform_target(self, transform: np.ndarray, target: TargetWrapper): - """ transform the position of an artist. """ + """ Transform the position of an artist. """ points = target.get_positions() points = self.apply_transform(transform, points) target.set_positions(points) def keyPressEvent(self, event: KeyEvent): - """ when a key is pressed. Arrow keys move the selection, Pageup/down movein z """ - #if not self.selected: + """ When a key is pressed. Arrow keys move the selection, Pageup/down movein z. """ + # if not self.selected: # return # move last axis in z order if event.key == 'pagedown': @@ -607,7 +608,8 @@ def keyPressEvent(self, event: KeyEvent): class DragManager: - """ a class to manage the selection and the moving of artists in a figure """ + """ A class to manage the selection and the moving of artists in a figure. """ + selected_element = None grab_element = None @@ -648,13 +650,13 @@ def __init__(self, figure: Figure, no_save): self.figure.change_tracker = self.change_tracker def activate(self): - """ activate the interaction callbacks from the figure """ + """ Activate the interaction callbacks from the figure. """ self.c3 = self.figure.canvas.mpl_connect('button_release_event', self.button_release_event0) self.c2 = self.figure.canvas.mpl_connect('button_press_event', self.button_press_event0) self.c4 = self.figure.canvas.mpl_connect('key_press_event', self.key_press_event) def deactivate(self): - """ deactivate the interaction callbacks from the figure """ + """ Deactivate the interaction callbacks from the figure. """ self.figure.canvas.mpl_disconnect(self.c3) self.figure.canvas.mpl_disconnect(self.c2) self.figure.canvas.mpl_disconnect(self.c4) @@ -665,13 +667,13 @@ def deactivate(self): self.figure.canvas.draw() def make_dragable(self, target: Artist): - """ make an artist draggable """ + """ Make an artist draggable. """ target.set_picker(True) if isinstance(target, Text): target.set_bbox({"facecolor": 'none', "edgecolor": 'none'}) def get_picked_element(self, event: MouseEvent, element: Artist = None, picked_element: Artist = None, last_selected: Artist = None): - """ get the picked element that an event refers to. + """ Get the picked element that an event refers to. To implement selection of elements at the back with multiple clicks. """ # start with the figure @@ -681,7 +683,7 @@ def get_picked_element(self, event: MouseEvent, element: Artist = None, picked_e # iterate over all children for child in sorted(element.get_children(), key=lambda x: x.get_zorder()): # check if the element is contained in the event and has an active dragger - #if child.contains(event)[0] and ((getattr(child, "_draggable", None) and getattr(child, "_draggable", + # if child.contains(event)[0] and ((getattr(child, "_draggable", None) and getattr(child, "_draggable", # None).connected) or isinstance(child, GrabberGeneric) or isinstance(child, GrabbableRectangleSelection)): if child.get_visible() and child.contains(event)[0] and (child.pickable() or isinstance(child, GrabberGeneric)) and not (child.get_label() is not None and child.get_label().startswith("_")): # if the element is the last selected, finish the search @@ -697,7 +699,7 @@ def get_picked_element(self, event: MouseEvent, element: Artist = None, picked_e return picked_element, finished def button_release_event0(self, event: MouseEvent): - """ when the mouse button is released """ + """ When the mouse button is released. """ # release the grabber if self.grab_element: self.grab_element.button_release_event(event) @@ -707,7 +709,7 @@ def button_release_event0(self, event: MouseEvent): self.selection.button_release_event(event) def button_press_event0(self, event: MouseEvent): - """ when the mouse button is pressed """ + """ When the mouse button is pressed. """ if event.button == 1: last = self.selection.targets[-1] if len(self.selection.targets) else None contained = np.any([t.target.contains(event)[0] for t in self.selection.targets]) @@ -731,7 +733,7 @@ def button_press_event0(self, event: MouseEvent): self.selection.button_press_event(event) def select_element(self, element: Artist, event: MouseEvent = None): - """ select an artist in a figure """ + """ Select an artist in a figure. """ # do nothing if it is already selected if element == self.selected_element: return @@ -744,14 +746,14 @@ def select_element(self, element: Artist, event: MouseEvent = None): self.selected_element = element def on_deselect(self, event: MouseEvent): - """ deselect currently selected artists""" + """ Deselect currently selected artists.""" modifier = "shift" in event.key.split("+") if event is not None and event.key is not None else False # only if the modifier key is not used if not modifier: self.selection.clear_targets() def on_select(self, element: Artist, event: MouseEvent): - """ when an artist is selected """ + """ When an artist is selected. """ if element is not None: self.selection.add_target(element) @@ -772,7 +774,7 @@ def redo(self): self.figure.canvas.draw() def key_press_event(self, event: KeyEvent): - """ when a key is pressed """ + """ When a key is pressed. """ # space: print code to restore current configuration if event.key == 'ctrl+s': self.figure.change_tracker.save() @@ -788,7 +790,8 @@ def key_press_event(self, event: KeyEvent): class GrabberGeneric(GrabFunctions): - """ a generic grabber object to move a selection """ + """ A generic grabber object to move a selection. """ + _no_save = True def __init__(self, parent: GrabbableRectangleSelection, x: float, y: float, dir: int): @@ -811,11 +814,12 @@ def updatePos(self): self.set_xy(self.parent.get_pos(self.pos)) def applyOffset(self, pos: (float, float), event: MouseEvent): - self.set_xy((self.ox+pos[0], self.oy+pos[1])) + self.set_xy((self.ox + pos[0], self.oy + pos[1])) class GrabberGenericRound(GrabberGeneric): - """ a rectangle with a round appearance """ + """ A rectangle with a round appearance. """ + d = 10 shape = "round" @@ -834,35 +838,36 @@ def __init__(self, parent: GrabbableRectangleSelection, x: float, y: float, dir: def set_xy(self, xy: (float, float)): self.xy = xy - self.ellipse.setRect(xy[0]-5, xy[1]-5, 10, 10) + self.ellipse.setRect(xy[0] - 5, xy[1] - 5, 10, 10) class GrabberGenericRectangle(GrabberGeneric): - """ a rectangle with a square appearance """ + """ A rectangle with a square appearance. """ + d = 10 shape = "rect" def __init__(self, parent: GrabbableRectangleSelection, x: float, y: float, dir: int, scene): # somehow the original "self" rectangle does not show up in the current matplotlib version, therefore this doubling - #self.rect = Rectangle((0, 0), self.d, self.d, figure=parent.figure, edgecolor="k", facecolor="r", zorder=1000, label="grabber") - #self.rect._no_save = True - #parent.figure.patches.append(self.rect) + # self.rect = Rectangle((0, 0), self.d, self.d, figure=parent.figure, edgecolor="k", facecolor="r", zorder=1000, label="grabber") + # self.rect._no_save = True + # parent.figure.patches.append(self.rect) - #Rectangle.__init__(self, (0, 0), self.d, self.d, picker=True, figure=parent.figure, edgecolor="k", facecolor="r", zorder=1000, label="grabber") + # Rectangle.__init__(self, (0, 0), self.d, self.d, picker=True, figure=parent.figure, edgecolor="k", facecolor="r", zorder=1000, label="grabber") - #self.figure.patches.append(self) + # self.figure.patches.append(self) pen3 = QtGui.QPen(QtGui.QColor("black"), 2) brush1 = QtGui.QBrush(QtGui.QColor("red")) - self.ellipse = MyRect(x-5, y-5, 10, 10, scene) + self.ellipse = MyRect(x - 5, y - 5, 10, 10, scene) self.ellipse.view = scene.view self.ellipse.grabber = self self.ellipse.setPen(pen3) self.ellipse.setBrush(brush1) self.xy = (x, y) - #self.updatePos() + # self.updatePos() GrabberGeneric.__init__(self, parent, x, y, dir) @@ -879,6 +884,7 @@ def set_xy(self, xy: (float, float)): Rectangle.set_xy(self, (xy[0] - self.d / 2, xy[1] - self.d / 2)) self.rect.set_xy((xy[0] - self.d / 2, xy[1] - self.d / 2)) + class MyItem: w = 10 @@ -896,14 +902,15 @@ def mouseReleaseEvent(self, e): p = e.scenePos() self.grabber.button_release_event(MyEvent(p.x(), self.view.h - p.y())) + class MyRect(MyItem, QtWidgets.QGraphicsRectItem): pass + class MyEllipse(MyItem, QtWidgets.QGraphicsEllipseItem): pass - class MyEvent: def __init__(self, x, y): self.x = x diff --git a/pylustrator/exception_swallower.py b/pylustrator/exception_swallower.py index 2862e59..b14110f 100644 --- a/pylustrator/exception_swallower.py +++ b/pylustrator/exception_swallower.py @@ -25,7 +25,8 @@ class Dummy: - """ a dummy object that provides dummy attributes, dummy items and dummy returns """ + """ A dummy object that provides dummy attributes, dummy items and dummy returns. """ + def __getattr__(self, item): return Dummy() @@ -37,7 +38,8 @@ def __getitem__(self, item): class SaveList(list): - """ a list that returns dummy objects when an invalid item is requested """ + """ A list that returns dummy objects when an invalid item is requested. """ + def __init__(self, target): list.__init__(self, target) @@ -49,7 +51,8 @@ def __getitem__(self, item): class SaveDict(dict): - """ a dictionary that returns dummy objects when an invalid item is requested """ + """ A dictionary that returns dummy objects when an invalid item is requested. """ + def __init__(self, target): dict.__init__(self, target) @@ -61,7 +64,8 @@ def __getitem__(self, item): class SaveTuple(tuple): - """ a tuple that returns dummy objects when an invalid item is requested """ + """ A tuple that returns dummy objects when an invalid item is requested. """ + def __init__(self, target): tuple.__init__(self, target) @@ -73,7 +77,8 @@ def __getitem__(self, item): class SaveListDescriptor: - """ a descriptor that wraps the target value with a SaveList, SaveDict or SaveTuple """ + """ A descriptor that wraps the target value with a SaveList, SaveDict or SaveTuple. """ + def __init__(self, variable_name): self.variable_name = variable_name @@ -84,11 +89,11 @@ def __set__(self, instance, value): value = SaveDict(value) if isinstance(value, tuple): value = SaveTuple(value) - setattr(instance, "_pylustrator_"+self.variable_name, value) + setattr(instance, "_pylustrator_" + self.variable_name, value) def __get__(self, instance, owner): try: - return getattr(instance, "_pylustrator_"+self.variable_name) + return getattr(instance, "_pylustrator_" + self.variable_name) except AttributeError: if self.variable_name in instance.__dict__: return instance.__dict__[self.variable_name] @@ -97,21 +102,21 @@ def __get__(self, instance, owner): def get_axes(self): - """ a function that returns the axes of a figure as a SaveList """ + """ A function that returns the axes of a figure as a SaveList. """ return SaveList(self._axstack.as_list()) def return_save_list(func): - """ a decorator to wrap the output of a function as a SaveList """ + """ A decorator to wrap the output of a function as a SaveList. """ def wrap(*args, **kwargs): return SaveList(func(*args, **kwargs)) return wrap def swallow_get_exceptions(): - """ replace lists with lists that return dummy objects when items are not present. - this is to ensure that the pylustrator generated code does not fail, even if the user removes some elements - from the figure. + """ Replace lists with lists that return dummy objects when items are not present. + this is to ensure that the pylustrator generated code does not fail, even if the user removes some elements + from the figure. """ Figure._get_axes = get_axes Figure.axes = property(fget=get_axes) @@ -122,6 +127,7 @@ def swallow_get_exceptions(): Axis.get_minor_ticks = return_save_list(Axis.get_minor_ticks) Axis.get_major_ticks = return_save_list(Axis.get_major_ticks) l = _AxesBase.get_legend + def get_legend(*args, **kwargs): leg = l(*args, **kwargs) if leg is None: diff --git a/pylustrator/helper_functions.py b/pylustrator/helper_functions.py index 3d94cc3..f971123 100644 --- a/pylustrator/helper_functions.py +++ b/pylustrator/helper_functions.py @@ -43,9 +43,7 @@ def fig_text(x: float, y: float, text: str, unit: str = "cm", *args, **kwargs): - """ - add a text to the figure positioned in cm - """ + """ Add a text to the figure positioned in cm. """ fig = plt.gcf() if unit == "cm": x = x / 2.54 / fig.get_size_inches()[0] @@ -58,9 +56,7 @@ def fig_text(x: float, y: float, text: str, unit: str = "cm", *args, **kwargs): def add_axes(dim: Sequence, unit: str = "cm", *args, **kwargs): - """ - add an axes with dimensions specified in cm - """ + """ Add an axes with dimensions specified in cm. """ fig = plt.gcf() x, y, w, h = dim if unit == "cm": @@ -76,15 +72,16 @@ def add_axes(dim: Sequence, unit: str = "cm", *args, **kwargs): def add_image(filename: str): - """ add an image to the current axes """ + """ Add an image to the current axes. """ plt.imshow(plt.imread(filename)) plt.xticks([]) plt.yticks([]) def changeFigureSize(w: float, h: float, cut_from_top: bool = False, cut_from_left: bool = False, fig: Figure = None): - """ change the figure size to the given dimensions. Optionally define if to remove or add space at the top or bottom - and left or right. + """ Change the figure size to the given dimensions. + Optionally define if to remove or add space at the top or bottom + and left or right. """ if fig is None: fig = plt.gcf() @@ -108,7 +105,7 @@ def changeFigureSize(w: float, h: float, cut_from_top: bool = False, cut_from_le x0, y0 = text.get_position() if cut_from_top: if cut_from_left: - text.set_position([1 - (1- x0) * fx, y0 * fy]) + text.set_position([1 - (1 - x0) * fx, y0 * fy]) else: text.set_position([x0 * fx, y0 * fy]) else: @@ -120,7 +117,7 @@ def changeFigureSize(w: float, h: float, cut_from_top: bool = False, cut_from_le def removeContentFromFigure(fig: Figure): - """ remove axes and text from a figure """ + """ Remove axes and text from a figure. """ axes = [] for ax in fig._axstack.as_list(): axes.append(ax) @@ -131,7 +128,7 @@ def removeContentFromFigure(fig: Figure): def addContentToFigure(fig: Figure, axes: Sequence): - """ add axes and texts to a figure """ + """ Add axes and texts to a figure. """ index = len(fig._axstack.as_list()) for ax in axes: if isinstance(ax, Axes): @@ -162,7 +159,7 @@ def get_unique_label(fig1, label_base): def imShowFullFigure(im: np.ndarray, filename: str, fig1: Figure, dpi: int, label: str): - """ create a new axes and display an image in this axes """ + """ Create a new axes and display an image in this axes. """ from matplotlib import rcParams if dpi is None: dpi = rcParams['figure.dpi'] @@ -176,9 +173,8 @@ def imShowFullFigure(im: np.ndarray, filename: str, fig1: Figure, dpi: int, labe class changeFolder: - """ - An environment that changes the working directory - """ + """ An environment that changes the working directory. """ + def __init__(self, directory): self.directory = directory @@ -192,9 +188,9 @@ def __exit__(self, type, value, traceback): def loadFigureFromFile(filename: str, figure: Figure = None, offset: list = None, dpi: int = None, cache: bool = False, label: str = ""): - """ - Add contents to the current figure from the file defined by filename. It can be either a python script defining - a figure, an image (filename or directly the numpy array), or an svg file. + """ Add contents to the current figure from the file defined by filename. + It can be either a python script defining + a figure, an image (filename or directly the numpy array), or an svg file. See also :ref:`composing`. @@ -231,9 +227,8 @@ def loadFigureFromFile(filename: str, figure: Figure = None, offset: list = None figure = plt.gcf() class noShow: - """ - An environment that prevents the script from calling the plt.show function - """ + """ An environment that prevents the script from calling the plt.show function. """ + def __enter__(self): # store the show function self.show = plt.show @@ -253,14 +248,14 @@ def __exit__(self, type, value, traceback): pylustrator.start = self.dragger class noNewFigures: - """ - An environment that prevents the script from creating new figures in the figure manager - """ + """An environment that prevents the script from creating new figures in the figure manager.""" + def __enter__(self): fig = plt.gcf() self.fig = plt.figure figsize = rcParams['figure.figsize'] fig.set_size_inches(figsize[0], figsize[1]) + def figure(num=None, figsize=None, *args, **kwargs): fig = plt.gcf() if figsize is not None: @@ -366,12 +361,12 @@ def convertFromPyplot(old, new): str(new) # important! (for some reason I don't know) for ax in old.axes: - #old.delaxes(ax) + # old.delaxes(ax) ax.remove() ax.figure = new new.axes.append(ax) new.add_axes(ax) - #new._axstack.add(new._make_key(ax), ax) + # new._axstack.add(new._make_key(ax), ax) new.bbox._parents.update(old.bbox._parents) new.dpi_scale_trans._parents.update(old.dpi_scale_trans._parents) replace_all_refs(old.bbox, new.bbox) @@ -381,7 +376,7 @@ def convertFromPyplot(old, new): def mark_inset(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequence[int]] = 1, loc2: Union[int, Sequence[int]] = 2, **kwargs): - """ like the mark_inset function from matplotlib, but loc can also be a tuple """ + """ Like the mark_inset function from matplotlib, but loc can also be a tuple. """ from mpl_toolkits.axes_grid1.inset_locator import (BboxConnector, BboxPatch, TransformedBbox) @@ -412,7 +407,7 @@ def mark_inset(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequence[in def draw_from_point_to_bbox(parent_axes: Axes, insert_axes: Axes, point: Sequence, loc=1, **kwargs): - """ add a box connector from a point to an axes """ + """ Add a box connector from a point to an axes. """ from mpl_toolkits.axes_grid1.inset_locator import (Bbox, BboxConnector, TransformedBbox) rect = TransformedBbox(Bbox([point, point]), parent_axes.transData) @@ -424,7 +419,7 @@ def draw_from_point_to_bbox(parent_axes: Axes, insert_axes: Axes, point: Sequenc def draw_from_point_to_point(parent_axes: Axes, insert_axes: Axes, point1: Sequence, point2: Sequence, **kwargs): - """ add a box connector from a point in on axes to a point in another axes """ + """ Add a box connector from a point in on axes to a point in another axes. """ from mpl_toolkits.axes_grid1.inset_locator import (Bbox, BboxConnector, TransformedBbox) rect = TransformedBbox(Bbox([point1, point1]), parent_axes.transData) @@ -438,7 +433,7 @@ def draw_from_point_to_point(parent_axes: Axes, insert_axes: Axes, point1: Seque def mark_inset_pos(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequence[int]], loc2: Union[int, Sequence[int]], point: Sequence, **kwargs): - """ add a box connector where the second axis is shrinked to a point """ + """ Add a box connector where the second axis is shrinked to a point. """ kwargs["lw"] = 0.8 ax_new = plt.axes(inset_axes.get_position()) ax_new.set_xlim(point[0], point[0]) @@ -449,8 +444,8 @@ def mark_inset_pos(parent_axes: Axes, inset_axes: Axes, loc1: Union[int, Sequenc ax_new.set_zorder(inset_axes.get_zorder() - 1) -def VoronoiPlot(points: Sequence, values: Sequence, vmin: float = None, vmax:float = None, cmap=None): - """ plot the voronoi regions of the poins with the given colormap """ +def VoronoiPlot(points: Sequence, values: Sequence, vmin: float = None, vmax: float = None, cmap=None): + """ Plot the voronoi regions of the poins with the given colormap. """ from matplotlib import cm from matplotlib.collections import PatchCollection from matplotlib.patches import Polygon @@ -496,12 +491,12 @@ def VoronoiPlot(points: Sequence, values: Sequence, vmin: float = None, vmax:flo def selectRectangle(axes: Axes = None): - """ add a rectangle selector to the given axes """ + """ Add a rectangle selector to the given axes. """ if axes is None: axes = plt.gca() def onselect(eclick, erelease): - 'eclick and erelease are matplotlib events at press and release' + """ Eclick and erelease are matplotlib events at press and release. """ print(f' startposition : ({eclick.xdata:f}, {eclick.ydata:f})') print(f' endposition : ({erelease.xdata:f}, {erelease.ydata:f})') print(' used button : ', eclick.button) @@ -512,7 +507,7 @@ def onselect(eclick, erelease): def despine(ax: Axes = None, complete: bool = False): - """ despine the given axes """ + """ Despine the given axes. """ if not ax: ax = plt.gca() ax.spines['right'].set_visible(False) @@ -528,10 +523,11 @@ def despine(ax: Axes = None, complete: bool = False): ax.xaxis.set_ticks_position('bottom') - letter_index = 0 + + def add_letter(ax: Axes = None, offset: float = 0, offset2: float = 0, letter: str = None): - """ add a letter indicating which subplot it is to the given figure """ + """ Add a letter indicating which subplot it is to the given figure. """ global letter_index from matplotlib.transforms import Affine2D, ScaledTranslation @@ -559,11 +555,11 @@ def add_letter(ax: Axes = None, offset: float = 0, offset2: float = 0, letter: s transform = Affine2D().scale(1 / 2.54, 1 / 2.54) + fig.dpi_scale_trans + ScaledTranslation(0, 1, ax.transAxes) # add a text a the given position - ax.text(-0.5+offset, offset2, letter, fontproperties=font, transform=transform, ha="center", va="bottom", picker=True) + ax.text(-0.5 + offset, offset2, letter, fontproperties=font, transform=transform, ha="center", va="bottom", picker=True) def get_letter_font_prop(): - """ get the properties of the subplot letters to add """ + """ Get the properties of the subplot letters to add. """ from matplotlib.font_manager import FontProperties font = FontProperties() font.set_family("C:\\WINDOWS\\Fonts\\HelveticaNeue-CondensedBold.ttf") @@ -574,7 +570,7 @@ def get_letter_font_prop(): def add_letters(*args, **kwargs): - """ add a letter indicating which subplot it is to all of the axes of the given figure """ + """ Add a letter indicating which subplot it is to all of the axes of the given figure. """ for ax in plt.gcf().axes: add_letter(ax, *args, **kwargs) @@ -600,9 +596,9 @@ def axes_to_grid(axes=None, track_changes=False): new_indices = [0, 0] for i in [0, 1]: d = np.abs(pos[i] - center[i]) - if len(d) == 0 or np.min(d) > dims[i]/2: + if len(d) == 0 or np.min(d) > dims[i] / 2: pos[i].append(center[i]) - new_indices[i] = len(pos[i])-1 + new_indices[i] = len(pos[i]) - 1 else: new_indices[i] = np.argmin(d) axes_indices.append(new_indices) @@ -626,19 +622,19 @@ def axes_to_grid(axes=None, track_changes=False): if x_count == 0: x_gap = 0 else: - x_gap = ((x_max-x_min)-(x_count+1)*width)/x_count + x_gap = ((x_max - x_min) - (x_count + 1) * width) / x_count if y_count == 0: y_gap = 0 else: - y_gap = ((y_max-y_min)-(y_count+1)*height)/y_count + y_gap = ((y_max - y_min) - (y_count + 1) * height) / y_count # make all the plots the same size and align them on the grid for i, ax in enumerate(axes): - ax.set_position([x_min+axes_indices[i][0] * (width+x_gap), - y_min+axes_indices[i][1] * (height + y_gap), + ax.set_position([x_min + axes_indices[i][0] * (width + x_gap), + y_min + axes_indices[i][1] * (height + y_gap), width, height, - ]) + ]) if track_changes is True: ax.figure.change_tracker.addChange(ax, f".set_position([{x_min + axes_indices[i][0] * (width + x_gap):f}, {y_min + axes_indices[i][1] * (height + y_gap):f}, {width:f}, {height:f}])") diff --git a/pylustrator/jupyter_cells.py b/pylustrator/jupyter_cells.py index ec9e52c..9be8a89 100644 --- a/pylustrator/jupyter_cells.py +++ b/pylustrator/jupyter_cells.py @@ -24,8 +24,9 @@ the file is instead of a normal file a jupyter notebook and redirects writes accordingly. """ + def setJupyterCellText(text: str): - """ the function replaces the text in the current jupyter cell with the given text """ + """ The function replaces the text in the current jupyter cell with the given text. """ from IPython.display import Javascript, display text = text.replace("\n", "\\n").replace("'", "\\'") js = """ @@ -37,14 +38,14 @@ def setJupyterCellText(text: str): // get the cell object var cell = Jupyter.notebook.get_cell(cell_idx); cell.get_text(); - cell.set_text('"""+text+"""'); - console.log('"""+text+"""'); + cell.set_text('""" + text + """'); + console.log('""" + text + """'); """ display(Javascript(js)) def getIpythonCurrentCell() -> str: - """ this function returns the text of the current jupyter cell """ + """ This function returns the text of the current jupyter cell. """ import inspect # get the first stack which has a filename starting with " str: global_files = {} build_in_open = open + + def open(filename: str, *args, **kwargs): - """ open a file and if its a jupyter cell then mock a filepointer to that cell """ + """ Open a file and if its a jupyter cell then mock a filepointer to that cell. """ if filename.startswith(" -""" Colormap """ +""" Colormap. """ from typing import Sequence, Union import numpy as np @@ -27,30 +27,32 @@ class CmapColor(list): - """ a class that appends metadate to a color """ + """ A class that appends metadate to a color. """ + def setMeta(self, value, cmap): self.value = value self.cmap = cmap def convert_rgb2lab(colors: Sequence): - """ convert colors from rgb to lab color space """ + """ Convert colors from rgb to lab color space. """ from skimage.color import rgb2lab return [rgb2lab(np.array(c)[None, None, :3]) for c in colors] def convert_lab2rgb(colors): - """ convert colors from lab to rgb color space """ + """ Convert colors from lab to rgb color space. """ from skimage.color import lab2rgb return [lab2rgb(np.array(c))[0, 0, :3] for c in colors] class LabColormap(ListedColormap): - """ a custom colormap that blends between N custom colors """ + """ A custom colormap that blends between N custom colors. """ + init_colors = None def __init__(self, colors: Sequence, N: int, stops=None): - """ initialize with the given colors and stops """ + """ Initialize with the given colors and stops. """ # store stops self.stops = stops # set colors @@ -59,7 +61,7 @@ def __init__(self, colors: Sequence, N: int, stops=None): Colormap.__init__(self, "test", N) def _init(self): - """ generate the colormap from the given colors (used by ListedColormap) """ + """ Generate the colormap from the given colors (used by ListedColormap). """ # convert to lab lab_colors = convert_rgb2lab(self.init_colors) # initialize new list @@ -76,7 +78,7 @@ def _init(self): ListedColormap._init(self) def __call__(self, value: float, *args, **kwargs): - """ get the color associated with the given value from the colormap """ + """ Get the color associated with the given value from the colormap. """ # get the color result = Colormap.__call__(self, value, *args, **kwargs) # add meta values to it @@ -86,12 +88,12 @@ def __call__(self, value: float, *args, **kwargs): return result def get_color(self) -> Sequence: - """ return all the colors """ + """ Return all the colors. """ # return the colors return self.init_colors def set_color(self, color: Union[str, Sequence], index: int = None): - """ set a color to the given index """ + """ Set a color to the given index. """ # update the color according to the index if index is not None: self.init_colors[index] = to_rgb(color) @@ -105,7 +107,7 @@ def set_color(self, color: Union[str, Sequence], index: int = None): self._isinit = False def get_stops(self) -> Sequence: - """ return the stops """ + """ Return the stops. """ # get the stops stops = self.stops # if they are not defined, interpolate from 0 to 1 @@ -115,7 +117,7 @@ def get_stops(self) -> Sequence: return stops def linearize_lightness(self): - """ linearize the lightness of the colors in the colormap """ + """ Linearize the lightness of the colors in the colormap. """ # convert to lab lab_colors = convert_rgb2lab(self.init_colors) # define start and end lightness diff --git a/pylustrator/parse_svg.py b/pylustrator/parse_svg.py index 52df443..45c36d4 100644 --- a/pylustrator/parse_svg.py +++ b/pylustrator/parse_svg.py @@ -39,12 +39,12 @@ def deform(base_trans: mtransforms.Transform, x: float, y: float, sx: float = 0, sy: float = 0): - """ apply an affine transformation to the given transformation """ + """ Apply an affine transformation to the given transformation. """ return mtransforms.Affine2D([[x, sx, 0], [sy, y, 0], [0, 0, 1]]) + base_trans def parseTransformation(transform_text: str) -> mtransforms.Transform: - """ convert a transform string in the svg file to a matplotlib transformation """ + """ Convert a transform string in the svg file to a matplotlib transformation. """ base_trans = mtransforms.IdentityTransform() if transform_text is None or transform_text == "": return base_trans @@ -70,11 +70,11 @@ def parseTransformation(transform_text: str) -> mtransforms.Transform: base_trans = mtransforms.Affine2D([[x, 0, 0], [0, y, 0], [0, 0, 1]]) + base_trans elif command == "skewX": x, = data - x = np.tan(x*np.pi/180) + x = np.tan(x * np.pi / 180) base_trans = mtransforms.Affine2D([[1, x, 0], [0, 1, 0], [0, 0, 1]]) + base_trans elif command == "skewY": y, = data - y = np.tan(y*np.pi/180) + y = np.tan(y * np.pi / 180) base_trans = mtransforms.Affine2D([[1, 0, 0], [y, 1, 0], [0, 0, 1]]) + base_trans elif command == "matrix": x, sy, sx, y, ox, oy = data @@ -85,7 +85,7 @@ def parseTransformation(transform_text: str) -> mtransforms.Transform: def get_inline_style(node: minidom.Element, base_style: dict = None) -> dict: - """ update the basestyle with the style defined by the style property of the node """ + """ Update the basestyle with the style defined by the style property of the node. """ style = {} if base_style is not None: style.update(base_style) @@ -104,8 +104,8 @@ def get_inline_style(node: minidom.Element, base_style: dict = None) -> dict: def get_css_style(node: minidom.Element, css_list: list, base_style: dict) -> dict: - """ update the base_style with the style definitions from the stylesheet that are applicable to the node - defined by the classes or id of the node + """ Update the base_style with the style definitions from the stylesheet that are applicable to the node + defined by the classes or id of the node. """ style = {} if base_style is not None: @@ -123,7 +123,7 @@ def get_css_style(node: minidom.Element, css_list: list, base_style: dict) -> di def apply_style(style: dict, patch: mpatches.Patch) -> dict: - """ apply the properties defined in style to the given patch """ + """ Apply the properties defined in style to the given patch. """ fill_opacity = float(style.get("opacity", 1)) * float(style.get("fill-opacity", 1)) stroke_opacity = float(style.get("opacity", 1)) * float(style.get("stroke-opacity", 1)) @@ -133,7 +133,7 @@ def readColor(value): except Exception: # matplotlib cannot handle html colors in the form #000 if len(value) == 4 and value[0] == "#": - return readColor("#"+value[1]*2+value[2]*2+value[3]*2) + return readColor("#" + value[1] * 2 + value[2] * 2 + value[3] * 2) raise # matplotlib defaults differ @@ -146,7 +146,7 @@ def readColor(value): try: if key == "opacity": pass - #patch.set_alpha(float(value)) + # patch.set_alpha(float(value)) elif key == "fill": if value == "none" or value == "transparent": patch.set_facecolor("none") @@ -176,12 +176,12 @@ def readColor(value): offset = 0 if isinstance(patch.get_linestyle(), tuple): offset, dashes = patch.get_linestyle() - patch.set_linestyle((offset, [float(s)*4 for s in value.split(",")])) + patch.set_linestyle((offset, [float(s) * 4 for s in value.split(",")])) elif key == "stroke-dashoffset": dashes = [1, 0] if isinstance(patch.get_linestyle(), tuple): offset, dashes = patch.get_linestyle() - patch.set_linestyle((float(value)*4, dashes)) + patch.set_linestyle((float(value) * 4, dashes)) elif key == "stroke-linecap": if value == "square": value = "projecting" @@ -225,7 +225,7 @@ def readColor(value): def font_properties_from_style(style: dict) -> FontProperties: - """ convert a style to a FontProperties object """ + """ Convert a style to a FontProperties object. """ fp = FontProperties() for key, value in style.items(): if key == "font-family": @@ -244,14 +244,14 @@ def font_properties_from_style(style: dict) -> FontProperties: def styleNoDisplay(style: dict) -> bool: - """ check whether the style defines not to display the element """ + """ Check whether the style defines not to display the element. """ return style.get("display", "inline") == "none" or \ - style.get("visibility", "visible") == "hidden" or \ - style.get("visibility", "visible") == "collapse" + style.get("visibility", "visible") == "hidden" or \ + style.get("visibility", "visible") == "collapse" def plt_patch(node: minidom.Element, trans_parent_trans: mtransforms.Transform, style: dict, constructor: callable, ids: dict, no_draw: bool = False) -> mpatches.Patch: - """ add a node to the figure by calling the provided constructor """ + """ Add a node to the figure by calling the provided constructor. """ trans_node = parseTransformation(node.getAttribute("transform")) style = get_inline_style(node, get_css_style(node, ids["css"], style)) @@ -263,7 +263,7 @@ def plt_patch(node: minidom.Element, trans_parent_trans: mtransforms.Transform, if not getattr(p, "is_marker", False): style = apply_style(style, p) p.style = style - #p.set_transform(p.get_transform() + plt.gca().transData) + # p.set_transform(p.get_transform() + plt.gca().transData) p.trans_parent = trans_parent_trans p.trans_node = parseTransformation(node.getAttribute("transform")) @@ -275,7 +275,7 @@ def plt_patch(node: minidom.Element, trans_parent_trans: mtransforms.Transform, def clone_patch(patch: mpatches.Patch) -> mpatches.Patch: - """ clone a patch element with the same properties as the given patch """ + """ Clone a patch element with the same properties as the given patch. """ if isinstance(patch, mpatches.Rectangle): return mpatches.Rectangle(xy=patch.get_xy(), width=patch.get_width(), @@ -292,7 +292,7 @@ def clone_patch(patch: mpatches.Patch) -> mpatches.Patch: def patch_rect(node: minidom.Element, trans: mtransforms.Transform, style: dict, ids: dict) -> mpatches.Rectangle: - """ draw a svg rectangle node as a rectangle patch element into the figure (with the given transformation and style) """ + """ Draw a svg rectangle node as a rectangle patch element into the figure (with the given transformation and style). """ if node.getAttribute("d") != "": return patch_path(node, trans, style, ids) if node.getAttribute("ry") != "" and node.getAttribute("ry") != 0: @@ -308,17 +308,17 @@ def patch_rect(node: minidom.Element, trans: mtransforms.Transform, style: dict, def patch_ellipse(node: minidom.Element, trans: mtransforms.Transform, style: dict, ids: dict) -> mpatches.Ellipse: - """ draw a svg ellipse node as a ellipse patch element into the figure (with the given transformation and style) """ + """ Draw a svg ellipse node as a ellipse patch element into the figure (with the given transformation and style). """ if node.getAttribute("d") != "": return patch_path(node, trans, style, ids) return mpatches.Ellipse(xy=(float(node.getAttribute("cx")), float(node.getAttribute("cy"))), - width=float(node.getAttribute("rx"))*2, - height=float(node.getAttribute("ry"))*2, + width=float(node.getAttribute("rx")) * 2, + height=float(node.getAttribute("ry")) * 2, transform=trans) def patch_circle(node: minidom.Element, trans: mtransforms.Transform, style: dict, ids: dict) -> mpatches.Circle: - """ draw a svg circle node as a circle patch element into the figure (with the given transformation and style) """ + """ Draw a svg circle node as a circle patch element into the figure (with the given transformation and style). """ if node.getAttribute("d") != "": return patch_path(node, trans, style, ids) return mpatches.Circle(xy=(float(node.getAttribute("cx")), float(node.getAttribute("cy"))), @@ -327,7 +327,7 @@ def patch_circle(node: minidom.Element, trans: mtransforms.Transform, style: dic def plt_draw_text(node: minidom.Element, trans: mtransforms.Transform, style: dict, ids: dict, no_draw: bool = False): - """ draw a svg text node as a text patch element into the figure (with the given transformation and style) """ + """ Draw a svg text node as a text patch element into the figure (with the given transformation and style). """ trans = parseTransformation(node.getAttribute("transform")) + trans + plt.gca().transData trans = mtransforms.Affine2D([[1, 0, 0], [0, -1, 0], [0, 0, 1]]) + trans if node.getAttribute("x") != "": @@ -383,9 +383,7 @@ def plt_draw_text(node: minidom.Element, trans: mtransforms.Transform, style: di def patch_path(node: minidom.Element, trans: mtransforms.Transform, style: dict, ids: dict) -> list: - """ draw a path svg node by using a matplotlib path patch (with the given transform and style) """ - - + """ Draw a path svg node by using a matplotlib path patch (with the given transform and style). """ start_pos = None command = None verts = [] @@ -419,8 +417,8 @@ def vec2angle(vec): if not no_angle: n = len(positions) - angles[-1].append(vec2angle(verts[-n] - verts[-n-1])) - for i in range(n-1): + angles[-1].append(vec2angle(verts[-n] - verts[-n - 1])) + for i in range(n - 1): angles.append([]) angles.append([vec2angle(verts[-1] - verts[-2])]) else: @@ -459,7 +457,7 @@ def vec2angle(vec): # horizontal lineto elif command == 'h': current_pos = addPathElement(mpath.Path.LINETO, - np.array([popValue()+current_pos[0]*(1-absolute), current_pos[1]])) + np.array([popValue() + current_pos[0] * (1 - absolute), current_pos[1]])) # vertical lineto elif command == 'v': current_pos = addPathElement(mpath.Path.LINETO, @@ -523,6 +521,7 @@ def vec2angle(vec): def addMarker(i, name): marker_style, patches = ids[name] + def add_list_elements(element): if isinstance(element, list): for e in element: @@ -535,7 +534,7 @@ def add_list_elements(element): a = angles[i] ca, sa = np.cos(a), np.sin(a) ox, oy = verts[i] - trans2 = parent_patch.trans_node + mtransforms.Affine2D([[ca, -sa, ox], [sa, ca, oy], [0, 0, 1]]) + parent_patch.trans_parent + trans#+ plt.gca().transAxes + trans2 = parent_patch.trans_node + mtransforms.Affine2D([[ca, -sa, ox], [sa, ca, oy], [0, 0, 1]]) + parent_patch.trans_parent + trans # + plt.gca().transAxes if marker_style.get("markerUnits", "strokeWidth") == "strokeWidth": s = svgUnitToMpl(style["stroke-width"]) trans2 = mtransforms.Affine2D([[s, 0, 0], [0, s, 0], [0, 0, 1]]) + trans2 @@ -560,19 +559,19 @@ def add_list_elements(element): if style.get("marker-mid").startswith("url(#"): name = style.get("marker-mid")[len("url(#"):-1] if name in ids: - for i in range(1, len(angles)-1): + for i in range(1, len(angles) - 1): addMarker(i, name) if style.get("marker-end"): if style.get("marker-end").startswith("url(#"): name = style.get("marker-end")[len("url(#"):-1] if name in ids: - addMarker(len(angles)-1, name) + addMarker(len(angles) - 1, name) return patch_list def svgUnitToMpl(unit: str, default=None) -> float: - """ convert a unit text to svg pixels """ + """ Convert a unit text to svg pixels. """ import re if unit == "": return default @@ -596,7 +595,7 @@ def svgUnitToMpl(unit: str, default=None) -> float: def openImageFromLink(link: str) -> np.ndarray: - """ load an embedded image file or an externally liked image file""" + """ Load an embedded image file or an externally liked image file.""" if link.startswith("file:///"): return plt.imread(link[len("file:///"):]) else: @@ -613,7 +612,7 @@ def openImageFromLink(link: str) -> np.ndarray: def parseStyleSheet(text: str) -> list: - """ parse a style sheet text """ + """ Parse a style sheet text. """ # remove line comments text = re.sub("//.*?\n", "", text) # remove multiline comments @@ -633,7 +632,7 @@ def parseStyleSheet(text: str) -> list: def parseGroup(node: minidom.Element, trans: mtransforms.Transform, style: dict, ids: dict, no_draw: bool = False) -> list: - """ parse the children of a group node with the inherited transformation and style """ + """ Parse the children of a group node with the inherited transformation and style. """ trans = parseTransformation(node.getAttribute("transform")) + trans style = get_inline_style(node, style) @@ -686,8 +685,8 @@ def parseGroup(node: minidom.Element, trans: mtransforms.Transform, style: dict, svgUnitToMpl(child.getAttribute("y")), svgUnitToMpl(child.getAttribute("y")) + svgUnitToMpl(child.getAttribute("height")), ], zorder=1) else: - pass#im_patch = plt.imshow(im[::-1], zorder=1) - #patch_list.append(im_patch) + pass # im_patch = plt.imshow(im[::-1], zorder=1) + # patch_list.append(im_patch) elif child.tagName == "metadata": pass # we do not have to draw metadata else: @@ -700,16 +699,16 @@ def parseGroup(node: minidom.Element, trans: mtransforms.Transform, style: dict, def svgread(filename: str): - """ read an SVG file """ + """ Read an SVG file. """ doc = minidom.parse(filename) svg = doc.getElementsByTagName("svg")[0] try: x1, y1, x2, y2 = [svgUnitToMpl(s.strip()) for s in svg.getAttribute("viewBox").split()] - width, height = (x2 - x1)/plt.gcf().dpi, (y2 - y1)/plt.gcf().dpi + width, height = (x2 - x1) / plt.gcf().dpi, (y2 - y1) / plt.gcf().dpi if max([width, height]) > 8: - f = 8/max([width, height]) - plt.gcf().set_size_inches(width*f, height*f) + f = 8 / max([width, height]) + plt.gcf().set_size_inches(width * f, height * f) else: plt.gcf().set_size_inches(width, height) except ValueError: @@ -719,8 +718,8 @@ def svgread(filename: str): width /= plt.gcf().dpi height /= plt.gcf().dpi if max([width, height]) > 8: - f = 8/max([width, height]) - plt.gcf().set_size_inches(width*f, height*f) + f = 8 / max([width, height]) + plt.gcf().set_size_inches(width * f, height * f) else: plt.gcf().set_size_inches(width, height) ax = plt.axes([0, 0, 1, 1], label=filename, frameon=False) diff --git a/pylustrator/pyjack.py b/pylustrator/pyjack.py index 1eb31b6..a61aa61 100644 --- a/pylustrator/pyjack.py +++ b/pylustrator/pyjack.py @@ -17,7 +17,7 @@ import sys as _sys import types as _types -_WRAPPER_TYPES = (type(object.__init__), type(object().__init__),) +_WRAPPER_TYPES = (type(object.__init__), type(object().__init__)) # deactivated closure support as this code does not work for python3 """ @@ -30,6 +30,7 @@ def proxy1(): return data _CELLTYPE = int # type(proxy0(None).func_closure[0]) """ + class PyjackException(Exception): pass @@ -78,7 +79,7 @@ def restore(): fn.restore = restore return fn else: - bundle = (fn, fn_type,) + bundle = (fn, fn_type) raise PyjackException("fn %r of type '%r' not supported" % bundle) @@ -148,7 +149,6 @@ def replace_all_refs(org_obj, new_obj): Python runtime interns strings. """ - _gc.collect() hit = False @@ -197,7 +197,7 @@ def replace_all_refs(org_obj, new_obj): hit = True # TUPLE, FROZENSET - elif isinstance(referrer, (tuple, frozenset,)): + elif isinstance(referrer, (tuple, frozenset)): new_tuple = [] for obj in referrer: if obj is org_obj: @@ -207,7 +207,7 @@ def replace_all_refs(org_obj, new_obj): replace_all_refs(referrer, type(referrer)(new_tuple)) # CELLTYPE (deactivated as it makes problems im Python3) - #elif isinstance(referrer, _CELLTYPE): + # elif isinstance(referrer, _CELLTYPE): # def proxy0(data): # def proxy1(): return data @@ -240,7 +240,7 @@ def replace_all_refs(org_obj, new_obj): # print(type(referrer), file=sys.stderr) pass - #if hit is False: + # if hit is False: # raise AttributeError("Object '%r' not found" % org_obj) return org_obj @@ -303,7 +303,7 @@ def __getattr__(self, attr): try: return getattr(self._fn, attr) except AttributeError: - bundle = (self._fn, attr,) + bundle = (self._fn, attr) raise AttributeError("function %r has no attr '%s'" % bundle) def restore(self): diff --git a/pylustrator/snap.py b/pylustrator/snap.py index 8b55573..8cf114b 100644 --- a/pylustrator/snap.py +++ b/pylustrator/snap.py @@ -51,14 +51,14 @@ def checkXLabel(target: Artist): - """ checks if the target is the xlabel of an axis """ + """ Checks if the target is the xlabel of an axis. """ for axes in target.figure.axes: if axes.xaxis.get_label() == target: return axes def checkYLabel(target: Artist): - """ checks if the target is the ylabel of an axis """ + """ Checks if the target is the ylabel of an axis. """ for axes in target.figure.axes: if axes.yaxis.get_label() == target: return axes @@ -70,10 +70,12 @@ def cache_property(object, name): setattr(object, f"_pylustrator_cached_{name}", True) getter = getattr(object, f"get_{name}") setter = getattr(object, f"set_{name}") + def new_getter(*args, **kwargs): if getattr(object, f"_pylustrator_cache_{name}", None) is None: setattr(object, f"_pylustrator_cache_{name}", getter(*args, **kwargs)) return getattr(object, f"_pylustrator_cache_{name}", None) + def new_setter(*args, **kwargs): result = setter(*args, **kwargs) setattr(object, f"_pylustrator_cache_{name}", None) @@ -82,9 +84,9 @@ def new_setter(*args, **kwargs): setattr(object, f"set_{name}", new_setter) - class TargetWrapper(): - """ a wrapper to add unified set and get position methods for any matplotlib artist """ + """ A wrapper to add unified set and get position methods for any matplotlib artist. """ + target = None def __init__(self, target: Artist): @@ -133,7 +135,7 @@ def __init__(self, target: Artist): self.do_scale = False def get_positions(self, use_previous_offset=False, update_offset=False) -> (int, int, int, int): - """ get the current position of the target Artist """ + """ Get the current position of the target Artist. """ points = [] if isinstance(self.target, Rectangle): points.append(self.target.get_xy()) @@ -196,7 +198,7 @@ def get_positions(self, use_previous_offset=False, update_offset=False) -> (int, return self.transform_points(points) def set_positions(self, points: (int, int)): - """ set the position of the target Artist """ + """ Set the position of the target Artist. """ points = self.transform_inverted_points(points) if self.figure.figure is not None: @@ -222,13 +224,13 @@ def set_positions(self, points: (int, int)): elif isinstance(self.target, FancyArrowPatch): self.target.set_positions(points[0], points[1]) change_tracker.addChange(self.target, - f".set_positions({tuple(points[0])}, {tuple(points[1])})") + f".set_positions({tuple(points[0])}, {tuple(points[1])})") elif isinstance(self.target, Text): if checkXLabel(self.target): axes = checkXLabel(self.target) axes.xaxis.labelpad = -(points[0][1] - self.target.pad_offset) / self.label_factor change_tracker.addChange(axes, - f".xaxis.labelpad = {axes.xaxis.labelpad:f}") + f".xaxis.labelpad = {axes.xaxis.labelpad:f}") self.target.set_position(points[0]) self.label_y = points[0][1] @@ -236,7 +238,7 @@ def set_positions(self, points: (int, int)): axes = checkYLabel(self.target) axes.yaxis.labelpad = -(points[0][0] - self.target.pad_offset) / self.label_factor change_tracker.addChange(axes, - f".yaxis.labelpad = {axes.yaxis.labelpad:f}") + f".yaxis.labelpad = {axes.yaxis.labelpad:f}") self.target.set_position(points[0]) self.label_x = points[0][0] @@ -246,7 +248,7 @@ def set_positions(self, points: (int, int)): change_tracker.addNewTextChange(self.target) else: change_tracker.addChange(self.target, - ".set_position([%f, %f])" % self.target.get_position()) + ".set_position([%f, %f])" % self.target.get_position()) if getattr(self.target, "xy", None) is not None: self.target.xy = points[1] change_tracker.addChange(self.target, ".xy = (%f, %f)" % tuple(self.target.xy)) @@ -254,14 +256,14 @@ def set_positions(self, points: (int, int)): point = self.target.axes.transAxes.inverted().transform(self.transform_inverted_points(points)[0]) self.target._loc = tuple(point) change_tracker.addNewLegendChange(self.target) - #change_tracker.addChange(self.target, "._set_loc((%f, %f))" % tuple(point)) + # change_tracker.addChange(self.target, "._set_loc((%f, %f))" % tuple(point)) elif isinstance(self.target, Axes): position = np.array([points[0], points[1] - points[0]]).flatten() if self.fixed_aspect: position[3] = position[2] * self.target.get_position().height / self.target.get_position().width self.target.set_position(position) change_tracker.addNewAxesChange(self.target) - #change_tracker.addChange(self.target, ".set_position([%f, %f, %f, %f])" % tuple( + # change_tracker.addChange(self.target, ".set_position([%f, %f, %f, %f])" % tuple( # np.array([points[0], points[1] - points[0]]).flatten())) setattr(self.target, "_pylustrator_cached_get_extend", None) @@ -274,7 +276,7 @@ def get_extent(self): return getattr(self.target, "_pylustrator_cached_get_extend") def do_get_extent(self) -> (int, int, int, int): - """ get the extent of the target """ + """ Get the extent of the target. """ points = np.array(self.get_positions()) return [np.min(points[:, 0]), np.min(points[:, 1]), @@ -282,18 +284,19 @@ def do_get_extent(self) -> (int, int, int, int): np.max(points[:, 1])] def transform_points(self, points: (int, int)) -> (int, int): - """ transform points from the targets local coordinate system to the figure coordinate system """ + """ Transform points from the targets local coordinate system to the figure coordinate system. """ transform = self.get_transform() return [transform.transform(p) for p in points] def transform_inverted_points(self, points: (int, int)) -> (int, int): - """ transform points from the figure coordinate system to the targets local coordinate system """ + """ Transform points from the figure coordinate system to the targets local coordinate system. """ transform = self.get_transform() return [transform.inverted().transform(p) for p in points] class SnapBase(): """ The base class to implement snaps. """ + data = None def __init__(self, ax_source: Artist, ax_target: Artist, edge: int): @@ -310,25 +313,25 @@ def __init__(self, ax_source: Artist, ax_target: Artist, edge: int): self.draw_path.setPen(pen1) def getPosition(self, target: TargetWrapper): - """ get the position of a target """ + """ Get the position of a target. """ try: return target.get_extent() except AttributeError: return np.array(target.figure.transFigure.transform(target.get_position())).flatten() def getDistance(self, index: int) -> (int, int): - """ Calculate the distance of the snap to its target """ + """ Calculate the distance of the snap to its target. """ return 0, 0 def checkSnap(self, index: int) -> Optional[float]: - """ Return the distance to the targets or None """ + """ Return the distance to the targets or None. """ distance = self.getDistance(index) if abs(distance) < 10: return distance return None def checkSnapActive(self): - """ Test if the snap condition is fullfilled """ + """ Test if the snap condition is fullfilled. """ distance = min([self.getDistance(index) for index in [0, 1]]) # show the snap if the distance to a target is smaller than 1 if abs(distance) < 1: @@ -337,7 +340,7 @@ def checkSnapActive(self): self.hide() def show(self): - """ Implements a visualisation of the snap, e.g. lines to indicate what objects are snapped to what """ + """ Implements a visualisation of the snap, e.g. lines to indicate what objects are snapped to what. """ pass def set_data(self, xdata, ydata): @@ -365,11 +368,11 @@ def set_data(self, xdata, ydata): self.data = (xdata, ydata) def hide(self): - """ Hides the visualisation """ + """ Hides the visualisation. """ self.set_data((), ()) def remove(self): - """ Remove the snap and its visualisation """ + """ Remove the snap and its visualisation. """ self.hide() try: self.draw_path.scene().removeItem(self.draw_path) @@ -378,10 +381,10 @@ def remove(self): class SnapSameEdge(SnapBase): - """ a snap that checks if two objects share an edge """ + """ A snap that checks if two objects share an edge. """ def getDistance(self, index: int) -> (int, int): - """ Calculate the distance of the snap to its target """ + """ Calculate the distance of the snap to its target. """ # only if the right edge index (x or y) is queried, if not the distance is infinite if self.edge % 2 != index: return np.inf @@ -392,7 +395,7 @@ def getDistance(self, index: int) -> (int, int): return p1[self.edge] - p2[self.edge] def show(self): - """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what """ + """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what. """ # get the position of both objects p1 = self.getPosition(self.ax_source) p2 = self.getPosition(self.ax_target) @@ -407,10 +410,10 @@ def show(self): class SnapSameDimension(SnapBase): - """ a snap that checks if two objects have the same width or height """ + """ A snap that checks if two objects have the same width or height. """ def getDistance(self, index: int) -> (int, int): - """ Calculate the distance of the snap to its target """ + """ Calculate the distance of the snap to its target. """ # only if the right edge index (x or y) is queried, if not the distance is infinite if self.edge % 2 != index: return np.inf @@ -421,7 +424,7 @@ def getDistance(self, index: int) -> (int, int): return (p2[self.edge - 2] - p2[self.edge]) - (p1[self.edge - 2] - p1[self.edge]) def show(self): - """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what """ + """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what. """ # get the position of both objects p1 = self.getPosition(self.ax_source) p2 = self.getPosition(self.ax_target) @@ -438,14 +441,14 @@ def show(self): class SnapSamePos(SnapBase): - """ a snap that checks if two objects have the same position """ + """ A snap that checks if two objects have the same position. """ def getPosition(self, text: TargetWrapper) -> (int, int): # get the position of an object return np.array(text.get_transform().transform(text.target.get_position())) def getDistance(self, index: int) -> int: - """ Calculate the distance of the snap to its target """ + """ Calculate the distance of the snap to its target. """ # only if the right edge index (x or y) is queried, if not the distance is infinite if self.edge % 2 != index: return np.inf @@ -456,7 +459,7 @@ def getDistance(self, index: int) -> int: return p1[self.edge] - p2[self.edge] def show(self): - """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what """ + """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what. """ # get the position of both objects p1 = self.getPosition(self.ax_source) p2 = self.getPosition(self.ax_target) @@ -465,17 +468,15 @@ def show(self): class SnapSameBorder(SnapBase): - """ A snap that checks if tree axes share the space between them """ + """ A snap that checks if tree axes share the space between them. """ def __init__(self, ax_source: Artist, ax_target: Artist, ax_target2: Artist, edge: int): super().__init__(ax_source, ax_target, edge) self.ax_target2 = ax_target2 def overlap(self, p1: list, p2: list, dir: int): - """ Test if two objects have an overlapping x or y region """ - if p1[dir + 2] < p2[dir] or p1[dir] > p2[dir + 2]: - return False - return True + """ Test if two objects have an overlapping x or y region. """ + return not p1[dir + 2] < p2[dir] or p1[dir] > p2[dir + 2] def getBorders(self, p1: list, p2: list): borders = [] @@ -490,7 +491,7 @@ def getBorders(self, p1: list, p2: list): return np.array(borders) def getDistance(self, index: int): - """ Calculate the distance of the snap to its target """ + """ Calculate the distance of the snap to its target. """ # get the positions of all three targets p1 = self.getPosition(self.ax_source) p2 = self.getPosition(self.ax_target) @@ -517,7 +518,7 @@ def getDistance(self, index: int): return np.inf def getConnection(self, p1: list, p2: list, dir: int): - """ return the coordinates of a line that spans the space between to axes """ + """ Return the coordinates of a line that spans the space between to axes. """ # check which edge (e.g. x, y) and which direction (e.g. if to change the order of p1 and p2) edge, order = dir // 2, dir % 2 # optionally change p1 with p2 @@ -532,7 +533,7 @@ def getConnection(self, p1: list, p2: list, dir: int): return [[x, x, np.nan], [p1[3], p2[1], np.nan]] def show(self): - """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what """ + """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what. """ # get the positions of all three axes p1 = self.getPosition(self.ax_source) p2 = self.getPosition(self.ax_target) @@ -546,21 +547,21 @@ def show(self): class SnapCenterWith(SnapBase): - """ A snap that checks if a text is centered with an axes """ + """ A snap that checks if a text is centered with an axes. """ def getPosition(self, text: TargetWrapper) -> (int, int): - """ get the position of the first object """ + """ Get the position of the first object. """ return np.array(text.get_transform().transform(text.target.get_position())) def getPosition2(self, axes: TargetWrapper) -> int: - """ get the position of the second object """ + """ Get the position of the second object. """ pos = np.array(axes.figure.transFigure.transform(axes.target.get_position())) p = pos[0, :] p[self.edge] = np.mean(pos, axis=0)[self.edge] return p def getDistance(self, index: int) -> int: - """ Calculate the distance of the snap to its target """ + """ Calculate the distance of the snap to its target. """ # only if the right edge index (x or y) is queried, if not the distance is infinite if self.edge % 2 != index: return np.inf @@ -571,7 +572,7 @@ def getDistance(self, index: int) -> int: return p1[self.edge] - p2[self.edge] def show(self): - """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what """ + """ A visualisation of the snap, e.g. lines to indicate what objects are snapped to what. """ # get the position of both objects p1 = self.getPosition(self.ax_source) p2 = self.getPosition2(self.ax_target) @@ -580,7 +581,7 @@ def show(self): def checkSnaps(snaps: List[SnapBase]) -> (int, int): - """ get the x and y offsets the snaps suggest """ + """ Get the x and y offsets the snaps suggest. """ result = [0, 0] # iterate over x and y for index in range(2): @@ -598,13 +599,13 @@ def checkSnaps(snaps: List[SnapBase]) -> (int, int): def checkSnapsActive(snaps: List[SnapBase]): - """ check if snaps are active and show them if yes """ + """ Check if snaps are active and show them if yes. """ for snap in snaps: snap.checkSnapActive() def getSnaps(targets: List[TargetWrapper], dir: int, no_height=False) -> List[SnapBase]: - """ get all snap objects for the target and the direction """ + """ Get all snap objects for the target and the direction. """ snaps = [] targets = [t.target for t in targets] for target in targets: diff --git a/setup.py b/setup.py index d9868c2..101fe9a 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,6 @@ 'numpy', 'matplotlib', 'qtawesome', - 'scikit-image' + 'scikit-image', ], ) diff --git a/tests/base_test_class.py b/tests/base_test_class.py index b1c162e..a4f8a07 100644 --- a/tests/base_test_class.py +++ b/tests/base_test_class.py @@ -12,7 +12,8 @@ def ensure_list(obj, count=1): if isinstance(obj, list): return obj else: - return [obj]*count + return [obj] * count + def select_elements(fig, get_obj_list): if not isinstance(get_obj_list, list): @@ -33,9 +34,11 @@ def select_elements(fig, get_obj_list): class Undefined: pass + class NotInSave: pass + class Value: def __init__(self, value, value_saved=Undefined): self.value = value @@ -115,7 +118,7 @@ def check_line_in_file(self, line_start): def grab_args(*args, **kwargs): return args, kwargs - arguments = re.match(re.escape(line_start)+r"(.*)\)", line).groups()[0] + arguments = re.match(re.escape(line_start) + r"(.*)\)", line).groups()[0] return line, eval(f"grab_args({arguments})") if line.startswith("#% start: automatic generated code from pylustrator"): in_block = True @@ -292,12 +295,12 @@ def change_property2(self, property_name_list, value_list, call, get_obj_list, l # test if the text has the right weight self.compare_list(get_obj_list, get_function(), value_list, - f"Property '{property_name_list[0]}' not set correctly. [{test_run}]") + f"Property '{property_name_list[0]}' not set correctly. [{test_run}]") # test undo and redo fig.window.undo() self.compare_list(get_obj_list, get_function(), current_values, - f"Property '{property_name_list[0]}' undo failed. [{test_run}]") + f"Property '{property_name_list[0]}' undo failed. [{test_run}]") if self.no_undo_save_test is False: # the output after undo should be the same as the beginning @@ -308,7 +311,7 @@ def change_property2(self, property_name_list, value_list, call, get_obj_list, l fig.window.redo() self.compare_list(get_obj_list, get_function(), value_list, - f"Property '{property_name_list[0]}' redo failed. [{test_run}]") + f"Property '{property_name_list[0]}' redo failed. [{test_run}]") print("\n---- save after redo ----", end="") fig.change_tracker.save() @@ -327,7 +330,7 @@ def change_property2(self, property_name_list, value_list, call, get_obj_list, l try: # test if the text has the right weight self.compare_list(get_obj_list, get_function(), value_list, - f"Property '{property_name_list[0]}' not set failed. [{test_run}]") + f"Property '{property_name_list[0]}' not set failed. [{test_run}]") # don't move it and save the result self.move_element((0, 0), get_obj_list) diff --git a/tests/test_axes.py b/tests/test_axes.py index 83ac667..4b0e575 100644 --- a/tests/test_axes.py +++ b/tests/test_axes.py @@ -40,22 +40,22 @@ def test_axis_limits(self): test_run = "Change axes limits." self.change_property2("xlim", (1, 10), lambda _: self.fig.window.input_properties.input_xaxis.input_lim.setValue((1, 10), signal=True), get_axes, line_command, - test_run) + test_run) self.change_property2("ylim", (2, 8), lambda _: self.fig.window.input_properties.input_yaxis.input_lim.setValue((2, 8), signal=True), get_axes, line_command, - test_run) + test_run) self.change_property2("xlabel", "label", - lambda _: self.fig.window.input_properties.input_xaxis.input_label.setText("label", - signal=True), - get_axes, line_command, - test_run) + lambda _: self.fig.window.input_properties.input_xaxis.input_label.setText("label", + signal=True), + get_axes, line_command, + test_run) self.change_property2("ylabel", "label", - lambda _: self.fig.window.input_properties.input_yaxis.input_label.setText("label", - signal=True), - get_axes, line_command, - test_run) + lambda _: self.fig.window.input_properties.input_yaxis.input_label.setText("label", + signal=True), + get_axes, line_command, + test_run) get_axes = [lambda: fig.axes[0], lambda: fig.axes[1]] line_command = ["plt.figure(1).axes[0].set(", "plt.figure(1).axes[1].set("] @@ -84,6 +84,7 @@ def test_axis_limits(self): signal=True), get_axes, line_command, test_run) + def test_axis_grid(self): # get the figure fig, text = self.run_plot_script() @@ -197,21 +198,22 @@ def check(): return check # minor ticks + def set_ticks(_): self.fig.window.input_properties.input_xaxis.tick_edit.setTarget(get_axes()) self.fig.window.input_properties.input_xaxis.tick_edit.input_ticks2.setText('10^-2\n0.1 "a"\n0.2 "b\n0.3 c\n0.5', - signal=True) + signal=True) self.change_property2("xticks", [0.01, 0.1, 0.2, 0.3, 0.5], set_ticks, get_axes, line_command, test_run, - test_saved_value=check_saved_property("x"), get_function=lambda: get_axes().get_xticks(minor=True)) + test_saved_value=check_saved_property("x"), get_function=lambda: get_axes().get_xticks(minor=True)) def set_ticks(_): self.fig.window.input_properties.input_yaxis.tick_edit.setTarget(get_axes()) self.fig.window.input_properties.input_yaxis.tick_edit.input_ticks2.setText('10^-2\n0.1 "a"\n0.2 "b\n0.3 c\n0.5', - signal=True) + signal=True) self.change_property2("yticks", [0.01, 0.1, 0.2, 0.3, 0.5], set_ticks, get_axes, line_command, test_run, - test_saved_value=check_saved_property("y"), get_function=lambda: get_axes().get_yticks(minor=True)) + test_saved_value=check_saved_property("y"), get_function=lambda: get_axes().get_yticks(minor=True)) def test_axes_alignment(self): # get the figure diff --git a/tests/test_legend.py b/tests/test_legend.py index fb3299d..0cf642b 100644 --- a/tests/test_legend.py +++ b/tests/test_legend.py @@ -20,7 +20,7 @@ def test_legend_properties(self): self.move_element((0, 0), get_legend) - #self.check_text_properties(get_text, line_command, test_run, 0.4931, 0.4979) + # self.check_text_properties(get_text, line_command, test_run, 0.4931, 0.4979) self.change_property("loc", (x, 0.856602), lambda _: self.move_element((-1, 0)), get_legend, line_command, test_run, get_function=lambda: get_legend()._loc) self.change_property("loc", (0.047605, 0.856602), lambda _: self.move_element((1, 0)), get_legend, line_command, @@ -29,10 +29,10 @@ def test_legend_properties(self): test_run, get_function=lambda: get_legend()._loc) self.change_property("loc", (0.047605, 0.856602), lambda _: self.move_element((0, 1)), get_legend, line_command, test_run, get_function=lambda: get_legend()._loc) - #self.change_property("loc", (0.2, 0.5), + # self.change_property("loc", (0.2, 0.5), # lambda _: fig.window.input_size.input_position.valueChangedX.emit(0.2), get_text, # line_command, test_run, get_function=lambda: get_text()._loc) - #self.change_property("loc", (0.2, 0.2), + # self.change_property("loc", (0.2, 0.2), # lambda _: fig.window.input_size.input_position.valueChangedY.emit(0.2), get_text, # line_command, test_run, get_function=lambda: get_text()._loc) diff --git a/tests/test_text.py b/tests/test_text.py index eccfb78..2ec62cd 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -200,9 +200,9 @@ def test_text_delete(self): test_run = "Delete new text in axes." self.change_property2("visible", False, - lambda _: fig.figure_dragger.selection.keyPressEvent( + lambda _: fig.figure_dragger.selection.keyPressEvent( KeyEvent('delete', fig.canvas, "delete")), get_text, line_command, - test_run, delete=True) + test_run, delete=True) def check_text_properties(self, get_text, line_command, test_run, x, y): fig = self.fig