Skip to content

plotting

Classes:

Functions:

STYLE_DIR module-attribute

STYLE_DIR = parent / 'plotting.mplstyle'

PlottingCanvas

PlottingCanvas(
    main: MainWindow,
    ylog: bool = False,
    xlog: bool = False,
    controls: bool = True,
    xpad: float | tuple[float, float] | None = None,
    ypad: float | tuple[float, float] | None = None,
    figsize: tuple[int, int] = (5, 4),
)

Bases: FigureCanvasQTAgg

Methods:

Attributes:

Source code
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def __init__(
    self, main: MainWindow, ylog: bool = False, xlog: bool = False, controls: bool = True,
    xpad: float | tuple[float, float] | None = None, ypad: float | tuple[float, float] | None = None,
    figsize: tuple[int, int] = (5, 4)
) -> None:
    from ..abstracts import HBoxLayout, PushButton
    from ..types import Stretch

    self.main = main

    self.ylog, self.xlog = ylog, xlog

    self.figure = Figure(figsize, self.main.settings.base_ppi)

    self.axes = self.figure.add_subplot(111)

    self.figure.set_layout_engine(ConstrainedLayoutEngine())

    super().__init__(self.figure)

    self.mpl_connect('motion_notify_event', self._on_mouse_moved)

    self.angleRemainder = 0
    self.zoomValue = 0.0

    self.clicked = False
    self.old_pos = QPointF(0.0, 0.0)

    self.controls = QFrame()

    self.copy_frame_button = PushButton('Copy Graph', clicked=self.copy_graph_to_clipboard)

    self.save_frame_as_button = PushButton('Save Graph as', clicked=self.on_save_graph_as_clicked)

    _controls = [self.copy_frame_button, self.save_frame_as_button, Stretch()]

    if controls:
        self.controls = HBoxLayout(_controls)
    else:
        self.controls = QFrame()
        HBoxLayout(self.controls, _controls)
        self.controls.hide()

    self.xpad = (xpad, xpad) if (not isinstance(xpad, tuple) and xpad is not None) else xpad
    self.ypad = (ypad, ypad) if (not isinstance(ypad, tuple) and ypad is not None) else ypad

WHEEL_STEP class-attribute instance-attribute

WHEEL_STEP = 15 * 8

angleRemainder instance-attribute

angleRemainder = 0

axes instance-attribute

axes = add_subplot(111)

clicked instance-attribute

clicked = False

controls instance-attribute

controls = QFrame()

copy_frame_button instance-attribute

copy_frame_button = PushButton('Copy Graph', clicked=copy_graph_to_clipboard)

cur_lims property writable

figure instance-attribute

figure = Figure(figsize, base_ppi)

line property

line: Line2D

main instance-attribute

main = main

mouseMoved class-attribute instance-attribute

mouseMoved = pyqtSignal(QMouseEvent)

old_pos instance-attribute

old_pos = QPointF(0.0, 0.0)

save_frame_as_button instance-attribute

save_frame_as_button = PushButton(
    "Save Graph as", clicked=on_save_graph_as_clicked
)

wheelScrolled class-attribute instance-attribute

wheelScrolled = pyqtSignal(int)

xpad instance-attribute

xpad = (
    (xpad, xpad) if not isinstance(xpad, tuple) and xpad is not None else xpad
)

ypad instance-attribute

ypad = (
    (ypad, ypad) if not isinstance(ypad, tuple) and ypad is not None else ypad
)

zoomValue instance-attribute

zoomValue = 0.0

clear

clear() -> None
Source code
260
261
262
263
264
265
266
267
def clear(self) -> None:
    self.axes.clear()

    if self.ylog:
        self.axes.set_yscale("log")

    if self.xlog:
        self.axes.set_xscale("log")

copy_graph_to_clipboard

copy_graph_to_clipboard() -> None
Source code
273
274
275
276
277
278
279
280
281
282
def copy_graph_to_clipboard(self) -> None:
    buffer = BytesIO()

    self.figure.savefig(buffer, transparent=False)

    self.main.clipboard.setImage(QImage.fromData(buffer.getvalue()))

    buffer.close()

    self.main.show_message('Graph successfully copied to clipboard')

