def new_figure_manager_given_figure(cls, num, figure): """ Create a new figure manager instance for the given figure. """ with _restore_foreground_window_at_end(): window = tk.Tk(className="matplotlib") window.withdraw() # Put a mpl icon on the window rather than the default tk icon. # Tkinter doesn't allow colour icons on linux systems, but tk>=8.5 # has a iconphoto command which we call directly. Source: # http://mail.python.org/pipermail/tkinter-discuss/2006-November/000954.html icon_fname = str(cbook._get_data_path('images/matplotlib.ppm')) icon_img = tk.PhotoImage(file=icon_fname, master=window) try: window.iconphoto(False, icon_img) except Exception as exc: # log the failure (due e.g. to Tk version), but carry on _log.info('Could not load matplotlib icon: %s', exc) canvas = cls.FigureCanvas(figure, master=window) manager = cls.FigureManager(canvas, num, window) if matplotlib.is_interactive(): manager.show() canvas.draw_idle() return manager
def _init_toolbar(self): self.set_style(Gtk.ToolbarStyle.ICONS) self._gtk_ids = {} for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.insert(Gtk.SeparatorToolItem(), -1) continue image = Gtk.Image() image.set_from_file( str(cbook._get_data_path('images', image_file + '.png'))) self._gtk_ids[text] = tbutton = Gtk.ToolButton() tbutton.set_label(text) tbutton.set_icon_widget(image) self.insert(tbutton, -1) tbutton.connect('clicked', getattr(self, callback)) tbutton.set_tooltip_text(tooltip_text) toolitem = Gtk.SeparatorToolItem() self.insert(toolitem, -1) toolitem.set_draw(False) toolitem.set_expand(True) toolitem = Gtk.ToolItem() self.insert(toolitem, -1) self.message = Gtk.Label() toolitem.add(self.message) self.show_all()
def test_if_rctemplate_would_be_valid(tmpdir): # This tests if the matplotlibrc.template file would result in a valid # rc file if all lines are uncommented. with cbook._get_data_path('matplotlibrc').open() as file: rclines = file.readlines() newlines = [] for line in rclines: if line[0] == "#": newline = line[1:] else: newline = line if "$TEMPLATE_BACKEND" in newline: newline = "backend : Agg" if "datapath" in newline: newline = "" newlines.append(newline) d = tmpdir.mkdir('test1') fname = str(d.join('testrcvalid.temp')) with open(fname, "w") as f: f.writelines(newlines) with pytest.warns(None) as record: mpl.rc_params_from_file(fname, fail_on_error=True, use_default_template=False) assert len(record) == 0
def __init__(self, size=None, weight='normal'): self._version = self.__version__ self.__default_weight = weight self.default_size = size paths = [cbook._get_data_path('fonts', subdir) for subdir in ['ttf', 'afm', 'pdfcorefonts']] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: if pathname in os.environ: ttfpath = os.environ[pathname] if ttfpath.find(';') >= 0: # win32 style paths.extend(ttfpath.split(';')) elif ttfpath.find(':') >= 0: # unix style paths.extend(ttfpath.split(':')) else: paths.append(ttfpath) _log.debug('font search path %s', str(paths)) # Load TrueType fonts and create font dictionary. self.defaultFamily = { 'ttf': 'DejaVu Sans', 'afm': 'Helvetica'} ttffiles = findSystemFonts(paths) + findSystemFonts() self.ttflist = createFontList(ttffiles) afmfiles = (findSystemFonts(paths, fontext='afm') + findSystemFonts(fontext='afm')) self.afmlist = createFontList(afmfiles, fontext='afm')
def _Button(self, text, file, command, extension='.gif'): img_file = str(cbook._get_data_path('images', file + extension)) im = tk.PhotoImage(master=self, file=img_file) b = tk.Button( master=self, text=text, padx=2, pady=2, image=im, command=command) b._ntimage = im b.pack(side=tk.LEFT) return b
def __init__(self, canvas, num): FigureManagerBase.__init__(self, canvas, num) self.canvas = canvas self.window = MainWindow() self.window.closing.connect(canvas.close_event) self.window.closing.connect(self._widgetclosed) self.window.setWindowTitle("Figure %d" % num) image = str(cbook._get_data_path('images/matplotlib.svg')) self.window.setWindowIcon(QtGui.QIcon(image)) # Give the keyboard focus to the figure instead of the # manager; StrongFocus accepts both tab and click to focus and # will enable the canvas to process event w/o clicking. # ClickFocus only takes the focus is the window has been # clicked # on. http://qt-project.org/doc/qt-4.8/qt.html#FocusPolicy-enum or # http://doc.qt.digia.com/qt/qt.html#FocusPolicy-enum self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus) self.canvas.setFocus() self.window._destroying = False self.toolmanager = self._get_toolmanager() self.toolbar = self._get_toolbar(self.canvas, self.window) self.statusbar = None if self.toolmanager: backend_tools.add_tools_to_manager(self.toolmanager) if self.toolbar: backend_tools.add_tools_to_container(self.toolbar) self.statusbar = StatusbarQt(self.window, self.toolmanager) if self.toolbar is not None: self.window.addToolBar(self.toolbar) if not self.toolmanager: # add text label to status bar statusbar_label = QtWidgets.QLabel() self.window.statusBar().addWidget(statusbar_label) self.toolbar.message.connect(statusbar_label.setText) tbs_height = self.toolbar.sizeHint().height() else: tbs_height = 0 # resize the main window so it will display the canvas with the # requested size: cs = canvas.sizeHint() sbs = self.window.statusBar().sizeHint() height = cs.height() + tbs_height + sbs.height() self.window.resize(cs.width(), height) self.window.setCentralWidget(self.canvas) if matplotlib.is_interactive(): self.window.show() self.canvas.draw_idle() self.window.raise_()
def __init__(self, canvas, window=None): self._win = window Gtk.Box.__init__(self) self.add_css_class('toolbar') self._gtk_ids = {} for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.append(Gtk.Separator()) continue image = Gtk.Image.new_from_gicon( Gio.Icon.new_for_string( str( cbook._get_data_path('images', f'{image_file}-symbolic.svg')))) self._gtk_ids[text] = button = (Gtk.ToggleButton() if callback in [ 'zoom', 'pan' ] else Gtk.Button()) button.set_child(image) button.add_css_class('flat') button.add_css_class('image-button') # Save the handler id, so that we can block it as needed. button._signal_handler = button.connect('clicked', getattr(self, callback)) button.set_tooltip_text(tooltip_text) self.append(button) # This filler item ensures the toolbar is always at least two text # lines high. Otherwise the canvas gets redrawn as the mouse hovers # over images because those use two-line messages which resize the # toolbar. label = Gtk.Label() label.set_markup( '<small>\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}</small>') label.set_hexpand(True) # Push real message to the right. self.append(label) self.message = Gtk.Label() self.message.set_justify(Gtk.Justification.RIGHT) self.append(self.message) _NavigationToolbar2GTK.__init__(self, canvas)
def __init__(self, size=None, weight='normal'): self._version = self.__version__ self.__default_weight = weight self.default_size = size paths = [ cbook._get_data_path('fonts', subdir) for subdir in ['ttf', 'afm', 'pdfcorefonts'] ] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: if pathname in os.environ: ttfpath = os.environ[pathname] if ttfpath.find(';') >= 0: # win32 style paths.extend(ttfpath.split(';')) elif ttfpath.find(':') >= 0: # unix style paths.extend(ttfpath.split(':')) else: paths.append(ttfpath) cbook.warn_deprecated("3.3", name=pathname, obj_type="environment variable", alternative="FontManager.addfont()") _log.debug('font search path %s', str(paths)) # Load TrueType fonts and create font dictionary. self.defaultFamily = {'ttf': 'DejaVu Sans', 'afm': 'Helvetica'} self.afmlist = [] self.ttflist = [] for fontext in ["afm", "ttf"]: for path in [ *findSystemFonts(paths, fontext=fontext), *findSystemFonts(fontext=fontext) ]: try: self.addfont(path) except OSError as exc: _log.info("Failed to open font file %s: %s", path, exc) except Exception as exc: _log.info("Failed to extract font properties from %s: %s", path, exc)
def __init__(self, targetfig, parent): super().__init__() self.setWindowIcon( QtGui.QIcon(str(cbook._get_data_path("images/matplotlib.png")))) self.setObjectName("SubplotTool") self._spinboxes = {} main_layout = QtWidgets.QHBoxLayout() self.setLayout(main_layout) for group, spinboxes, buttons in [ ("Borders", ["top", "bottom", "left", "right"], [("Export values", self._export_values)]), ("Spacings", ["hspace", "wspace"], [("Tight layout", self._tight_layout), ("Reset", self._reset), ("Close", self.close)]) ]: layout = QtWidgets.QVBoxLayout() main_layout.addLayout(layout) box = QtWidgets.QGroupBox(group) layout.addWidget(box) inner = QtWidgets.QFormLayout(box) for name in spinboxes: self._spinboxes[name] = spinbox = QtWidgets.QDoubleSpinBox() spinbox.setRange(0, 1) spinbox.setDecimals(3) spinbox.setSingleStep(0.005) spinbox.setKeyboardTracking(False) spinbox.valueChanged.connect(self._on_value_changed) inner.addRow(name, spinbox) layout.addStretch(1) for name, method in buttons: button = QtWidgets.QPushButton(name) # Don't trigger on <enter>, which is used to input values. button.setAutoDefault(False) button.clicked.connect(method) layout.addWidget(button) if name == "Close": button.setFocus() self._figure = targetfig self._defaults = {} self._export_values_dialog = None self.update_from_current_subplotpars()
def __init__(self, canvas, window): self.win = window GObject.GObject.__init__(self) self.set_style(Gtk.ToolbarStyle.ICONS) self._gtk_ids = {} for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.insert(Gtk.SeparatorToolItem(), -1) continue image = Gtk.Image.new_from_gicon( Gio.Icon.new_for_string( str( cbook._get_data_path('images', f'{image_file}-symbolic.svg'))), Gtk.IconSize.LARGE_TOOLBAR) self._gtk_ids[text] = tbutton = (Gtk.ToggleToolButton() if callback in ['zoom', 'pan'] else Gtk.ToolButton()) tbutton.set_label(text) tbutton.set_icon_widget(image) self.insert(tbutton, -1) # Save the handler id, so that we can block it as needed. tbutton._signal_handler = tbutton.connect('clicked', getattr(self, callback)) tbutton.set_tooltip_text(tooltip_text) toolitem = Gtk.SeparatorToolItem() self.insert(toolitem, -1) toolitem.set_draw(False) toolitem.set_expand(True) toolitem = Gtk.ToolItem() self.insert(toolitem, -1) self.message = Gtk.Label() toolitem.add(self.message) self.show_all() NavigationToolbar2.__init__(self, canvas)
def __init__(self, canvas, window, *, pack_toolbar=True): # Avoid using self.window (prefer self.canvas.get_tk_widget().master), # so that Tool implementations can reuse the methods. self.window = window tk.Frame.__init__(self, master=window, borderwidth=2, width=int(canvas.figure.bbox.width), height=50) self._buttons = {} for text, tooltip_text, image_file, callback in self.toolitems: if text is None: # Add a spacer; return value is unused. self._Spacer() else: self._buttons[text] = button = self._Button( text, str(cbook._get_data_path(f"images/{image_file}.gif")), toggle=callback in ["zoom", "pan"], command=getattr(self, callback), ) if tooltip_text is not None: ToolTip.createToolTip(button, tooltip_text) # This filler item ensures the toolbar is always at least two text # lines high. Otherwise the canvas gets redrawn as the mouse hovers # over images because those use two-line messages which resize the # toolbar. label = tk.Label(master=self, text='\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}') label.pack(side=tk.RIGHT) self.message = tk.StringVar(master=self) self._message_label = tk.Label(master=self, textvariable=self.message) self._message_label.pack(side=tk.RIGHT) NavigationToolbar2.__init__(self, canvas) if pack_toolbar: self.pack(side=tk.BOTTOM, fill=tk.X)
def test_if_rctemplate_is_up_to_date(): # This tests if the matplotlibrc.template file contains all valid rcParams. deprecated = {*mpl._all_deprecated, *mpl._deprecated_remain_as_none} with cbook._get_data_path('matplotlibrc').open() as file: rclines = file.readlines() missing = {} for k, v in mpl.defaultParams.items(): if k[0] == "_": continue if k in deprecated: continue found = False for line in rclines: if k in line: found = True if not found: missing.update({k: v}) if missing: raise ValueError("The following params are missing in the " "matplotlibrc.template file: {}" .format(missing.items()))
def _set_image_for_button(self, button): """ Set the image for a button based on its pixel size. The pixel size is determined by the DPI scaling of the window. """ if button._image_file is None: return # Allow _image_file to be relative to Matplotlib's "images" data # directory. path_regular = cbook._get_data_path('images', button._image_file) path_large = path_regular.with_name( path_regular.name.replace('.png', '_large.png')) size = button.winfo_pixels('18p') # Use the high-resolution (48x48 px) icon if it exists and is needed with Image.open(path_large if ( size > 24 and path_large.exists()) else path_regular) as im: image = ImageTk.PhotoImage(im.resize((size, size)), master=self) button.configure(image=image, height='18p', width='18p') button._ntimage = image # Prevent garbage collection.
def test_if_rctemplate_is_up_to_date(): # This tests if the matplotlibrc.template file contains all valid rcParams. deprecated = {*mpl._all_deprecated, *mpl._deprecated_remain_as_none} with cbook._get_data_path('matplotlibrc').open() as file: rclines = file.readlines() missing = {} for k, v in mpl.defaultParams.items(): if k[0] == "_": continue if k in deprecated: continue found = False for line in rclines: if k in line: found = True if not found: missing.update({k: v}) if missing: raise ValueError("The following params are missing in the " "matplotlibrc.template file: {}".format( missing.items()))
def __init__(self, canvas, num): self.window = MainWindow() super().__init__(canvas, num) self.window.closing.connect(canvas.close_event) self.window.closing.connect(self._widgetclosed) if sys.platform != "darwin": image = str(cbook._get_data_path('images/matplotlib.svg')) icon = QtGui.QIcon(image) self.window.setWindowIcon(icon) self.window._destroying = False if self.toolbar: self.window.addToolBar(self.toolbar) tbs_height = self.toolbar.sizeHint().height() else: tbs_height = 0 # resize the main window so it will display the canvas with the # requested size: cs = canvas.sizeHint() cs_height = cs.height() height = cs_height + tbs_height self.window.resize(cs.width(), height) self.window.setCentralWidget(self.canvas) if mpl.is_interactive(): self.window.show() self.canvas.draw_idle() # Give the keyboard focus to the figure instead of the manager: # StrongFocus accepts both tab and click to focus and will enable the # canvas to process event without clicking. # https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum self.canvas.setFocusPolicy(_enum("QtCore.Qt.FocusPolicy").StrongFocus) self.canvas.setFocus() self.window.raise_()
def __init__(self, size=None, weight='normal'): self._version = self.__version__ self.__default_weight = weight self.default_size = size # Create list of font paths. paths = [ cbook._get_data_path('fonts', subdir) for subdir in ['ttf', 'afm', 'pdfcorefonts'] ] _log.debug('font search path %s', str(paths)) self.defaultFamily = {'ttf': 'DejaVu Sans', 'afm': 'Helvetica'} self.afmlist = [] self.ttflist = [] # Delay the warning by 5s. timer = Timer( 5, lambda: _log.warning( 'Matplotlib is building the font cache; this may take a moment.' )) timer.start() try: for fontext in ["afm", "ttf"]: for path in [ *findSystemFonts(paths, fontext=fontext), *findSystemFonts(fontext=fontext) ]: try: self.addfont(path) except OSError as exc: _log.info("Failed to open font file %s: %s", path, exc) except Exception as exc: _log.info( "Failed to extract font properties from %s: " "%s", path, exc) finally: timer.cancel()
def _init_toolbar(self): self.basedir = str(cbook._get_data_path('images')) # Ensure that zoom and pan are mutually exclusive. self._button_group = QtWidgets.QButtonGroup() background_color = self.palette().color(self.backgroundRole()) foreground_color = self.palette().color(self.foregroundRole()) icon_color = (foreground_color if background_color.value() < 128 else None) for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.addSeparator() else: a = self.addAction(self._icon(image_file + '.png', icon_color), text, getattr(self, callback)) self._actions[callback] = a if callback in ['zoom', 'pan']: a.setCheckable(True) self._button_group.addButton(self.widgetForAction(a)) if tooltip_text is not None: a.setToolTip(tooltip_text) if text == 'Subplots': a = self.addAction(self._icon("qt4_editor_options.png", icon_color), 'Customize', self.edit_parameters) a.setToolTip('Edit axis, curve and image parameters') # Add the (x, y) location widget at the right side of the toolbar # The stretch factor is 1 which means any resizing of the toolbar # will resize this label instead of the buttons. if self.coordinates: self.locLabel = QtWidgets.QLabel("", self) self.locLabel.setAlignment( QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) self.locLabel.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Ignored)) labelAction = self.addWidget(self.locLabel) labelAction.setVisible(True)
def _icon(self, name): """ Construct a `.QIcon` from an image file *name*, including the extension and relative to Matplotlib's "images" data directory. """ # use a high-resolution icon with suffix '_large' if available # note: user-provided icons may not have '_large' versions path_regular = cbook._get_data_path('images', name) path_large = path_regular.with_name( path_regular.name.replace('.png', '_large.png')) filename = str(path_large if path_large.exists() else path_regular) pm = QtGui.QPixmap(filename) _setDevicePixelRatio(pm, _devicePixelRatioF(self)) if self.palette().color(self.backgroundRole()).value() < 128: icon_color = self.palette().color(self.foregroundRole()) mask = pm.createMaskFromColor( QtGui.QColor('black'), _enum("QtCore.Qt.MaskMode").MaskOutColor) pm.fill(icon_color) pm.setMask(mask) return QtGui.QIcon(pm)
def _init_toolbar(self): self.basedir = str(cbook._get_data_path('images')) for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.addSeparator() else: a = self.addAction(self._icon(image_file + '.png'), text, getattr(self, callback)) self._actions[callback] = a if callback in ['zoom', 'pan']: a.setCheckable(True) if tooltip_text is not None: a.setToolTip(tooltip_text) if text == 'Subplots': a = self.addAction(self._icon("qt4_editor_options.png"), 'Customize', self.edit_parameters) a.setToolTip('Edit axis, curve and image parameters') # Add the x,y location widget at the right side of the toolbar # The stretch factor is 1 which means any resizing of the toolbar # will resize this label instead of the buttons. if self.coordinates: self.locLabel = QtWidgets.QLabel("", self) self.locLabel.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) self.locLabel.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Ignored)) labelAction = self.addWidget(self.locLabel) labelAction.setVisible(True) # Esthetic adjustments - we need to set these explicitly in PyQt5 # otherwise the layout looks different - but we don't want to set it if # not using HiDPI icons otherwise they look worse than before. if is_pyqt5() and self.canvas._dpi_ratio > 1: self.setIconSize(QtCore.QSize(24, 24)) self.layout().setSpacing(12)
def _init_toolbar(self): self.basedir = str(cbook._get_data_path('images')) for text, tooltip_text, image_file, callback in self.toolitems: if text is None: self.addSeparator() else: a = self.addAction(self._icon(image_file + '.png'), text, getattr(self, callback)) self._actions[callback] = a if callback in ['zoom', 'pan']: a.setCheckable(True) if tooltip_text is not None: a.setToolTip(tooltip_text) if text == 'Subplots': a = self.addAction(self._icon("qt4_editor_options.png"), 'Customize', self.edit_parameters) a.setToolTip('Edit axis, curve and image parameters') # Add the x,y location widget at the right side of the toolbar # The stretch factor is 1 which means any resizing of the toolbar # will resize this label instead of the buttons. if self.coordinates: self.locLabel = QtWidgets.QLabel("", self) self.locLabel.setAlignment( QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) self.locLabel.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Ignored)) labelAction = self.addWidget(self.locLabel) labelAction.setVisible(True) # Esthetic adjustments - we need to set these explicitly in PyQt5 # otherwise the layout looks different - but we don't want to set it if # not using HiDPI icons otherwise they look worse than before. if is_pyqt5(): self.setIconSize(QtCore.QSize(24, 24)) self.layout().setSpacing(12)
def __init__(self, canvas, num): app = _backend_gtk._create_application() self.window = Gtk.Window() app.add_window(self.window) super().__init__(canvas, num) self.window.set_wmclass("matplotlib", "Matplotlib") icon_ext = "png" if sys.platform == "win32" else "svg" self.window.set_icon_from_file( str(cbook._get_data_path(f"images/matplotlib.{icon_ext}"))) self.vbox = Gtk.Box() self.vbox.set_property("orientation", Gtk.Orientation.VERTICAL) self.window.add(self.vbox) self.vbox.show() self.canvas.show() self.vbox.pack_start(self.canvas, True, True, 0) # calculate size for window w, h = self.canvas.get_width_height() if self.toolbar is not None: self.toolbar.show() self.vbox.pack_end(self.toolbar, False, False, 0) min_size, nat_size = self.toolbar.get_preferred_size() h += nat_size.height self.window.set_default_size(w, h) self._destroying = False self.window.connect("destroy", lambda *args: Gcf.destroy(self)) self.window.connect("delete_event", lambda *args: Gcf.destroy(self)) if mpl.is_interactive(): self.window.show() self.canvas.draw_idle() self.canvas.grab_focus()
def test_glyphs_subset(): fpath = str(_get_data_path("fonts/ttf/DejaVuSerif.ttf")) chars = "these should be subsetted! 1234567890" # non-subsetted FT2Font nosubfont = FT2Font(fpath) nosubfont.set_text(chars) # subsetted FT2Font subfont = FT2Font(get_glyphs_subset(fpath, chars)) subfont.set_text(chars) nosubcmap = nosubfont.get_charmap() subcmap = subfont.get_charmap() # all unique chars must be available in subsetted font assert set(chars) == set(chr(key) for key in subcmap.keys()) # subsetted font's charmap should have less entries assert len(subcmap) < len(nosubcmap) # since both objects are assigned same characters assert subfont.get_num_glyphs() == nosubfont.get_num_glyphs()
class __getattr__: @_api.deprecated("3.5", obj_type="") @property def cursord(self): try: new_cursor = functools.partial(Gdk.Cursor.new_from_name, Gdk.Display.get_default()) return { Cursors.MOVE: new_cursor("move"), Cursors.HAND: new_cursor("pointer"), Cursors.POINTER: new_cursor("default"), Cursors.SELECT_REGION: new_cursor("crosshair"), Cursors.WAIT: new_cursor("wait"), } except TypeError as exc: return {} icon_filename = _api.deprecated("3.6", obj_type="")( property(lambda self: "matplotlib.png" if sys.platform == "win32" else "matplotlib.svg")) window_icon = _api.deprecated("3.6", obj_type="")( property(lambda self: str( cbook._get_data_path("images", __getattr__("icon_filename")))))
def test_if_rctemplate_would_be_valid(tmpdir): # This tests if the matplotlibrc.template file would result in a valid # rc file if all lines are uncommented. with cbook._get_data_path('matplotlibrc').open() as file: rclines = file.readlines() newlines = [] for line in rclines: if line[0] == "#": newline = line[1:] else: newline = line if "$TEMPLATE_BACKEND" in newline: newline = "backend : Agg" if "datapath" in newline: newline = "" newlines.append(newline) d = tmpdir.mkdir('test1') fname = str(d.join('testrcvalid.temp')) with open(fname, "w") as f: f.writelines(newlines) mpl.rc_params_from_file(fname, fail_on_error=True, # Test also fails on warning. use_default_template=False)
def _icon(name): """ Construct a `wx.Bitmap` suitable for use as icon from an image file *name*, including the extension and relative to Matplotlib's "images" data directory. """ image = np.array(PIL.Image.open(cbook._get_data_path("images", name))) try: dark = wx.SystemSettings.GetAppearance().IsDark() except AttributeError: # wxpython < 4.1 # copied from wx's IsUsingDarkBackground / GetLuminance. bg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) # See wx.Colour.GetLuminance. bg_lum = (.299 * bg.red + .587 * bg.green + .114 * bg.blue) / 255 fg_lum = (.299 * fg.red + .587 * fg.green + .114 * fg.blue) / 255 dark = fg_lum - bg_lum > .2 if dark: fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) black_mask = (image[..., :3] == 0).all(axis=-1) image[black_mask, :3] = (fg.Red(), fg.Green(), fg.Blue()) return wx.Bitmap.FromBufferRGBA(image.shape[1], image.shape[0], image.tobytes())
def _create_qApp(): """ Only one qApp can exist at a time, so check before creating one. """ global qApp if qApp is None: app = QtWidgets.QApplication.instance() if app is None: # display_is_valid returns False only if on Linux and neither X11 # nor Wayland display can be opened. if not mpl._c_internal_utils.display_is_valid(): raise RuntimeError('Invalid DISPLAY variable') try: QtWidgets.QApplication.setAttribute( QtCore.Qt.AA_EnableHighDpiScaling) except AttributeError: # Only for Qt>=5.6, <6. pass try: QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) except AttributeError: # Only for Qt>=5.14. pass qApp = QtWidgets.QApplication(["matplotlib"]) if sys.platform == "darwin": image = str(cbook._get_data_path('images/matplotlib.svg')) icon = QtGui.QIcon(image) qApp.setWindowIcon(icon) qApp.lastWindowClosed.connect(qApp.quit) cbook._setup_new_guiapp() else: qApp = app try: qApp.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) # Only for Qt<6. except AttributeError: pass
class ToolCopyToClipboardGTK3(backend_tools.ToolCopyToClipboardBase): def trigger(self, *args, **kwargs): clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) window = self.canvas.get_window() x, y, width, height = window.get_geometry() pb = Gdk.pixbuf_get_from_window(window, x, y, width, height) clipboard.set_image(pb) # Define the file to use as the GTk icon if sys.platform == 'win32': icon_filename = 'matplotlib.png' else: icon_filename = 'matplotlib.svg' window_icon = str(cbook._get_data_path('images', icon_filename)) def error_msg_gtk(msg, parent=None): if parent is not None: # find the toplevel Gtk.Window parent = parent.get_toplevel() if not parent.is_toplevel(): parent = None if not isinstance(msg, str): msg = ','.join(map(str, msg)) dialog = Gtk.MessageDialog(parent=parent, type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, message_format=msg) dialog.run() dialog.destroy()
class RendererPS(_backend_pdf_ps.RendererPDFPSBase): """ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles. """ _afm_font_dir = cbook._get_data_path("fonts/afm") _use_afm_rc_name = "ps.useafm" def __init__(self, width, height, pswriter, imagedpi=72): # Although postscript itself is dpi independent, we need to inform the # image code about a requested dpi to generate high resolution images # and them scale them before embedding them. super().__init__(width, height) self._pswriter = pswriter if mpl.rcParams['text.usetex']: self.textcnt = 0 self.psfrag = [] self.imagedpi = imagedpi # current renderer state (None=uninitialised) self.color = None self.linewidth = None self.linejoin = None self.linecap = None self.linedash = None self.fontname = None self.fontsize = None self._hatches = {} self.image_magnification = imagedpi / 72 self._clip_paths = {} self._path_collection_id = 0 self._character_tracker = _backend_pdf_ps.CharacterTracker() self._logwarn_once = functools.lru_cache(None)(_log.warning) def _is_transparent(self, rgb_or_rgba): if rgb_or_rgba is None: return True # Consistent with rgbFace semantics. elif len(rgb_or_rgba) == 4: if rgb_or_rgba[3] == 0: return True if rgb_or_rgba[3] != 1: self._logwarn_once( "The PostScript backend does not support transparency; " "partially transparent artists will be rendered opaque.") return False else: # len() == 3. return False def set_color(self, r, g, b, store=True): if (r, g, b) != self.color: self._pswriter.write(f"{r:1.3f} setgray\n" if r == g == b else f"{r:1.3f} {g:1.3f} {b:1.3f} setrgbcolor\n") if store: self.color = (r, g, b) def set_linewidth(self, linewidth, store=True): linewidth = float(linewidth) if linewidth != self.linewidth: self._pswriter.write("%1.3f setlinewidth\n" % linewidth) if store: self.linewidth = linewidth @staticmethod def _linejoin_cmd(linejoin): # Support for directly passing integer values is for backcompat. linejoin = { 'miter': 0, 'round': 1, 'bevel': 2, 0: 0, 1: 1, 2: 2 }[linejoin] return f"{linejoin:d} setlinejoin\n" def set_linejoin(self, linejoin, store=True): if linejoin != self.linejoin: self._pswriter.write(self._linejoin_cmd(linejoin)) if store: self.linejoin = linejoin @staticmethod def _linecap_cmd(linecap): # Support for directly passing integer values is for backcompat. linecap = { 'butt': 0, 'round': 1, 'projecting': 2, 0: 0, 1: 1, 2: 2 }[linecap] return f"{linecap:d} setlinecap\n" def set_linecap(self, linecap, store=True): if linecap != self.linecap: self._pswriter.write(self._linecap_cmd(linecap)) if store: self.linecap = linecap def set_linedash(self, offset, seq, store=True): if self.linedash is not None: oldo, oldseq = self.linedash if np.array_equal(seq, oldseq) and oldo == offset: return self._pswriter.write(f"[{_nums_to_str(*seq)}]" f" {_nums_to_str(offset)} setdash\n" if seq is not None and len(seq) else "[] 0 setdash\n") if store: self.linedash = (offset, seq) def set_font(self, fontname, fontsize, store=True): if (fontname, fontsize) != (self.fontname, self.fontsize): self._pswriter.write(f"/{fontname} {fontsize:1.3f} selectfont\n") if store: self.fontname = fontname self.fontsize = fontsize def create_hatch(self, hatch): sidelen = 72 if hatch in self._hatches: return self._hatches[hatch] name = 'H%d' % len(self._hatches) linewidth = mpl.rcParams['hatch.linewidth'] pageheight = self.height * 72 self._pswriter.write(f"""\ << /PatternType 1 /PaintType 2 /TilingType 2 /BBox[0 0 {sidelen:d} {sidelen:d}] /XStep {sidelen:d} /YStep {sidelen:d} /PaintProc {{ pop {linewidth:g} setlinewidth {self._convert_path( Path.hatch(hatch), Affine2D().scale(sidelen), simplify=False)} gsave fill grestore stroke }} bind >> matrix 0 {pageheight:g} translate makepattern /{name} exch def """) self._hatches[hatch] = name return name def get_image_magnification(self): """ Get the factor by which to magnify images passed to draw_image. Allows a backend to have images at a different resolution to other artists. """ return self.image_magnification def _convert_path(self, path, transform, clip=False, simplify=None): if clip: clip = (0.0, 0.0, self.width * 72.0, self.height * 72.0) else: clip = None return _path.convert_to_string(path, transform, clip, simplify, None, 6, [b"m", b"l", b"", b"c", b"cl"], True).decode("ascii") def _get_clip_cmd(self, gc): clip = [] rect = gc.get_clip_rectangle() if rect is not None: clip.append("%s clipbox\n" % _nums_to_str(*rect.size, *rect.p0)) path, trf = gc.get_clip_path() if path is not None: key = (path, id(trf)) custom_clip_cmd = self._clip_paths.get(key) if custom_clip_cmd is None: custom_clip_cmd = "c%d" % len(self._clip_paths) self._pswriter.write(f"""\ /{custom_clip_cmd} {{ {self._convert_path(path, trf, simplify=False)} clip newpath }} bind def """) self._clip_paths[key] = custom_clip_cmd clip.append(f"{custom_clip_cmd}\n") return "".join(clip) @_log_if_debug_on def draw_image(self, gc, x, y, im, transform=None): # docstring inherited h, w = im.shape[:2] imagecmd = "false 3 colorimage" data = im[::-1, :, :3] # Vertically flipped rgb values. # data.tobytes().hex() has no spaces, so can be linewrapped by simply # splitting data every nchars. It's equivalent to textwrap.fill only # much faster. nchars = 128 data = data.tobytes().hex() hexlines = "\n".join([ data[n * nchars:(n + 1) * nchars] for n in range(math.ceil(len(data) / nchars)) ]) if transform is None: matrix = "1 0 0 1 0 0" xscale = w / self.image_magnification yscale = h / self.image_magnification else: matrix = " ".join(map(str, transform.frozen().to_values())) xscale = 1.0 yscale = 1.0 self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} {x:g} {y:g} translate [{matrix}] concat {xscale:g} {yscale:g} scale /DataString {w:d} string def {w:d} {h:d} 8 [ {w:d} 0 0 -{h:d} 0 {h:d} ] {{ currentfile DataString readhexstring pop }} bind {imagecmd} {hexlines} grestore """) @_log_if_debug_on def draw_path(self, gc, path, transform, rgbFace=None): # docstring inherited clip = rgbFace is None and gc.get_hatch_path() is None simplify = path.should_simplify and clip ps = self._convert_path(path, transform, clip=clip, simplify=simplify) self._draw_ps(ps, gc, rgbFace) @_log_if_debug_on def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): # docstring inherited ps_color = (None if self._is_transparent(rgbFace) else '%1.3f setgray' % rgbFace[0] if rgbFace[0] == rgbFace[1] == rgbFace[2] else '%1.3f %1.3f %1.3f setrgbcolor' % rgbFace[:3]) # construct the generic marker command: # don't want the translate to be global ps_cmd = ['/o {', 'gsave', 'newpath', 'translate'] lw = gc.get_linewidth() alpha = (gc.get_alpha() if gc.get_forced_alpha() or len(gc.get_rgb()) == 3 else gc.get_rgb()[3]) stroke = lw > 0 and alpha > 0 if stroke: ps_cmd.append('%.1f setlinewidth' % lw) ps_cmd.append(self._linejoin_cmd(gc.get_joinstyle())) ps_cmd.append(self._linecap_cmd(gc.get_capstyle())) ps_cmd.append( self._convert_path(marker_path, marker_trans, simplify=False)) if rgbFace: if stroke: ps_cmd.append('gsave') if ps_color: ps_cmd.extend([ps_color, 'fill']) if stroke: ps_cmd.append('grestore') if stroke: ps_cmd.append('stroke') ps_cmd.extend(['grestore', '} bind def']) for vertices, code in path.iter_segments(trans, clip=(0, 0, self.width * 72, self.height * 72), simplify=False): if len(vertices): x, y = vertices[-2:] ps_cmd.append("%g %g o" % (x, y)) ps = '\n'.join(ps_cmd) self._draw_ps(ps, gc, rgbFace, fill=False, stroke=False) @_log_if_debug_on def draw_path_collection(self, gc, master_transform, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): # Is the optimization worth it? Rough calculation: # cost of emitting a path in-line is # (len_path + 2) * uses_per_path # cost of definition+use is # (len_path + 3) + 3 * uses_per_path len_path = len(paths[0].vertices) if len(paths) > 0 else 0 uses_per_path = self._iter_collection_uses_per_path( paths, all_transforms, offsets, facecolors, edgecolors) should_do_optimization = \ len_path + 3 * uses_per_path + 3 < (len_path + 2) * uses_per_path if not should_do_optimization: return RendererBase.draw_path_collection( self, gc, master_transform, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position) path_codes = [] for i, (path, transform) in enumerate( self._iter_collection_raw_paths(master_transform, paths, all_transforms)): name = 'p%d_%d' % (self._path_collection_id, i) path_bytes = self._convert_path(path, transform, simplify=False) self._pswriter.write(f"""\ /{name} {{ newpath translate {path_bytes} }} bind def """) path_codes.append(name) for xo, yo, path_id, gc0, rgbFace in self._iter_collection( gc, master_transform, all_transforms, path_codes, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): ps = "%g %g %s" % (xo, yo, path_id) self._draw_ps(ps, gc0, rgbFace) self._path_collection_id += 1 @_log_if_debug_on def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): # docstring inherited if self._is_transparent(gc.get_rgb()): return # Special handling for fully transparent. if not hasattr(self, "psfrag"): self._logwarn_once( "The PS backend determines usetex status solely based on " "rcParams['text.usetex'] and does not support having " "usetex=True only for some elements; this element will thus " "be rendered as if usetex=False.") self.draw_text(gc, x, y, s, prop, angle, False, mtext) return w, h, bl = self.get_text_width_height_descent(s, prop, ismath="TeX") fontsize = prop.get_size_in_points() thetext = 'psmarker%d' % self.textcnt color = '%1.3f,%1.3f,%1.3f' % gc.get_rgb()[:3] fontcmd = { 'sans-serif': r'{\sffamily %s}', 'monospace': r'{\ttfamily %s}' }.get(mpl.rcParams['font.family'][0], r'{\rmfamily %s}') s = fontcmd % s tex = r'\color[rgb]{%s} %s' % (color, s) # Stick to the bottom alignment. pos = _nums_to_str(x, y - bl) self.psfrag.append(r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}' % (thetext, angle, fontsize, fontsize * 1.25, tex)) self._pswriter.write(f"""\ gsave {pos} moveto ({thetext}) show grestore """) self.textcnt += 1 @_log_if_debug_on def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # docstring inherited if self._is_transparent(gc.get_rgb()): return # Special handling for fully transparent. if ismath == 'TeX': return self.draw_tex(gc, x, y, s, prop, angle) if ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) if mpl.rcParams['ps.useafm']: font = self._get_font_afm(prop) scale = 0.001 * prop.get_size_in_points() thisx = 0 last_name = None # kerns returns 0 for None. xs_names = [] for c in s: name = uni2type1.get(ord(c), f"uni{ord(c):04X}") try: width = font.get_width_from_char_name(name) except KeyError: name = 'question' width = font.get_width_char('?') kern = font.get_kern_dist_from_name(last_name, name) last_name = name thisx += kern * scale xs_names.append((thisx, name)) thisx += width * scale else: font = self._get_font_ttf(prop) font.set_text(s, 0, flags=LOAD_NO_HINTING) self._character_tracker.track(font, s) xs_names = [(item.x, font.get_glyph_name(item.glyph_idx)) for item in _text_helpers.layout(s, font)] self.set_color(*gc.get_rgb()) ps_name = (font.postscript_name.encode("ascii", "replace").decode("ascii")) self.set_font(ps_name, prop.get_size_in_points()) thetext = "\n".join(f"{x:g} 0 m /{name:s} glyphshow" for x, name in xs_names) self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} {x:g} {y:g} translate {angle:g} rotate {thetext} grestore """) @_log_if_debug_on def draw_mathtext(self, gc, x, y, s, prop, angle): """Draw the math text using matplotlib.mathtext.""" width, height, descent, glyphs, rects = \ self._text2path.mathtext_parser.parse(s, 72, prop) self.set_color(*gc.get_rgb()) self._pswriter.write(f"gsave\n" f"{x:g} {y:g} translate\n" f"{angle:g} rotate\n") lastfont = None for font, fontsize, num, ox, oy in glyphs: self._character_tracker.track_glyph(font, num) if (font.postscript_name, fontsize) != lastfont: lastfont = font.postscript_name, fontsize self._pswriter.write( f"/{font.postscript_name} {fontsize} selectfont\n") glyph_name = (font.get_name_char(chr(num)) if isinstance( font, AFM) else font.get_glyph_name(font.get_char_index(num))) self._pswriter.write(f"{ox:g} {oy:g} moveto\n" f"/{glyph_name} glyphshow\n") for ox, oy, w, h in rects: self._pswriter.write(f"{ox} {oy} {w} {h} rectfill\n") self._pswriter.write("grestore\n") @_log_if_debug_on def draw_gouraud_triangle(self, gc, points, colors, trans): self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), colors.reshape((1, 3, 4)), trans) @_log_if_debug_on def draw_gouraud_triangles(self, gc, points, colors, trans): assert len(points) == len(colors) assert points.ndim == 3 assert points.shape[1] == 3 assert points.shape[2] == 2 assert colors.ndim == 3 assert colors.shape[1] == 3 assert colors.shape[2] == 4 shape = points.shape flat_points = points.reshape((shape[0] * shape[1], 2)) flat_points = trans.transform(flat_points) flat_colors = colors.reshape((shape[0] * shape[1], 4)) points_min = np.min(flat_points, axis=0) - (1 << 12) points_max = np.max(flat_points, axis=0) + (1 << 12) factor = np.ceil((2**32 - 1) / (points_max - points_min)) xmin, ymin = points_min xmax, ymax = points_max streamarr = np.empty(shape[0] * shape[1], dtype=[('flags', 'u1'), ('points', '2>u4'), ('colors', '3u1')]) streamarr['flags'] = 0 streamarr['points'] = (flat_points - points_min) * factor streamarr['colors'] = flat_colors[:, :3] * 255.0 stream = quote_ps_string(streamarr.tobytes()) self._pswriter.write(f"""\ gsave << /ShadingType 4 /ColorSpace [/DeviceRGB] /BitsPerCoordinate 32 /BitsPerComponent 8 /BitsPerFlag 8 /AntiAlias true /Decode [ {xmin:g} {xmax:g} {ymin:g} {ymax:g} 0 1 0 1 0 1 ] /DataSource ({stream}) >> shfill grestore """) def _draw_ps(self, ps, gc, rgbFace, *, fill=True, stroke=True): """ Emit the PostScript snippet *ps* with all the attributes from *gc* applied. *ps* must consist of PostScript commands to construct a path. The *fill* and/or *stroke* kwargs can be set to False if the *ps* string already includes filling and/or stroking, in which case `_draw_ps` is just supplying properties and clipping. """ write = self._pswriter.write mightstroke = (gc.get_linewidth() > 0 and not self._is_transparent(gc.get_rgb())) if not mightstroke: stroke = False if self._is_transparent(rgbFace): fill = False hatch = gc.get_hatch() if mightstroke: self.set_linewidth(gc.get_linewidth()) self.set_linejoin(gc.get_joinstyle()) self.set_linecap(gc.get_capstyle()) self.set_linedash(*gc.get_dashes()) self.set_color(*gc.get_rgb()[:3]) write('gsave\n') write(self._get_clip_cmd(gc)) write(ps.strip()) write("\n") if fill: if stroke or hatch: write("gsave\n") self.set_color(*rgbFace[:3], store=False) write("fill\n") if stroke or hatch: write("grestore\n") if hatch: hatch_name = self.create_hatch(hatch) write("gsave\n") write("%f %f %f " % gc.get_hatch_color()[:3]) write("%s setpattern fill grestore\n" % hatch_name) if stroke: write("stroke\n") write("grestore\n")
def configure_subplots(self): image = str(cbook._get_data_path('images/matplotlib.png')) dia = SubplotToolQt(self.canvas.figure, self.canvas.parent()) dia.setWindowIcon(QtGui.QIcon(image)) dia.exec_()
def figure_edit(axes, parent=None): """Edit matplotlib figure options""" sep = (None, None) # separator # Get / General # Cast to builtin floats as they have nicer reprs. xmin, xmax = map(float, axes.get_xlim()) ymin, ymax = map(float, axes.get_ylim()) general = [ ('Title', axes.get_title()), sep, (None, "<b>X-Axis</b>"), ('Left', xmin), ('Right', xmax), ('Label', axes.get_xlabel()), ('Scale', [axes.get_xscale(), 'linear', 'log', 'logit']), sep, (None, "<b>Y-Axis</b>"), ('Bottom', ymin), ('Top', ymax), ('Label', axes.get_ylabel()), ('Scale', [axes.get_yscale(), 'linear', 'log', 'logit']), sep, ('(Re-)Generate automatic legend', False), ] # Save the unit data xconverter = axes.xaxis.converter yconverter = axes.yaxis.converter xunits = axes.xaxis.get_units() yunits = axes.yaxis.get_units() # Sorting for default labels (_lineXXX, _imageXXX). def cmp_key(label): match = re.match(r"(_line|_image)(\d+)", label) if match: return match.group(1), int(match.group(2)) else: return label, 0 # Get / Curves linedict = {} for line in axes.get_lines(): label = line.get_label() if label == '_nolegend_': continue linedict[label] = line curves = [] def prepare_data(d, init): """ Prepare entry for FormLayout. *d* is a mapping of shorthands to style names (a single style may have multiple shorthands, in particular the shorthands `None`, `"None"`, `"none"` and `""` are synonyms); *init* is one shorthand of the initial style. This function returns an list suitable for initializing a FormLayout combobox, namely `[initial_name, (shorthand, style_name), (shorthand, style_name), ...]`. """ if init not in d: d = {**d, init: str(init)} # Drop duplicate shorthands from dict (by overwriting them during # the dict comprehension). name2short = {name: short for short, name in d.items()} # Convert back to {shorthand: name}. short2name = {short: name for name, short in name2short.items()} # Find the kept shorthand for the style specified by init. canonical_init = name2short[d[init]] # Sort by representation and prepend the initial value. return ([canonical_init] + sorted( short2name.items(), key=lambda short_and_name: short_and_name[1])) curvelabels = sorted(linedict, key=cmp_key) for label in curvelabels: line = linedict[label] color = mcolors.to_hex(mcolors.to_rgba(line.get_color(), line.get_alpha()), keep_alpha=True) ec = mcolors.to_hex(mcolors.to_rgba(line.get_markeredgecolor(), line.get_alpha()), keep_alpha=True) fc = mcolors.to_hex(mcolors.to_rgba(line.get_markerfacecolor(), line.get_alpha()), keep_alpha=True) curvedata = [ ('Label', label), sep, (None, '<b>Line</b>'), ('Line style', prepare_data(LINESTYLES, line.get_linestyle())), ('Draw style', prepare_data(DRAWSTYLES, line.get_drawstyle())), ('Width', line.get_linewidth()), ('Color (RGBA)', color), sep, (None, '<b>Marker</b>'), ('Style', prepare_data(MARKERS, line.get_marker())), ('Size', line.get_markersize()), ('Face color (RGBA)', fc), ('Edge color (RGBA)', ec) ] curves.append([curvedata, label, ""]) # Is there a curve displayed? has_curve = bool(curves) # Get ScalarMappables. mappabledict = {} for mappable in [*axes.images, *axes.collections]: label = mappable.get_label() if label == '_nolegend_' or mappable.get_array() is None: continue mappabledict[label] = mappable mappablelabels = sorted(mappabledict, key=cmp_key) mappables = [] cmaps = [(cmap, name) for name, cmap in sorted(cm._cmap_registry.items())] for label in mappablelabels: mappable = mappabledict[label] cmap = mappable.get_cmap() if cmap not in cm._cmap_registry.values(): cmaps = [(cmap, cmap.name), *cmaps] low, high = mappable.get_clim() mappabledata = [ ('Label', label), ('Colormap', [cmap.name] + cmaps), ('Min. value', low), ('Max. value', high), ] if hasattr(mappable, "get_interpolation"): # Images. interpolations = [(name, name) for name in sorted(mimage.interpolations_names)] mappabledata.append( ('Interpolation', [mappable.get_interpolation(), *interpolations])) mappables.append([mappabledata, label, ""]) # Is there a scalarmappable displayed? has_sm = bool(mappables) datalist = [(general, "Axes", "")] if curves: datalist.append((curves, "Curves", "")) if mappables: datalist.append((mappables, "Images, etc.", "")) def apply_callback(data): """A callback to apply changes.""" orig_xlim = axes.get_xlim() orig_ylim = axes.get_ylim() general = data.pop(0) curves = data.pop(0) if has_curve else [] mappables = data.pop(0) if has_sm else [] if data: raise ValueError("Unexpected field") # Set / General (title, xmin, xmax, xlabel, xscale, ymin, ymax, ylabel, yscale, generate_legend) = general if axes.get_xscale() != xscale: axes.set_xscale(xscale) if axes.get_yscale() != yscale: axes.set_yscale(yscale) axes.set_title(title) axes.set_xlim(xmin, xmax) axes.set_xlabel(xlabel) axes.set_ylim(ymin, ymax) axes.set_ylabel(ylabel) # Restore the unit data axes.xaxis.converter = xconverter axes.yaxis.converter = yconverter axes.xaxis.set_units(xunits) axes.yaxis.set_units(yunits) axes.xaxis._update_axisinfo() axes.yaxis._update_axisinfo() # Set / Curves for index, curve in enumerate(curves): line = linedict[curvelabels[index]] (label, linestyle, drawstyle, linewidth, color, marker, markersize, markerfacecolor, markeredgecolor) = curve line.set_label(label) line.set_linestyle(linestyle) line.set_drawstyle(drawstyle) line.set_linewidth(linewidth) rgba = mcolors.to_rgba(color) line.set_alpha(None) line.set_color(rgba) if marker != 'none': line.set_marker(marker) line.set_markersize(markersize) line.set_markerfacecolor(markerfacecolor) line.set_markeredgecolor(markeredgecolor) # Set ScalarMappables. for index, mappable_settings in enumerate(mappables): mappable = mappabledict[mappablelabels[index]] if len(mappable_settings) == 5: label, cmap, low, high, interpolation = mappable_settings mappable.set_interpolation(interpolation) elif len(mappable_settings) == 4: label, cmap, low, high = mappable_settings mappable.set_label(label) mappable.set_cmap(cm.get_cmap(cmap)) mappable.set_clim(*sorted([low, high])) # re-generate legend, if checkbox is checked if generate_legend: draggable = None ncol = 1 if axes.legend_ is not None: old_legend = axes.get_legend() draggable = old_legend._draggable is not None ncol = old_legend._ncol new_legend = axes.legend(ncol=ncol) if new_legend: new_legend.set_draggable(draggable) # Redraw figure = axes.get_figure() figure.canvas.draw() if not (axes.get_xlim() == orig_xlim and axes.get_ylim() == orig_ylim): figure.canvas.toolbar.push_current() data = _formlayout.fedit( datalist, title="Figure options", parent=parent, icon=QtGui.QIcon( str(cbook._get_data_path('images', 'qt4_editor_options.svg'))), apply=apply_callback) if data is not None: apply_callback(data)
def basedir(self): return str(cbook._get_data_path('images'))
def _init_toolbar(self): _macosx.NavigationToolbar2.__init__( self, str(cbook._get_data_path('images')))
def figure_edit(axes, parent=None): """Edit matplotlib figure options""" sep = (None, None) # separator # Get / General # Cast to builtin floats as they have nicer reprs. xmin, xmax = map(float, axes.get_xlim()) ymin, ymax = map(float, axes.get_ylim()) general = [('Title', axes.get_title()), sep, (None, "<b>X-Axis</b>"), ('Left', xmin), ('Right', xmax), ('Label', axes.get_xlabel()), ('Scale', [axes.get_xscale(), 'linear', 'log', 'logit']), sep, (None, "<b>Y-Axis</b>"), ('Bottom', ymin), ('Top', ymax), ('Label', axes.get_ylabel()), ('Scale', [axes.get_yscale(), 'linear', 'log', 'logit']), sep, ('(Re-)Generate automatic legend', False), ] # Save the unit data xconverter = axes.xaxis.converter yconverter = axes.yaxis.converter xunits = axes.xaxis.get_units() yunits = axes.yaxis.get_units() # Sorting for default labels (_lineXXX, _imageXXX). def cmp_key(label): match = re.match(r"(_line|_image)(\d+)", label) if match: return match.group(1), int(match.group(2)) else: return label, 0 # Get / Curves linedict = {} for line in axes.get_lines(): label = line.get_label() if label == '_nolegend_': continue linedict[label] = line curves = [] def prepare_data(d, init): """Prepare entry for FormLayout. `d` is a mapping of shorthands to style names (a single style may have multiple shorthands, in particular the shorthands `None`, `"None"`, `"none"` and `""` are synonyms); `init` is one shorthand of the initial style. This function returns an list suitable for initializing a FormLayout combobox, namely `[initial_name, (shorthand, style_name), (shorthand, style_name), ...]`. """ if init not in d: d = {**d, init: str(init)} # Drop duplicate shorthands from dict (by overwriting them during # the dict comprehension). name2short = {name: short for short, name in d.items()} # Convert back to {shorthand: name}. short2name = {short: name for name, short in name2short.items()} # Find the kept shorthand for the style specified by init. canonical_init = name2short[d[init]] # Sort by representation and prepend the initial value. return ([canonical_init] + sorted(short2name.items(), key=lambda short_and_name: short_and_name[1])) curvelabels = sorted(linedict, key=cmp_key) for label in curvelabels: line = linedict[label] color = mcolors.to_hex( mcolors.to_rgba(line.get_color(), line.get_alpha()), keep_alpha=True) ec = mcolors.to_hex( mcolors.to_rgba(line.get_markeredgecolor(), line.get_alpha()), keep_alpha=True) fc = mcolors.to_hex( mcolors.to_rgba(line.get_markerfacecolor(), line.get_alpha()), keep_alpha=True) curvedata = [ ('Label', label), sep, (None, '<b>Line</b>'), ('Line style', prepare_data(LINESTYLES, line.get_linestyle())), ('Draw style', prepare_data(DRAWSTYLES, line.get_drawstyle())), ('Width', line.get_linewidth()), ('Color (RGBA)', color), sep, (None, '<b>Marker</b>'), ('Style', prepare_data(MARKERS, line.get_marker())), ('Size', line.get_markersize()), ('Face color (RGBA)', fc), ('Edge color (RGBA)', ec)] curves.append([curvedata, label, ""]) # Is there a curve displayed? has_curve = bool(curves) # Get ScalarMappables. mappabledict = {} for mappable in [*axes.images, *axes.collections]: label = mappable.get_label() if label == '_nolegend_' or mappable.get_array() is None: continue mappabledict[label] = mappable mappablelabels = sorted(mappabledict, key=cmp_key) mappables = [] cmaps = [(cmap, name) for name, cmap in sorted(cm.cmap_d.items())] for label in mappablelabels: mappable = mappabledict[label] cmap = mappable.get_cmap() if cmap not in cm.cmap_d.values(): cmaps = [(cmap, cmap.name), *cmaps] low, high = mappable.get_clim() mappabledata = [ ('Label', label), ('Colormap', [cmap.name] + cmaps), ('Min. value', low), ('Max. value', high), ] if hasattr(mappable, "get_interpolation"): # Images. interpolations = [ (name, name) for name in sorted(mimage.interpolations_names)] mappabledata.append(( 'Interpolation', [mappable.get_interpolation(), *interpolations])) mappables.append([mappabledata, label, ""]) # Is there a scalarmappable displayed? has_sm = bool(mappables) datalist = [(general, "Axes", "")] if curves: datalist.append((curves, "Curves", "")) if mappables: datalist.append((mappables, "Images, etc.", "")) def apply_callback(data): """This function will be called to apply changes""" orig_xlim = axes.get_xlim() orig_ylim = axes.get_ylim() general = data.pop(0) curves = data.pop(0) if has_curve else [] mappables = data.pop(0) if has_sm else [] if data: raise ValueError("Unexpected field") # Set / General (title, xmin, xmax, xlabel, xscale, ymin, ymax, ylabel, yscale, generate_legend) = general if axes.get_xscale() != xscale: axes.set_xscale(xscale) if axes.get_yscale() != yscale: axes.set_yscale(yscale) axes.set_title(title) axes.set_xlim(xmin, xmax) axes.set_xlabel(xlabel) axes.set_ylim(ymin, ymax) axes.set_ylabel(ylabel) # Restore the unit data axes.xaxis.converter = xconverter axes.yaxis.converter = yconverter axes.xaxis.set_units(xunits) axes.yaxis.set_units(yunits) axes.xaxis._update_axisinfo() axes.yaxis._update_axisinfo() # Set / Curves for index, curve in enumerate(curves): line = linedict[curvelabels[index]] (label, linestyle, drawstyle, linewidth, color, marker, markersize, markerfacecolor, markeredgecolor) = curve line.set_label(label) line.set_linestyle(linestyle) line.set_drawstyle(drawstyle) line.set_linewidth(linewidth) rgba = mcolors.to_rgba(color) line.set_alpha(None) line.set_color(rgba) if marker != 'none': line.set_marker(marker) line.set_markersize(markersize) line.set_markerfacecolor(markerfacecolor) line.set_markeredgecolor(markeredgecolor) # Set ScalarMappables. for index, mappable_settings in enumerate(mappables): mappable = mappabledict[mappablelabels[index]] if len(mappable_settings) == 5: label, cmap, low, high, interpolation = mappable_settings mappable.set_interpolation(interpolation) elif len(mappable_settings) == 4: label, cmap, low, high = mappable_settings mappable.set_label(label) mappable.set_cmap(cm.get_cmap(cmap)) mappable.set_clim(*sorted([low, high])) # re-generate legend, if checkbox is checked if generate_legend: draggable = None ncol = 1 if axes.legend_ is not None: old_legend = axes.get_legend() draggable = old_legend._draggable is not None ncol = old_legend._ncol new_legend = axes.legend(ncol=ncol) if new_legend: new_legend.set_draggable(draggable) # Redraw figure = axes.get_figure() figure.canvas.draw() if not (axes.get_xlim() == orig_xlim and axes.get_ylim() == orig_ylim): figure.canvas.toolbar.push_current() data = _formlayout.fedit( datalist, title="Figure options", parent=parent, icon=QtGui.QIcon( str(cbook._get_data_path('images', 'qt4_editor_options.svg'))), apply=apply_callback) if data is not None: apply_callback(data)
def _load_bitmap(filename): """ Load a wx.Bitmap from a file in the "images" directory of the Matplotlib data. """ return wx.Bitmap(str(cbook._get_data_path('images', filename)))
def get(self): self.set_header('Content-Type', 'image/png') self.write( cbook._get_data_path('images/matplotlib.png').read_bytes())
def _copy_css_file(app, exc): if exc is None and app.builder.format == 'html': src = cbook._get_data_path('plot_directive/plot_directive.css') dst = app.outdir / Path('_static') dst.mkdir(exist_ok=True) shutil.copy(src, dst)
def _set_image_for_button(self, button): """ Set the image for a button based on its pixel size. The pixel size is determined by the DPI scaling of the window. """ if button._image_file is None: return # Allow _image_file to be relative to Matplotlib's "images" data # directory. path_regular = cbook._get_data_path('images', button._image_file) path_large = path_regular.with_name( path_regular.name.replace('.png', '_large.png')) size = button.winfo_pixels('18p') # Nested functions because ToolbarTk calls _Button. def _get_color(color_name): # `winfo_rgb` returns an (r, g, b) tuple in the range 0-65535 return button.winfo_rgb(button.cget(color_name)) def _is_dark(color): if isinstance(color, str): color = _get_color(color) return max(color) < 65535 / 2 def _recolor_icon(image, color): image_data = np.asarray(image).copy() black_mask = (image_data[..., :3] == 0).all(axis=-1) image_data[black_mask, :3] = color return Image.fromarray(image_data, mode="RGBA") # Use the high-resolution (48x48 px) icon if it exists and is needed with Image.open(path_large if ( size > 24 and path_large.exists()) else path_regular) as im: image = ImageTk.PhotoImage(im.resize((size, size)), master=self) button._ntimage = image # create a version of the icon with the button's text color foreground = (255 / 65535) * np.array( button.winfo_rgb(button.cget("foreground"))) im_alt = _recolor_icon(im, foreground) image_alt = ImageTk.PhotoImage(im_alt.resize((size, size)), master=self) button._ntimage_alt = image_alt if _is_dark("background"): button.configure(image=image_alt) else: button.configure(image=image) # Checkbuttons may switch the background to `selectcolor` in the # checked state, so check separately which image it needs to use in # that state to still ensure enough contrast with the background. if (isinstance(button, tk.Checkbutton) and button.cget("selectcolor") != ""): if self._windowingsystem != "x11": selectcolor = "selectcolor" else: # On X11, selectcolor isn't used directly for indicator-less # buttons. See `::tk::CheckEnter` in the Tk button.tcl source # code for details. r1, g1, b1 = _get_color("selectcolor") r2, g2, b2 = _get_color("activebackground") selectcolor = ((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2) if _is_dark(selectcolor): button.configure(selectimage=image_alt) else: button.configure(selectimage=image) button.configure(height='18p', width='18p')
class RendererPS(_backend_pdf_ps.RendererPDFPSBase): """ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles. """ _afm_font_dir = cbook._get_data_path("fonts/afm") _use_afm_rc_name = "ps.useafm" def __init__(self, width, height, pswriter, imagedpi=72): # Although postscript itself is dpi independent, we need to inform the # image code about a requested dpi to generate high resolution images # and them scale them before embedding them. super().__init__(width, height) self._pswriter = pswriter if mpl.rcParams['text.usetex']: self.textcnt = 0 self.psfrag = [] self.imagedpi = imagedpi # current renderer state (None=uninitialised) self.color = None self.linewidth = None self.linejoin = None self.linecap = None self.linedash = None self.fontname = None self.fontsize = None self._hatches = {} self.image_magnification = imagedpi / 72 self._clip_paths = {} self._path_collection_id = 0 self._character_tracker = _backend_pdf_ps.CharacterTracker() self.mathtext_parser = MathTextParser("PS") @cbook.deprecated("3.3") @property def used_characters(self): return self._character_tracker.used_characters @cbook.deprecated("3.3") def track_characters(self, *args, **kwargs): """Keep track of which characters are required from each font.""" self._character_tracker.track(*args, **kwargs) @cbook.deprecated("3.3") def merge_used_characters(self, *args, **kwargs): self._character_tracker.merge(*args, **kwargs) def set_color(self, r, g, b, store=True): if (r, g, b) != self.color: if r == g and r == b: self._pswriter.write("%1.3f setgray\n" % r) else: self._pswriter.write( "%1.3f %1.3f %1.3f setrgbcolor\n" % (r, g, b)) if store: self.color = (r, g, b) def set_linewidth(self, linewidth, store=True): linewidth = float(linewidth) if linewidth != self.linewidth: self._pswriter.write("%1.3f setlinewidth\n" % linewidth) if store: self.linewidth = linewidth def set_linejoin(self, linejoin, store=True): if linejoin != self.linejoin: self._pswriter.write("%d setlinejoin\n" % linejoin) if store: self.linejoin = linejoin def set_linecap(self, linecap, store=True): if linecap != self.linecap: self._pswriter.write("%d setlinecap\n" % linecap) if store: self.linecap = linecap def set_linedash(self, offset, seq, store=True): if self.linedash is not None: oldo, oldseq = self.linedash if np.array_equal(seq, oldseq) and oldo == offset: return if seq is not None and len(seq): s = "[%s] %d setdash\n" % (_nums_to_str(*seq), offset) self._pswriter.write(s) else: self._pswriter.write("[] 0 setdash\n") if store: self.linedash = (offset, seq) def set_font(self, fontname, fontsize, store=True): if mpl.rcParams['ps.useafm']: return if (fontname, fontsize) != (self.fontname, self.fontsize): out = ("/%s findfont\n" "%1.3f scalefont\n" "setfont\n" % (fontname, fontsize)) self._pswriter.write(out) if store: self.fontname = fontname self.fontsize = fontsize def create_hatch(self, hatch): sidelen = 72 if hatch in self._hatches: return self._hatches[hatch] name = 'H%d' % len(self._hatches) linewidth = mpl.rcParams['hatch.linewidth'] pageheight = self.height * 72 self._pswriter.write(f"""\ << /PatternType 1 /PaintType 2 /TilingType 2 /BBox[0 0 {sidelen:d} {sidelen:d}] /XStep {sidelen:d} /YStep {sidelen:d} /PaintProc {{ pop {linewidth:f} setlinewidth {self._convert_path( Path.hatch(hatch), Affine2D().scale(sidelen), simplify=False)} gsave fill grestore stroke }} bind >> matrix 0.0 {pageheight:f} translate makepattern /{name} exch def """) self._hatches[hatch] = name return name def get_image_magnification(self): """ Get the factor by which to magnify images passed to draw_image. Allows a backend to have images at a different resolution to other artists. """ return self.image_magnification def draw_image(self, gc, x, y, im, transform=None): # docstring inherited h, w = im.shape[:2] imagecmd = "false 3 colorimage" data = im[::-1, :, :3] # Vertically flipped rgb values. # area_data.tobytes().hex() has no spaces, so can be linewrapped by simply # splitting area_data every nchars. It's equivalent to textwrap.fill only # much faster. nchars = 128 data = data.tobytes().hex() hexlines = "\n".join( [ data[n * nchars:(n + 1) * nchars] for n in range(math.ceil(len(data) / nchars)) ] ) if transform is None: matrix = "1 0 0 1 0 0" xscale = w / self.image_magnification yscale = h / self.image_magnification else: matrix = " ".join(map(str, transform.frozen().to_values())) xscale = 1.0 yscale = 1.0 bbox = gc.get_clip_rectangle() clippath, clippath_trans = gc.get_clip_path() clip = [] if bbox is not None: clip.append('%s clipbox' % _nums_to_str(*bbox.size, *bbox.p0)) if clippath is not None: id = self._get_clip_path(clippath, clippath_trans) clip.append('%s' % id) clip = '\n'.join(clip) self._pswriter.write(f"""\ gsave {clip} {x:f} {y:f} translate [{matrix}] concat {xscale:f} {yscale:f} scale /DataString {w:d} string def {w:d} {h:d} 8 [ {w:d} 0 0 -{h:d} 0 {h:d} ] {{ currentfile DataString readhexstring pop }} bind {imagecmd} {hexlines} grestore """) def _convert_path(self, path, transform, clip=False, simplify=None): if clip: clip = (0.0, 0.0, self.width * 72.0, self.height * 72.0) else: clip = None return _path.convert_to_string( path, transform, clip, simplify, None, 6, [b'm', b'l', b'', b'c', b'cl'], True).decode('ascii') def _get_clip_path(self, clippath, clippath_transform): key = (clippath, id(clippath_transform)) pid = self._clip_paths.get(key) if pid is None: pid = 'c%x' % len(self._clip_paths) clippath_bytes = self._convert_path( clippath, clippath_transform, simplify=False) self._pswriter.write(f"""\ /{pid} {{ {clippath_bytes} clip newpath }} bind def """) self._clip_paths[key] = pid return pid def draw_path(self, gc, path, transform, rgbFace=None): # docstring inherited clip = rgbFace is None and gc.get_hatch_path() is None simplify = path.should_simplify and clip ps = self._convert_path(path, transform, clip=clip, simplify=simplify) self._draw_ps(ps, gc, rgbFace) def draw_markers( self, gc, marker_path, marker_trans, path, trans, rgbFace=None): # docstring inherited if debugPS: self._pswriter.write('% draw_markers \n') ps_color = ( None if _is_transparent(rgbFace) else '%1.3f setgray' % rgbFace[0] if rgbFace[0] == rgbFace[1] == rgbFace[2] else '%1.3f %1.3f %1.3f setrgbcolor' % rgbFace[:3]) # construct the generic marker command: # don't want the translate to be global ps_cmd = ['/o {', 'gsave', 'newpath', 'translate'] lw = gc.get_linewidth() alpha = (gc.get_alpha() if gc.get_forced_alpha() or len(gc.get_rgb()) == 3 else gc.get_rgb()[3]) stroke = lw > 0 and alpha > 0 if stroke: ps_cmd.append('%.1f setlinewidth' % lw) jint = gc.get_joinstyle() ps_cmd.append('%d setlinejoin' % jint) cint = gc.get_capstyle() ps_cmd.append('%d setlinecap' % cint) ps_cmd.append(self._convert_path(marker_path, marker_trans, simplify=False)) if rgbFace: if stroke: ps_cmd.append('gsave') if ps_color: ps_cmd.extend([ps_color, 'fill']) if stroke: ps_cmd.append('grestore') if stroke: ps_cmd.append('stroke') ps_cmd.extend(['grestore', '} bind def']) for vertices, code in path.iter_segments( trans, clip=(0, 0, self.width*72, self.height*72), simplify=False): if len(vertices): x, y = vertices[-2:] ps_cmd.append("%g %g o" % (x, y)) ps = '\n'.join(ps_cmd) self._draw_ps(ps, gc, rgbFace, fill=False, stroke=False) def draw_path_collection(self, gc, master_transform, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): # Is the optimization worth it? Rough calculation: # cost of emitting a path in-line is # (len_path + 2) * uses_per_path # cost of definition+use is # (len_path + 3) + 3 * uses_per_path len_path = len(paths[0].vertices) if len(paths) > 0 else 0 uses_per_path = self._iter_collection_uses_per_path( paths, all_transforms, offsets, facecolors, edgecolors) should_do_optimization = \ len_path + 3 * uses_per_path + 3 < (len_path + 2) * uses_per_path if not should_do_optimization: return RendererBase.draw_path_collection( self, gc, master_transform, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position) path_codes = [] for i, (path, transform) in enumerate(self._iter_collection_raw_paths( master_transform, paths, all_transforms)): name = 'p%x_%x' % (self._path_collection_id, i) path_bytes = self._convert_path(path, transform, simplify=False) self._pswriter.write(f"""\ /{name} {{ newpath translate {path_bytes} }} bind def """) path_codes.append(name) for xo, yo, path_id, gc0, rgbFace in self._iter_collection( gc, master_transform, all_transforms, path_codes, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): ps = "%g %g %s" % (xo, yo, path_id) self._draw_ps(ps, gc0, rgbFace) self._path_collection_id += 1 @cbook._delete_parameter("3.3", "ismath") def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): # docstring inherited if not hasattr(self, "psfrag"): _log.warning( "The PS backend determines usetex status solely based on " "rcParams['text.usetex'] and does not support having " "usetex=True only for some elements; this element will thus " "be rendered as if usetex=False.") self.draw_text(gc, x, y, s, prop, angle, False, mtext) return w, h, bl = self.get_text_width_height_descent(s, prop, ismath="TeX") fontsize = prop.get_size_in_points() thetext = 'psmarker%d' % self.textcnt color = '%1.3f,%1.3f,%1.3f' % gc.get_rgb()[:3] fontcmd = {'sans-serif': r'{\sffamily %s}', 'monospace': r'{\ttfamily %s}'}.get( mpl.rcParams['font.family'][0], r'{\rmfamily %s}') s = fontcmd % s tex = r'\color[rgb]{%s} %s' % (color, s) corr = 0 # w/2*(fontsize-10)/10 if dict.__getitem__(mpl.rcParams, 'text.latex.preview'): # use baseline alignment! pos = _nums_to_str(x-corr, y) self.psfrag.append( r'\psfrag{%s}[Bl][Bl][1][%f]{\fontsize{%f}{%f}%s}' % ( thetext, angle, fontsize, fontsize*1.25, tex)) else: # Stick to the bottom alignment. pos = _nums_to_str(x-corr, y-bl) self.psfrag.append( r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}' % ( thetext, angle, fontsize, fontsize*1.25, tex)) self._pswriter.write(f"""\ gsave {pos} moveto ({thetext}) show grestore """) self.textcnt += 1 def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): # docstring inherited if debugPS: self._pswriter.write("% text\n") if _is_transparent(gc.get_rgb()): return # Special handling for fully transparent. if ismath == 'TeX': return self.draw_tex(gc, x, y, s, prop, angle) elif ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) elif mpl.rcParams['ps.useafm']: self.set_color(*gc.get_rgb()) font = self._get_font_afm(prop) fontname = font.get_fontname() fontsize = prop.get_size_in_points() scale = 0.001 * fontsize thisx = 0 thisy = font.get_str_bbox_and_descent(s)[4] * scale last_name = None lines = [] for c in s: name = uni2type1.get(ord(c), f"uni{ord(c):04X}") try: width = font.get_width_from_char_name(name) except KeyError: name = 'question' width = font.get_width_char('?') if last_name is not None: kern = font.get_kern_dist_from_name(last_name, name) else: kern = 0 last_name = name thisx += kern * scale lines.append('%f %f m /%s glyphshow' % (thisx, thisy, name)) thisx += width * scale thetext = "\n".join(lines) self._pswriter.write(f"""\ gsave /{fontname} findfont {fontsize} scalefont setfont {x:f} {y:f} translate {angle:f} rotate {thetext} grestore """) else: font = self._get_font_ttf(prop) font.set_text(s, 0, flags=LOAD_NO_HINTING) self._character_tracker.track(font, s) self.set_color(*gc.get_rgb()) ps_name = (font.postscript_name .encode('ascii', 'replace').decode('ascii')) self.set_font(ps_name, prop.get_size_in_points()) thetext = '\n'.join( '%f 0 m /%s glyphshow' % (x, font.get_glyph_name(glyph_idx)) for glyph_idx, x in _text_layout.layout(s, font)) self._pswriter.write(f"""\ gsave {x:f} {y:f} translate {angle:f} rotate {thetext} grestore """) def new_gc(self): # docstring inherited return GraphicsContextPS() def draw_mathtext(self, gc, x, y, s, prop, angle): """Draw the math text using matplotlib.mathtext.""" if debugPS: self._pswriter.write("% mathtext\n") width, height, descent, pswriter, used_characters = \ self.mathtext_parser.parse(s, 72, prop) self._character_tracker.merge(used_characters) self.set_color(*gc.get_rgb()) thetext = pswriter.getvalue() self._pswriter.write(f"""\ gsave {x:f} {y:f} translate {angle:f} rotate {thetext} grestore """) def draw_gouraud_triangle(self, gc, points, colors, trans): self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), colors.reshape((1, 3, 4)), trans) def draw_gouraud_triangles(self, gc, points, colors, trans): assert len(points) == len(colors) assert points.ndim == 3 assert points.shape[1] == 3 assert points.shape[2] == 2 assert colors.ndim == 3 assert colors.shape[1] == 3 assert colors.shape[2] == 4 shape = points.shape flat_points = points.reshape((shape[0] * shape[1], 2)) flat_points = trans.transform(flat_points) flat_colors = colors.reshape((shape[0] * shape[1], 4)) points_min = np.min(flat_points, axis=0) - (1 << 12) points_max = np.max(flat_points, axis=0) + (1 << 12) factor = np.ceil((2 ** 32 - 1) / (points_max - points_min)) xmin, ymin = points_min xmax, ymax = points_max streamarr = np.empty( shape[0] * shape[1], dtype=[('flags', 'u1'), ('points', '2>u4'), ('colors', '3u1')]) streamarr['flags'] = 0 streamarr['points'] = (flat_points - points_min) * factor streamarr['colors'] = flat_colors[:, :3] * 255.0 stream = quote_ps_string(streamarr.tobytes()) self._pswriter.write(f"""\ gsave << /ShadingType 4 /ColorSpace [/DeviceRGB] /BitsPerCoordinate 32 /BitsPerComponent 8 /BitsPerFlag 8 /AntiAlias true /Decode [ {xmin:f} {xmax:f} {ymin:f} {ymax:f} 0 1 0 1 0 1 ] /DataSource ({stream}) >> shfill grestore """) def _draw_ps(self, ps, gc, rgbFace, fill=True, stroke=True, command=None): """ Emit the PostScript snippet 'ps' with all the attributes from 'gc' applied. 'ps' must consist of PostScript commands to construct a path. The fill and/or stroke kwargs can be set to False if the 'ps' string already includes filling and/or stroking, in which case _draw_ps is just supplying properties and clipping. """ # local variable eliminates all repeated attribute lookups write = self._pswriter.write if debugPS and command: write("% "+command+"\n") mightstroke = (gc.get_linewidth() > 0 and not _is_transparent(gc.get_rgb())) if not mightstroke: stroke = False if _is_transparent(rgbFace): fill = False hatch = gc.get_hatch() if mightstroke: self.set_linewidth(gc.get_linewidth()) jint = gc.get_joinstyle() self.set_linejoin(jint) cint = gc.get_capstyle() self.set_linecap(cint) self.set_linedash(*gc.get_dashes()) self.set_color(*gc.get_rgb()[:3]) write('gsave\n') cliprect = gc.get_clip_rectangle() if cliprect: write('%1.4g %1.4g %1.4g %1.4g clipbox\n' % (*cliprect.size, *cliprect.p0)) clippath, clippath_trans = gc.get_clip_path() if clippath: id = self._get_clip_path(clippath, clippath_trans) write('%s\n' % id) # Jochen, is the strip necessary? - this could be a honking big string write(ps.strip()) write("\n") if fill: if stroke or hatch: write("gsave\n") self.set_color(*rgbFace[:3], store=False) write("fill\n") if stroke or hatch: write("grestore\n") if hatch: hatch_name = self.create_hatch(hatch) write("gsave\n") write("%f %f %f " % gc.get_hatch_color()[:3]) write("%s setpattern fill grestore\n" % hatch_name) if stroke: write("stroke\n") write("grestore\n")
class ToolCopyToClipboardGTK3(backend_tools.ToolCopyToClipboardBase): def trigger(self, *args, **kwargs): clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) window = self.canvas.get_window() x, y, width, height = window.get_geometry() pb = Gdk.pixbuf_get_from_window(window, x, y, width, height) clipboard.set_image(pb) # Define the file to use as the GTk icon if sys.platform == 'win32': icon_filename = 'matplotlib.png' else: icon_filename = 'matplotlib.svg' window_icon = str(cbook._get_data_path('images', icon_filename)) def error_msg_gtk(msg, parent=None): if parent is not None: # find the toplevel Gtk.Window parent = parent.get_toplevel() if not parent.is_toplevel(): parent = None if not isinstance(msg, str): msg = ','.join(map(str, msg)) dialog = Gtk.MessageDialog( parent=parent, type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, message_format=msg) dialog.run() dialog.destroy()
def __init__(self, canvas): self.canvas = canvas # Needed by the _macosx __init__. _macosx.NavigationToolbar2.__init__( self, str(cbook._get_data_path('images'))) NavigationToolbar2.__init__(self, canvas)