Source code for magicclass.widgets.misc

from __future__ import annotations
from typing import TYPE_CHECKING, Iterable, MutableSequence
from qtpy.QtWidgets import QTabWidget, QLineEdit, QMenu
from qtpy.QtGui import QTextCursor
from qtpy.QtCore import Qt
from magicgui.widgets import PushButton, TextEdit, Table

from .utils import FreeWidget

if TYPE_CHECKING:
    from qtpy.QtWidgets import QTextEdit
    from matplotlib.axes import Axes

[docs]class Figure(FreeWidget): """ A matplotlib figure canvas. """ def __init__(self, nrows: int = 1, ncols: int = 1, figsize: tuple[int, int] = (4, 3), style = None, **kwargs): import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvas backend = mpl.get_backend() try: mpl.use("Agg") if style is None: fig, _ = plt.subplots(nrows, ncols, figsize=figsize) else: with plt.style.context(style): fig, _ = plt.subplots(nrows, ncols, figsize=figsize) finally: mpl.use(backend) super().__init__(**kwargs) canvas = FigureCanvas(fig) self.set_widget(canvas) self.figure = fig self.min_height = 40 for name, method in self.__class__.__dict__.items(): if name.startswith("_") or getattr(method, "__doc__", None) is None: continue plt_method = getattr(plt, name, None) plt_doc = getattr(plt_method, "__doc__", "") if plt_doc: method.__doc__ += f"\nOriginal docstring:\n\n{plt_doc}"
[docs] def draw(self): """Copy of ``plt.draw()``.""" self.figure.tight_layout() self.figure.canvas.draw()
[docs] def clf(self): """Copy of ``plt.clf``.""" self.figure.clf() self.draw()
[docs] def cla(self): """Copy of ``plt.cla``.""" self.ax.cla() self.draw()
@property def axes(self) -> "list[Axes]": return self.figure.axes @property def ax(self) -> "Axes": try: _ax = self.axes[0] except IndexError: _ax = self.figure.add_subplot(111) return _ax
[docs] def plot(self, *args, **kwargs) -> "Axes": """Copy of ``plt.plot``.""" self.ax.plot(*args, **kwargs) self.draw() return self.ax
[docs] def scatter(self, *args, **kwargs) -> "Axes": """Copy of ``plt.scatter``.""" self.ax.scatter(*args, **kwargs) self.draw() return self.ax
[docs] def hist(self, *args, **kwargs): """Copy of ``plt.hist``.""" self.ax.hist(*args, **kwargs) self.draw() return self.ax
[docs] def text(self, *args, **kwargs): """Copy of ``plt.text``.""" self.ax.text(*args, **kwargs) self.draw() return self.ax
[docs] def xlim(self, *args, **kwargs): """Copy of ``plt.xlim``.""" ax = self.ax if not args and not kwargs: return ax.get_xlim() ret = ax.set_xlim(*args, **kwargs) self.draw() return ret
[docs] def ylim(self, *args, **kwargs): """Copy of ``plt.ylim``.""" ax = self.ax if not args and not kwargs: return ax.get_ylim() ret = ax.set_ylim(*args, **kwargs) self.draw() return ret
[docs] def imshow(self, *args, **kwargs) -> "Axes": """Copy of ``plt.imshow``.""" self.ax.imshow(*args, **kwargs) self.draw() return self.ax
[docs] def legend(self, *args, **kwargs): """Copy of ``plt.legend``.""" leg = self.ax.legend(*args, **kwargs) self.draw() return leg
[docs] def title(self, *args, **kwargs): """Copy of ``plt.title``.""" title = self.ax.set_title(*args, **kwargs) self.draw() return title
[docs] def xlabel(self, *args, **kwargs): """Copy of ``plt.xlabel``.""" xlabel = self.ax.set_xlabel(*args, **kwargs) self.draw() return xlabel
[docs] def ylabel(self, *args, **kwargs): """Copy of ``plt.ylabel``.""" ylabel = self.ax.set_ylabel(*args, **kwargs) self.draw() return ylabel
[docs]class ConsoleTextEdit(TextEdit): """A text edit with console-like setting.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) from qtpy.QtGui import QFont, QTextOption self.native: QTextEdit font = QFont("Consolas") font.setStyleHint(QFont.Monospace) font.setFixedPitch(True) self.native.setFont(font) self.native.setWordWrapMode(QTextOption.NoWrap) # set tab width self.tab_size = 4 @property def tab_size(self): metrics = self.native.fontMetrics() return self.native.tabStopWidth() // metrics.width(" ") @tab_size.setter def tab_size(self, size: int): metrics = self.native.fontMetrics() self.native.setTabStopWidth(size*metrics.width(" "))
[docs] def append(self, text: str): """Append new text.""" self.native.append(text)
[docs] def erase_last(self): """Erase the last line.""" cursor = self.native.textCursor() cursor.movePosition(QTextCursor.End) cursor.select(QTextCursor.LineUnderCursor) cursor.removeSelectedText() cursor.deletePreviousChar() self.native.setTextCursor(cursor)
@property def selected(self) -> str: """Return selected string.""" cursor = self.native.textCursor() return cursor.selectedText().replace(u"\u2029", "\n")
[docs]class CheckButton(PushButton): """A checkable button.""" def __init__(self, text: str | None = None, **kwargs): super().__init__(text=text, **kwargs) self.native.setCheckable(True)
class _QtSpreadSheet(QTabWidget): def __init__(self): super().__init__() self.setMovable(True) self._n_table = 0 self.tabBar().tabBarDoubleClicked.connect(self.editTabBarLabel) self.tabBar().setContextMenuPolicy(Qt.CustomContextMenu) self.tabBar().customContextMenuRequested.connect(self.showContextMenu) self._line_edit = None def addTable(self, table): self.addTab(table, f"Sheet {self._n_table}") self._n_table += 1 def renameTab(self, index: int, name: str) -> None: self.tabBar().setTabText(index, name) return None def editTabBarLabel(self, index: int): if index < 0: return if self._line_edit is not None: self._line_edit.deleteLater() self._line_edit = None tabbar = self.tabBar() self._line_edit = QLineEdit(self) @self._line_edit.editingFinished.connect def _(_=None): self.renameTab(index, self._line_edit.text()) self._line_edit.deleteLater() self._line_edit = None self._line_edit.setText(tabbar.tabText(index)) self._line_edit.setGeometry(tabbar.tabRect(index)) self._line_edit.setFocus() self._line_edit.selectAll() self._line_edit.show() def showContextMenu(self, point): if point.isNull(): return tabbar = self.tabBar() index = tabbar.tabAt(point) menu = QMenu(self) rename_action = menu.addAction("Rename") rename_action.triggered.connect(lambda _: self.editTabBarLabel(index)) delete_action = menu.addAction("Delete") delete_action.triggered.connect(lambda _: self.removeTab(index)) menu.exec(tabbar.mapToGlobal(point))
[docs]class SpreadSheet(FreeWidget, MutableSequence[Table]): """A simple spread sheet widget.""" def __init__(self): super().__init__() spreadsheet = _QtSpreadSheet() self.set_widget(spreadsheet) self.central_widget: _QtSpreadSheet self._tables: list[Table] = [] def __len__(self) -> int: return self.central_widget.count()
[docs] def index(self, item: Table | str): if isinstance(item, Table): for i, table in enumerate(self._tables): if item is table: return i else: raise ValueError elif isinstance(item, str): tabbar = self.central_widget.tabBar() for i in range(tabbar.count()): text = tabbar.tabText(i) if text == item: return i else: raise ValueError else: raise TypeError
def __getitem__(self, key): if isinstance(key, str): key = self.index(key) return self._tables[key] def __setitem__(self, key, value): raise NotImplementedError def __delitem__(self, key): if isinstance(key, str): key = self.index(key) self.central_widget.removeTab(key) del self._tables[key] def __iter__(self) -> Iterable[Table]: return iter(self._tables)
[docs] def insert(self, key: int, value): if key < 0: key += len(self) table = Table(value=value) self.central_widget.addTable(table.native) self._tables.insert(key, table)
[docs] def rename(self, index: int, name: str): self.central_widget.renameTab(index, name) return None