draw

draw() -> None
Source code
269
270
271
def draw(self) -> None:
    if 0 not in self.figure.get_size_inches():
        super().draw()

event

event(event: QEvent) -> bool
Source code
155
156
157
158
159
160
161
162
163
164
165
166
167
def event(self, event: QEvent) -> bool:
    if isinstance(event, QNativeGestureEvent):
        typ = event.gestureType()

        if typ == Qt.NativeGestureType.BeginNativeGesture:
            self.zoomValue = 0.0
        elif typ == Qt.NativeGestureType.ZoomNativeGesture:
            self.zoomValue += event.value()

        if typ == Qt.NativeGestureType.EndNativeGesture:
            self.zoom_function(event, -1 if self.zoomValue < 0 else 1)

    return super().event(event)

limits_to_range classmethod

limits_to_range(lim: tuple[int, int]) -> int
Source code
123
124
125
@classmethod
def limits_to_range(cls, lim: tuple[int, int]) -> int:
    return lim[1] - lim[0]

mouseMoveEvent

mouseMoveEvent(event: QMouseEvent) -> None
Source code
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def mouseMoveEvent(self, event: QMouseEvent) -> None:
    self.mouseMoved.emit(event)

    if self.clicked:
        cur_xlim, cur_ylim = self.cur_lims

        curr_pos = event.globalPosition()

        rel_x = self.old_pos.x() - curr_pos.x()
        rel_y = self.old_pos.y() - curr_pos.y()

        width, height = self.get_width_height()
        v_width, v_height = cur_xlim[1] - cur_xlim[0], cur_ylim[1] - cur_ylim[0]

        xrate = rel_x * v_width / width
        yrate = rel_y * v_height / height

        self.cur_lims = (
            (x + xrate for x in cur_xlim),
            (x - yrate for x in cur_ylim)
        )

    self.old_pos = event.globalPosition()

    try:
        super().mouseMoveEvent(event)
    except Exception:
        event.ignore()

mousePressEvent

mousePressEvent(event: QMouseEvent) -> None
Source code
169
170
171
def mousePressEvent(self, event: QMouseEvent) -> None:
    if event.button() == Qt.MouseButton.LeftButton:
        self.clicked = True

mouseReleaseEvent

mouseReleaseEvent(event: QMouseEvent) -> None
Source code
173
174
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
    self.clicked = False

on_mouse_moved

on_mouse_moved(event: MouseEvent) -> None
Source code
120
121
def on_mouse_moved(self, event: PlotMouseEvent) -> None:
    ...

on_save_graph_as_clicked

on_save_graph_as_clicked(checked: bool | None = None) -> None
Source code
284
285
286
287
288
289
def on_save_graph_as_clicked(self, checked: bool | None = None) -> None:
    save_path_str, _ = QFileDialog.getSaveFileName(
        self.main, 'Save as', f'graph_{self.main.current_output.last_showed_frame}', 'Graph (*.svg)'
    )

    self.figure.savefig(save_path_str, transparent=False)

render

render(frame: Frame, set_lims: bool = True) -> None
Source code
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
def render(self, frame: Frame, set_lims: bool = True) -> None:
    if not set_lims:
        self.zoomValue = 0.0

    lims = self.cur_lims if self.zoomValue else None

    self.clear()

    self._render(frame)

    self.axes.legend()
    self.figure.canvas.draw_idle()

    if lims:
        self.cur_lims = lims
    elif self.xpad or self.ypad:
        xlim, ylim = self.cur_lims

        line = self.line

        self.cur_lims = (
            (line._xorig[0] - self.xpad[0], line._xorig[-1] + self.xpad[1]) if self.xpad else xlim,
            (line._yorig[0] - self.ypad[0], line._yorig[-1] + self.ypad[1]) if self.ypad else ylim
        )

wheelEvent

wheelEvent(event: QWheelEvent) -> None
Source code
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def wheelEvent(self, event: QWheelEvent) -> None:
    modifier = event.modifiers()
    mouse = event.buttons()

    if modifier == Qt.KeyboardModifier.ControlModifier or mouse in {
        Qt.MouseButton.RightButton, Qt.MouseButton.MiddleButton
    }:
        angleDelta = event.angleDelta().y()

        if self.angleRemainder * angleDelta < 0:
            self.angleRemainder = 0

        self.angleRemainder += angleDelta

        if abs(self.angleRemainder) >= self.WHEEL_STEP:
            steps = self.angleRemainder // self.WHEEL_STEP
            self.zoomValue += steps
            self.zoom_function(event, steps)

            self.angleRemainder %= self.WHEEL_STEP

    event.ignore()

zoom_function

zoom_function(event: QWheelEvent, steps: int) -> None
Source code
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def zoom_function(self, event: QWheelEvent, steps: int) -> None:
    if not steps:
        return

    scale_factor = 1
    base_scale = 1.1

    for _ in range(abs(steps)):
        if steps > 0:
            scale_factor *= base_scale
        else:
            scale_factor /= base_scale

    cur_xlim, cur_ylim = self.cur_lims

    pos = event.position()
    xdata, ydata = pos.x(), pos.y()
    width, height = self.get_width_height()

    xrate, yrate = xdata / width, ydata / height

    xabs = cur_xlim[1] * xrate + cur_xlim[0] * (1 - xrate)
    yabs = cur_ylim[0] * yrate + cur_ylim[1] * (1 - yrate)

    new_width = abs(cur_xlim[1] - cur_xlim[0]) / scale_factor
    new_height = abs(cur_ylim[1] - cur_ylim[0]) / scale_factor

    self.cur_lims = (
        (xabs - new_width * xrate, xabs + new_width * (1 - xrate)),
        (yabs - new_height * (1 - yrate), yabs + new_height * yrate)
    )

PlottingCanvasDefaultFrame

PlottingCanvasDefaultFrame(
    main: MainWindow,
    ylog: bool = False,
    xlog: bool = False,
    controls: bool = True,
    xpad: float | tuple[float, float] | None = None,
    ypad: float | tuple[float, float] | None = None,
    figsize: tuple[int, int] = (5, 4),
)

Bases: PlottingCanvas

Methods:

Attributes:

Source code
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def __init__(
    self, main: MainWindow, ylog: bool = False, xlog: bool = False, controls: bool = True,
    xpad: float | tuple[float, float] | None = None, ypad: float | tuple[float, float] | None = None,
    figsize: tuple[int, int] = (5, 4)
) -> None:
    from ..abstracts import HBoxLayout, PushButton
    from ..types import Stretch

    self.main = main

    self.ylog, self.xlog = ylog, xlog

    self.figure = Figure(figsize, self.main.settings.base_ppi)

    self.axes = self.figure.add_subplot(111)

    self.figure.set_layout_engine(ConstrainedLayoutEngine())

    super().__init__(self.figure)

    self.mpl_connect('motion_notify_event', self._on_mouse_moved)

    self.angleRemainder = 0
    self.zoomValue = 0.0

    self.clicked = False
    self.old_pos = QPointF(0.0, 0.0)

    self.controls = QFrame()

    self.copy_frame_button = PushButton('Copy Graph', clicked=self.copy_graph_to_clipboard)

    self.save_frame_as_button = PushButton('Save Graph as', clicked=self.on_save_graph_as_clicked)

    _controls = [self.copy_frame_button, self.save_frame_as_button, Stretch()]

    if controls:
        self.controls = HBoxLayout(_controls)
    else:
        self.controls = QFrame()
        HBoxLayout(self.controls, _controls)
        self.controls.hide()

    self.xpad = (xpad, xpad) if (not isinstance(xpad, tuple) and xpad is not None) else xpad
    self.ypad = (ypad, ypad) if (not isinstance(ypad, tuple) and ypad is not None) else ypad

WHEEL_STEP class-attribute instance-attribute

WHEEL_STEP = 15 * 8

angleRemainder instance-attribute

angleRemainder = 0

axes instance-attribute

axes = add_subplot(111)

clicked instance-attribute

clicked = False

controls instance-attribute

controls = QFrame()

copy_frame_button instance-attribute

copy_frame_button = PushButton('Copy Graph', clicked=copy_graph_to_clipboard)

cur_lims property writable

figure instance-attribute

figure = Figure(figsize, base_ppi)

line property

line: Line2D

main instance-attribute

main = main

mouseMoved class-attribute instance-attribute

mouseMoved = pyqtSignal(QMouseEvent)

old_pos instance-attribute

old_pos = QPointF(0.0, 0.0)

save_frame_as_button instance-attribute

save_frame_as_button = PushButton(
    "Save Graph as", clicked=on_save_graph_as_clicked
)

wheelScrolled class-attribute instance-attribute

wheelScrolled = pyqtSignal(int)

xpad instance-attribute

xpad = (
    (xpad, xpad) if not isinstance(xpad, tuple) and xpad is not None else xpad
)

ypad instance-attribute

ypad = (
    (ypad, ypad) if not isinstance(ypad, tuple) and ypad is not None else ypad
)

zoomValue instance-attribute

zoomValue = 0.0

clear

clear() -> None
Source code
260
261
262
263
264
265
266
267
def clear(self) -> None:
    self.axes.clear()

    if self.ylog:
        self.axes.set_yscale("log")

    if self.xlog:
        self.axes.set_xscale("log")

copy_graph_to_clipboard

copy_graph_to_clipboard() -> None
Source code
273
274
275
276
277
278
279
280
281
282
def copy_graph_to_clipboard(self) -> None:
    buffer = BytesIO()

    self.figure.savefig(buffer, transparent=False)

    self.main.clipboard.setImage(QImage.fromData(buffer.getvalue()))

    buffer.close()

    self.main.show_message('Graph successfully copied to clipboard')

draw

draw() -> None
Source code
269
270
271
def draw(self) -> None:
    if 0 not in self.figure.get_size_inches():
        super().draw()

event

event(event: QEvent) -> bool
Source code
155
156
157
158
159
160
161
162
163
164
165
166
167
def event(self, event: QEvent) -> bool:
    if isinstance(event, QNativeGestureEvent):
        typ = event.gestureType()

        if typ == Qt.NativeGestureType.BeginNativeGesture:
            self.zoomValue = 0.0
        elif typ == Qt.NativeGestureType.ZoomNativeGesture:
            self.zoomValue += event.value()

        if typ == Qt.NativeGestureType.EndNativeGesture:
            self.zoom_function(event, -1 if self.zoomValue < 0 else 1)

    return super().event(event)

limits_to_range classmethod

limits_to_range(lim: tuple[int, int]) -> int
Source code
123
124
125
@classmethod
def limits_to_range(cls, lim: tuple[int, int]) -> int:
    return lim[1] - lim[0]

mouseMoveEvent

mouseMoveEvent(event: QMouseEvent) -> None
Source code
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def mouseMoveEvent(self, event: QMouseEvent) -> None:
    self.mouseMoved.emit(event)

    if self.clicked:
        cur_xlim, cur_ylim = self.cur_lims

        curr_pos = event.globalPosition()

        rel_x = self.old_pos.x() - curr_pos.x()
        rel_y = self.old_pos.y() - curr_pos.y()

        width, height = self.get_width_height()
        v_width, v_height = cur_xlim[1] - cur_xlim[0], cur_ylim[1] - cur_ylim[0]

        xrate = rel_x * v_width / width
        yrate = rel_y * v_height / height

        self.cur_lims = (
            (x + xrate for x in cur_xlim),
            (x - yrate for x in cur_ylim)
        )

    self.old_pos = event.globalPosition()

    try:
        super().mouseMoveEvent(event)
    except Exception:
        event.ignore()

mousePressEvent

mousePressEvent(event: QMouseEvent) -> None
Source code
169
170
171
def mousePressEvent(self, event: QMouseEvent) -> None:
    if event.button() == Qt.MouseButton.LeftButton:
        self.clicked = True

mouseReleaseEvent

mouseReleaseEvent(event: QMouseEvent) -> None
Source code
173
174
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
    self.clicked = False

on_mouse_moved

on_mouse_moved(event: MouseEvent) -> None
Source code
120
121
def on_mouse_moved(self, event: PlotMouseEvent) -> None:
    ...

on_save_graph_as_clicked

on_save_graph_as_clicked(checked: bool | None = None) -> None
Source code
284
285
286
287
288
289
def on_save_graph_as_clicked(self, checked: bool | None = None) -> None:
    save_path_str, _ = QFileDialog.getSaveFileName(
        self.main, 'Save as', f'graph_{self.main.current_output.last_showed_frame}', 'Graph (*.svg)'
    )

    self.figure.savefig(save_path_str, transparent=False)

render

render(frame: Frame | None = None, set_lims: bool = True) -> None
Source code
322
323
324
325
326
327
328
329
def render(self, frame: Frame | None = None, set_lims: bool = True) -> None:
    if not self.main.current_output:
        return

    if frame is None:
        frame = self.main.current_output.last_showed_frame

    super().render(frame, set_lims)

wheelEvent

wheelEvent(event: QWheelEvent) -> None
Source code
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def wheelEvent(self, event: QWheelEvent) -> None:
    modifier = event.modifiers()
    mouse = event.buttons()

    if modifier == Qt.KeyboardModifier.ControlModifier or mouse in {
        Qt.MouseButton.RightButton, Qt.MouseButton.MiddleButton
    }:
        angleDelta = event.angleDelta().y()

        if self.angleRemainder * angleDelta < 0:
            self.angleRemainder = 0

        self.angleRemainder += angleDelta

        if abs(self.angleRemainder) >= self.WHEEL_STEP:
            steps = self.angleRemainder // self.WHEEL_STEP
            self.zoomValue += steps
            self.zoom_function(event, steps)

            self.angleRemainder %= self.WHEEL_STEP

    event.ignore()

zoom_function

zoom_function(event: QWheelEvent, steps: int) -> None
Source code
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def zoom_function(self, event: QWheelEvent, steps: int) -> None:
    if not steps:
        return

    scale_factor = 1
    base_scale = 1.1

    for _ in range(abs(steps)):
        if steps > 0:
            scale_factor *= base_scale
        else:
            scale_factor /= base_scale

    cur_xlim, cur_ylim = self.cur_lims

    pos = event.position()
    xdata, ydata = pos.x(), pos.y()
    width, height = self.get_width_height()

    xrate, yrate = xdata / width, ydata / height

    xabs = cur_xlim[1] * xrate + cur_xlim[0] * (1 - xrate)
    yabs = cur_ylim[0] * yrate + cur_ylim[1] * (1 - yrate)

    new_width = abs(cur_xlim[1] - cur_xlim[0]) / scale_factor
    new_height = abs(cur_ylim[1] - cur_ylim[0]) / scale_factor

    self.cur_lims = (
        (xabs - new_width * xrate, xabs + new_width * (1 - xrate)),
        (yabs - new_height * (1 - yrate), yabs + new_height * yrate)
    )

apply_plotting_style

apply_plotting_style() -> None
Source code
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def apply_plotting_style() -> None:
    plt.style.use({
        'axes.edgecolor': '#FFFFFF3D',
        'axes.facecolor': '#FFFFFF07',
        'axes.labelcolor': '#FFFFFFD9',
        'axes.prop_cycle': cycler('color', [
            '#FF6200', '#696969', '#525199', '#60A6DA', '#D0D93C', '#A8A8A9', '#FF0000', '#349651', '#AB0066'
        ]),
        'figure.facecolor': '#19232D',
        'legend.edgecolor': '#FFFFFFD9',
        'legend.facecolor': 'inherit',
        'legend.framealpha': 0.12,
        'markers.fillstyle': 'full',
        'savefig.facecolor': '#19232D',
        'text.color': 'white',
        'xtick.color': '#FFFFFFD9',
        'ytick.color': '#FFFFFFD9'
    })