Skip to content
Snippets Groups Projects
Commit 071cf913 authored by s224362's avatar s224362
Browse files

Documenting code

parents 2dc8ba0e b9b92769
No related branches found
No related tags found
No related merge requests found
...@@ -2,22 +2,27 @@ from PyQt5.QtWidgets import ( ...@@ -2,22 +2,27 @@ from PyQt5.QtWidgets import (
QPushButton, QVBoxLayout, QWidget, QPushButton, QVBoxLayout, QWidget,
QSlider, QLabel, QGridLayout, QSizePolicy QSlider, QLabel, QGridLayout, QSizePolicy
) )
from PyQt5.QtGui import QPixmap, QImage from PyQt5.QtGui import QPixmap, QImage, QShowEvent
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
import numpy as np import numpy as np
from mainWindow import MainWindow
from typing import Optional
class AdvancedSettingsWidget(QWidget): class AdvancedSettingsWidget(QWidget):
""" """
Shows toggle rainbow, circle editor, line smoothing slider, contrast slider, Shows toggle rainbow, circle editor, line smoothing slider, contrast slider,
plus two image previews (contrasted-blurred and cost). plus two image previews (contrasted-blurred and cost).
The images should maintain aspect ratio upon resize. The images maintain aspect ratio upon resize.
"""
def __init__(self, main_window: MainWindow, parent: Optional[QWidget] = None):
"""
Constructor.
""" """
def __init__(self, main_window, parent=None):
super().__init__(parent) super().__init__(parent)
self._main_window = main_window self._main_window = main_window
self._last_cb_pix = None # store QPixmap for contrasted-blurred self._last_cb_pix = None # store QPixmap for contrasted-blurred image
self._last_cost_pix = None # store QPixmap for cost self._last_cost_pix = None # store QPixmap for cost image
main_layout = QVBoxLayout() main_layout = QVBoxLayout()
self.setLayout(main_layout) self.setLayout(main_layout)
...@@ -25,17 +30,17 @@ class AdvancedSettingsWidget(QWidget): ...@@ -25,17 +30,17 @@ class AdvancedSettingsWidget(QWidget):
# A small grid for controls # A small grid for controls
controls_layout = QGridLayout() controls_layout = QGridLayout()
# 1) Rainbow toggle # Rainbow toggle
self.btn_toggle_rainbow = QPushButton("Toggle Rainbow") self.btn_toggle_rainbow = QPushButton("Toggle Rainbow")
self.btn_toggle_rainbow.clicked.connect(self._on_toggle_rainbow) self.btn_toggle_rainbow.clicked.connect(self._on_toggle_rainbow)
controls_layout.addWidget(self.btn_toggle_rainbow, 0, 0) controls_layout.addWidget(self.btn_toggle_rainbow, 0, 0)
# 2) Circle editor # Disk size calibration (Circle editor)
self.btn_circle_editor = QPushButton("Calibrate Kernel Size") self.btn_circle_editor = QPushButton("Calibrate Kernel Size")
self.btn_circle_editor.clicked.connect(self._main_window.open_circle_editor) self.btn_circle_editor.clicked.connect(self._main_window.open_circle_editor)
controls_layout.addWidget(self.btn_circle_editor, 0, 1) controls_layout.addWidget(self.btn_circle_editor, 0, 1)
# 3) Line smoothing slider + label # Line smoothing slider + label
self._lab_smoothing = QLabel("Line smoothing (3)") self._lab_smoothing = QLabel("Line smoothing (3)")
controls_layout.addWidget(self._lab_smoothing, 1, 0) controls_layout.addWidget(self._lab_smoothing, 1, 0)
self.line_smoothing_slider = QSlider(Qt.Horizontal) self.line_smoothing_slider = QSlider(Qt.Horizontal)
...@@ -44,7 +49,7 @@ class AdvancedSettingsWidget(QWidget): ...@@ -44,7 +49,7 @@ class AdvancedSettingsWidget(QWidget):
self.line_smoothing_slider.valueChanged.connect(self._on_line_smoothing_slider) self.line_smoothing_slider.valueChanged.connect(self._on_line_smoothing_slider)
controls_layout.addWidget(self.line_smoothing_slider, 1, 1) controls_layout.addWidget(self.line_smoothing_slider, 1, 1)
# 4) Contrast slider + label # Contrast slider + label
self._lab_contrast = QLabel("Contrast (0.01)") self._lab_contrast = QLabel("Contrast (0.01)")
controls_layout.addWidget(self._lab_contrast, 2, 0) controls_layout.addWidget(self._lab_contrast, 2, 0)
self.contrast_slider = QSlider(Qt.Horizontal) self.contrast_slider = QSlider(Qt.Horizontal)
...@@ -56,14 +61,12 @@ class AdvancedSettingsWidget(QWidget): ...@@ -56,14 +61,12 @@ class AdvancedSettingsWidget(QWidget):
main_layout.addLayout(controls_layout) main_layout.addLayout(controls_layout)
# We'll set a minimum width so that the main window expands
# rather than overlapping the image
self.setMinimumWidth(350) self.setMinimumWidth(350)
# Now a vertical layout for the two images, each with a label above it # A vertical layout for the two images, each with a label above it
images_layout = QVBoxLayout() images_layout = QVBoxLayout()
# 1) Contrasted-blurred label + image # Contrasted-blurred label + image
self.label_cb_title = QLabel("Contrasted Blurred Image") self.label_cb_title = QLabel("Contrasted Blurred Image")
self.label_cb_title.setAlignment(Qt.AlignCenter) self.label_cb_title.setAlignment(Qt.AlignCenter)
images_layout.addWidget(self.label_cb_title) images_layout.addWidget(self.label_cb_title)
...@@ -73,7 +76,7 @@ class AdvancedSettingsWidget(QWidget): ...@@ -73,7 +76,7 @@ class AdvancedSettingsWidget(QWidget):
self.label_contrasted_blurred.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.label_contrasted_blurred.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
images_layout.addWidget(self.label_contrasted_blurred) images_layout.addWidget(self.label_contrasted_blurred)
# 2) Cost image label + image # Cost image label + image
self.label_cost_title = QLabel("Current COST IMAGE") self.label_cost_title = QLabel("Current COST IMAGE")
self.label_cost_title.setAlignment(Qt.AlignCenter) self.label_cost_title.setAlignment(Qt.AlignCenter)
images_layout.addWidget(self.label_cost_title) images_layout.addWidget(self.label_cost_title)
...@@ -85,21 +88,24 @@ class AdvancedSettingsWidget(QWidget): ...@@ -85,21 +88,24 @@ class AdvancedSettingsWidget(QWidget):
main_layout.addLayout(images_layout) main_layout.addLayout(images_layout)
def showEvent(self, event): def showEvent(self, event: QShowEvent):
""" When shown, ask parent to resize to accommodate. """ """ When shown, ask parent to resize to accommodate. """
super().showEvent(event) super().showEvent(event)
if self.parentWidget(): if self.parentWidget():
self.parentWidget().adjustSize() self.parentWidget().adjustSize()
def resizeEvent(self, event): def resizeEvent(self, event: QShowEvent):
""" """
Keep the images at correct aspect ratio by re-scaling Keep the images at correct aspect ratio by re-scaling
our stored pixmaps to the new label sizes. stored pixmaps to the new label sizes.
""" """
super().resizeEvent(event) super().resizeEvent(event)
self._update_labels() self._update_labels()
def _update_labels(self): def _update_labels(self):
"""
Re-scale stored pixmaps to the new label sizes.
"""
if self._last_cb_pix is not None: if self._last_cb_pix is not None:
scaled_cb = self._last_cb_pix.scaled( scaled_cb = self._last_cb_pix.scaled(
self.label_contrasted_blurred.size(), self.label_contrasted_blurred.size(),
...@@ -117,21 +123,29 @@ class AdvancedSettingsWidget(QWidget): ...@@ -117,21 +123,29 @@ class AdvancedSettingsWidget(QWidget):
self.label_cost_image.setPixmap(scaled_cost) self.label_cost_image.setPixmap(scaled_cost)
def _on_toggle_rainbow(self): def _on_toggle_rainbow(self):
"""
Called when the rainbow toggle button is clicked.
"""
self._main_window.toggle_rainbow() self._main_window.toggle_rainbow()
def _on_line_smoothing_slider(self, value): def _on_line_smoothing_slider(self, value: int):
"""
Called when the line smoothing slider is moved.
"""
self._lab_smoothing.setText(f"Line smoothing ({value})") self._lab_smoothing.setText(f"Line smoothing ({value})")
self._main_window.image_view.set_savgol_window_length(value) self._main_window.image_view.set_savgol_window_length(value)
def _on_contrast_slider(self, value): def _on_contrast_slider(self, value: int):
"""
Called when the contrast slider is moved.
"""
clip_limit = value / 100.0 clip_limit = value / 100.0
self._lab_contrast.setText(f"Contrast ({clip_limit:.2f})") self._lab_contrast.setText(f"Contrast ({clip_limit:.2f})")
self._main_window.update_contrast(clip_limit) self._main_window.update_contrast(clip_limit)
def update_displays(self, contrasted_img_np, cost_img_np): def update_displays(self, contrasted_img_np: np.ndarray, cost_img_np: np.ndarray):
""" """
Called by main_window to refresh the two images in the advanced panel. Update the contrasted-blurred and cost images.
We'll store them as QPixmaps, then do the re-scale in _update_labels().
""" """
cb_pix = self._np_array_to_qpixmap(contrasted_img_np) cb_pix = self._np_array_to_qpixmap(contrasted_img_np)
cost_pix = self._np_array_to_qpixmap(cost_img_np, normalize=True) cost_pix = self._np_array_to_qpixmap(cost_img_np, normalize=True)
...@@ -140,7 +154,10 @@ class AdvancedSettingsWidget(QWidget): ...@@ -140,7 +154,10 @@ class AdvancedSettingsWidget(QWidget):
self._last_cost_pix = cost_pix self._last_cost_pix = cost_pix
self._update_labels() self._update_labels()
def _np_array_to_qpixmap(self, arr, normalize=False): def _np_array_to_qpixmap(self, arr: np.ndarray, normalize: bool = False) -> QPixmap:
"""
Convert a numpy array to a QPixmap.
"""
if arr is None: if arr is None:
return None return None
arr_ = arr.copy() arr_ = arr.copy()
......
from PyQt5.QtWidgets import QGraphicsView from PyQt5.QtWidgets import QGraphicsView, QWidget
from panZoomGraphicsView import PanZoomGraphicsView from panZoomGraphicsView import PanZoomGraphicsView
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QMouseEvent, QWheelEvent
from draggableCircleItem import DraggableCircleItem from draggableCircleItem import DraggableCircleItem
from circleEditorWidget import CircleEditorWidget
from typing import Optional
# A specialized PanZoomGraphicsView for the circle editor # A specialized PanZoomGraphicsView for the circle editor (disk size calibration)
class CircleEditorGraphicsView(PanZoomGraphicsView): class CircleEditorGraphicsView(PanZoomGraphicsView):
def __init__(self, circle_editor_widget, parent=None): def __init__(self, circle_editor_widget: CircleEditorWidget, parent: Optional[QWidget] = None):
"""
Constructor.
"""
super().__init__(parent) super().__init__(parent)
self._circle_editor_widget = circle_editor_widget self._circle_editor_widget = circle_editor_widget
def mousePressEvent(self, event): def mousePressEvent(self, event: QMouseEvent):
"""
If the user clicks on the circle, we let the circle item handle the event.
"""
if event.button() == Qt.LeftButton: if event.button() == Qt.LeftButton:
# Check if user clicked on the circle item # Check if user clicked on the circle item
clicked_item = self.itemAt(event.pos()) clicked_item = self.itemAt(event.pos())
...@@ -24,10 +33,9 @@ class CircleEditorGraphicsView(PanZoomGraphicsView): ...@@ -24,10 +33,9 @@ class CircleEditorGraphicsView(PanZoomGraphicsView):
return QGraphicsView.mousePressEvent(self, event) return QGraphicsView.mousePressEvent(self, event)
super().mousePressEvent(event) super().mousePressEvent(event)
def wheelEvent(self, event): def wheelEvent(self, event: QWheelEvent):
""" """
If the mouse is hovering over the circle, we adjust the circle's radius If the user scrolls the mouse wheel over the circle, we change the circle
instead of zooming the image.
""" """
pos_in_widget = event.pos() pos_in_widget = event.pos()
item_under = self.itemAt(pos_in_widget) item_under = self.itemAt(pos_in_widget)
......
...@@ -2,13 +2,20 @@ from PyQt5.QtWidgets import ( ...@@ -2,13 +2,20 @@ from PyQt5.QtWidgets import (
QGraphicsScene, QGraphicsPixmapItem, QPushButton, QGraphicsScene, QGraphicsPixmapItem, QPushButton,
QHBoxLayout, QVBoxLayout, QWidget, QSlider, QLabel QHBoxLayout, QVBoxLayout, QWidget, QSlider, QLabel
) )
from PyQt5.QtGui import QFont from PyQt5.QtGui import QFont, QPixmap
from PyQt5.QtCore import Qt, QRectF, QSize from PyQt5.QtCore import Qt, QRectF, QSize
from circleEditorGraphicsView import CircleEditorGraphicsView from circleEditorGraphicsView import CircleEditorGraphicsView
from draggableCircleItem import DraggableCircleItem from draggableCircleItem import DraggableCircleItem
from typing import Optional, Callable
class CircleEditorWidget(QWidget): class CircleEditorWidget(QWidget):
def __init__(self, pixmap, init_radius=20, done_callback=None, parent=None): """
A widget for the user to calibrate the disk size (kernel size) for the ridge detection.
"""
def __init__(self, pixmap: QPixmap, init_radius: int = 20, done_callback: Optional[Callable[[], None]] = None, parent: Optional[QWidget] = None):
"""
Constructor.
"""
super().__init__(parent) super().__init__(parent)
self._pixmap = pixmap self._pixmap = pixmap
self._done_callback = done_callback self._done_callback = done_callback
...@@ -17,9 +24,7 @@ class CircleEditorWidget(QWidget): ...@@ -17,9 +24,7 @@ class CircleEditorWidget(QWidget):
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
self.setLayout(layout) self.setLayout(layout)
# # Add centered label above image
# 1) ADD A CENTERED LABEL ABOVE THE IMAGE, WITH BIGGER FONT
#
label_instructions = QLabel("Scale the dot to be of the size of your ridge") label_instructions = QLabel("Scale the dot to be of the size of your ridge")
label_instructions.setAlignment(Qt.AlignCenter) label_instructions.setAlignment(Qt.AlignCenter)
big_font = QFont("Arial", 20) big_font = QFont("Arial", 20)
...@@ -27,15 +32,12 @@ class CircleEditorWidget(QWidget): ...@@ -27,15 +32,12 @@ class CircleEditorWidget(QWidget):
label_instructions.setFont(big_font) label_instructions.setFont(big_font)
layout.addWidget(label_instructions) layout.addWidget(label_instructions)
# # Show the image
# 2) THE SPECIALIZED GRAPHICS VIEW THAT SHOWS THE IMAGE
#
self._graphics_view = CircleEditorGraphicsView(circle_editor_widget=self) self._graphics_view = CircleEditorGraphicsView(circle_editor_widget=self)
self._scene = QGraphicsScene(self) self._scene = QGraphicsScene(self)
self._graphics_view.setScene(self._scene) self._graphics_view.setScene(self._scene)
layout.addWidget(self._graphics_view) layout.addWidget(self._graphics_view)
# Show the image
self._image_item = QGraphicsPixmapItem(self._pixmap) self._image_item = QGraphicsPixmapItem(self._pixmap)
self._scene.addItem(self._image_item) self._scene.addItem(self._image_item)
...@@ -49,9 +51,7 @@ class CircleEditorWidget(QWidget): ...@@ -49,9 +51,7 @@ class CircleEditorWidget(QWidget):
self._graphics_view.setSceneRect(QRectF(self._pixmap.rect())) self._graphics_view.setSceneRect(QRectF(self._pixmap.rect()))
self._graphics_view.fitInView(self._image_item, Qt.KeepAspectRatio) self._graphics_view.fitInView(self._image_item, Qt.KeepAspectRatio)
# ### Controls below
# 3) CONTROLS BELOW
#
bottom_layout = QHBoxLayout() bottom_layout = QHBoxLayout()
layout.addLayout(bottom_layout) layout.addLayout(bottom_layout)
...@@ -64,7 +64,7 @@ class CircleEditorWidget(QWidget): ...@@ -64,7 +64,7 @@ class CircleEditorWidget(QWidget):
self._slider.setValue(self._init_radius) self._slider.setValue(self._init_radius)
bottom_layout.addWidget(self._slider) bottom_layout.addWidget(self._slider)
# done button # Done button
self._btn_done = QPushButton("Done") self._btn_done = QPushButton("Done")
bottom_layout.addWidget(self._btn_done) bottom_layout.addWidget(self._btn_done)
...@@ -72,16 +72,25 @@ class CircleEditorWidget(QWidget): ...@@ -72,16 +72,25 @@ class CircleEditorWidget(QWidget):
self._slider.valueChanged.connect(self._on_slider_changed) self._slider.valueChanged.connect(self._on_slider_changed)
self._btn_done.clicked.connect(self._on_done_clicked) self._btn_done.clicked.connect(self._on_done_clicked)
def _on_slider_changed(self, value): def _on_slider_changed(self, value: int):
"""
Handle slider value changes.
"""
self._circle_item.set_radius(value) self._circle_item.set_radius(value)
self._lbl_size.setText(f"size ({value})") self._lbl_size.setText(f"size ({value})")
def _on_done_clicked(self): def _on_done_clicked(self):
"""
Handle the user clicking the "Done" button.
"""
final_radius = self._circle_item.radius() final_radius = self._circle_item.radius()
if self._done_callback is not None: if self._done_callback is not None:
self._done_callback(final_radius) self._done_callback(final_radius)
def update_slider_value(self, new_radius): def update_slider_value(self, new_radius: int):
"""
Update the slider value.
"""
self._slider.blockSignals(True) self._slider.blockSignals(True)
self._slider.setValue(new_radius) self._slider.setValue(new_radius)
self._slider.blockSignals(False) self._slider.blockSignals(False)
......
import numpy as np import numpy as np
from typing import Optional
def circle_edge_kernel(k_size=5, radius=None): def circle_edge_kernel(k_size: int = 5, radius: Optional[int] = None) -> np.ndarray:
""" """
Create a k_size x k_size array whose values increase linearly Create a k_size x k_size array whose values increase linearly
from 0 at the center to 1 at the circle boundary (radius). from 0 at the center to 1 at the circle boundary (radius).
Parameters Args:
---------- k_size: The size (width and height) of the kernel array.
k_size : int radius: The circle's radius. By default, set to (k_size-1)/2.
The size (width and height) of the kernel array.
radius : float, optional
The circle's radius. By default, set to (k_size-1)/2.
Returns Returns:
------- kernel: The circle-edge-weighted kernel.
kernel : 2D numpy array of shape (k_size, k_size)
The circle-edge-weighted kernel.
""" """
if radius is None: if radius is None:
# By default, let the radius be half the kernel size # By default, let the radius be half the kernel size
......
import numpy as np import numpy as np
def compute_disk_size(user_radius, upscale_factor=1.2): def compute_disk_size(user_radius: int, upscale_factor: float = 1.2) -> int:
"""
Compute the size of the disk to be used in the cost image computation.
Args:
user_radius: The radius in pixels.
upscale_factor: The factor by which the disk size will be upscaled.
Returns:
The size of the disk.
"""
return int(np.ceil(upscale_factor * 2 * user_radius + 1) // 2 * 2 + 1) return int(np.ceil(upscale_factor * 2 * user_radius + 1) // 2 * 2 + 1)
\ No newline at end of file
import cv2 import cv2
import numpy as np
from typing import Tuple
# Currently not implemented # Currently not implemented
def downscale(img, points, scale_percent): def downscale(img: np.ndarray, points: Tuple[Tuple[int, int], Tuple[int, int]], scale_percent: int) -> Tuple[np.ndarray, Tuple[Tuple[int, int], Tuple[int, int]]]:
""" """
Downsample `img` to `scale_percent` size and scale the given points accordingly. Downscale an image and its corresponding points.
Returns (downsampled_img, (scaled_seed, scaled_target)).
Args:
img: The image.
points: The points to downscale.
scale_percent: The percentage to downscale to. E.g. scale_percent = 60 results in a new image 60% of the original image's size.
Returns:
The downsampled image and the downsampled points.
""" """
if scale_percent == 100: if scale_percent == 100:
return img, (tuple(points[0]), tuple(points[1])) return img, (tuple(points[0]), tuple(points[1]))
......
from PyQt5.QtWidgets import QGraphicsEllipseItem from PyQt5.QtWidgets import QGraphicsEllipseItem, QGraphicsItem
from PyQt5.QtGui import QPen, QBrush from PyQt5.QtGui import QPen, QBrush, QColor
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from typing import Optional
class DraggableCircleItem(QGraphicsEllipseItem): class DraggableCircleItem(QGraphicsEllipseItem):
def __init__(self, x, y, radius=20, color=Qt.red, parent=None): """
A QGraphicsEllipseItem that can be dragged around.
"""
def __init__(self, x: float, y: float, radius: float = 20, color: QColor = Qt.red, parent: Optional[QGraphicsItem] = None):
"""
Constructor.
"""
super().__init__(0, 0, 2*radius, 2*radius, parent) super().__init__(0, 0, 2*radius, 2*radius, parent)
self._r = radius self._r = radius
...@@ -20,7 +27,10 @@ class DraggableCircleItem(QGraphicsEllipseItem): ...@@ -20,7 +27,10 @@ class DraggableCircleItem(QGraphicsEllipseItem):
# Position so that (x, y) is the center # Position so that (x, y) is the center
self.setPos(x - radius, y - radius) self.setPos(x - radius, y - radius)
def set_radius(self, r): def set_radius(self, r: float):
"""
Set the radius of the circle
"""
old_center = self.sceneBoundingRect().center() old_center = self.sceneBoundingRect().center()
self._r = r self._r = r
self.setRect(0, 0, 2*r, 2*r) self.setRect(0, 0, 2*r, 2*r)
...@@ -30,4 +40,7 @@ class DraggableCircleItem(QGraphicsEllipseItem): ...@@ -30,4 +40,7 @@ class DraggableCircleItem(QGraphicsEllipseItem):
self.moveBy(diff_x, diff_y) self.moveBy(diff_x, diff_y)
def radius(self): def radius(self):
"""
Get the radius of the circle
"""
return self._r return self._r
\ No newline at end of file
...@@ -2,7 +2,7 @@ import math ...@@ -2,7 +2,7 @@ import math
from scipy.signal import savgol_filter from scipy.signal import savgol_filter
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsPixmapItem from PyQt5.QtWidgets import QGraphicsScene, QGraphicsPixmapItem
from PyQt5.QtGui import QPixmap, QColor from PyQt5.QtGui import QPixmap, QColor
from PyQt5.QtCore import Qt, QRectF from PyQt5.QtCore import Qt, QRectF, QPoint
import numpy as np import numpy as np
from panZoomGraphicsView import PanZoomGraphicsView from panZoomGraphicsView import PanZoomGraphicsView
from labeledPointItem import LabeledPointItem from labeledPointItem import LabeledPointItem
...@@ -16,30 +16,6 @@ class ImageGraphicsView(PanZoomGraphicsView): ...@@ -16,30 +16,6 @@ class ImageGraphicsView(PanZoomGraphicsView):
This class extends PanZoomGraphicsView to provide additional functionality This class extends PanZoomGraphicsView to provide additional functionality
for loading images, adding labeled anchor points, and computing paths for loading images, adding labeled anchor points, and computing paths
between points based on a cost image. between points based on a cost image.
Attributes:
scene (QGraphicsScene): The graphics scene for displaying items.
image_item (QGraphicsPixmapItem): The item for displaying the loaded image.
anchor_points (list): List of tuples representing anchor points (x, y).
point_items (list): List of LabeledPointItem objects for anchor points.
full_path_points (list): List of QGraphicsEllipseItems representing the path.
_full_path_xy (list): List of coordinates for the entire path.
dot_radius (int): Radius of the anchor points.
path_radius (int): Radius of the path points.
radius_cost_image (int): Radius for lowering cost in the cost image.
_img_w (int): Width of the loaded image.
_img_h (int): Height of the loaded image.
_mouse_pressed (bool): Indicates if the mouse is pressed.
_press_view_pos (QPoint): Position of the mouse press event.
_drag_threshold (int): Threshold for detecting drag events.
_was_dragging (bool): Indicates if a drag event occurred.
_dragging_idx (int): Index of the point being dragged.
_drag_offset (tuple): Offset for dragging points.
_drag_counter (int): Counter for drag events.
cost_image_original (np.ndarray): Original cost image.
cost_image (np.ndarray): Current cost image.
_rainbow_enabled (bool): Indicates if rainbow coloring is enabled.
_savgol_window_length (int): Window length for Savitzky-Golay smoothing.
""" """
def __init__(self, parent=None): def __init__(self, parent=None):
...@@ -367,6 +343,7 @@ class ImageGraphicsView(PanZoomGraphicsView): ...@@ -367,6 +343,7 @@ class ImageGraphicsView(PanZoomGraphicsView):
# MOUSE EVENTS # MOUSE EVENTS
# -------------------------------------------------------------------- # --------------------------------------------------------------------
def mouse_press_event(self, event): def mouse_press_event(self, event):
"""Handle mouse press events for dragging a point or adding a point."""
if event.button() == Qt.LeftButton: if event.button() == Qt.LeftButton:
self._mouse_pressed = True self._mouse_pressed = True
self._was_dragging = False self._was_dragging = False
...@@ -416,6 +393,7 @@ class ImageGraphicsView(PanZoomGraphicsView): ...@@ -416,6 +393,7 @@ class ImageGraphicsView(PanZoomGraphicsView):
super().mouse_move_event(event) super().mouse_move_event(event)
def mouse_release_event(self, event): def mouse_release_event(self, event):
"""Handle mouse release events for dragging a point or adding a point."""
super().mouse_release_event(event) super().mouse_release_event(event)
if event.button() == Qt.LeftButton and self._mouse_pressed: if event.button() == Qt.LeftButton and self._mouse_pressed:
self._mouse_pressed = False self._mouse_pressed = False
...@@ -439,7 +417,8 @@ class ImageGraphicsView(PanZoomGraphicsView): ...@@ -439,7 +417,8 @@ class ImageGraphicsView(PanZoomGraphicsView):
self._was_dragging = False self._was_dragging = False
def _remove_point_by_click(self, view_pos: "QPoint"): def _remove_point_by_click(self, view_pos: QPoint):
"""Remove a point by clicking on it."""
idx = self._find_item_near(view_pos, threshold=10) idx = self._find_item_near(view_pos, threshold=10)
if idx is None: if idx is None:
return return
...@@ -454,7 +433,8 @@ class ImageGraphicsView(PanZoomGraphicsView): ...@@ -454,7 +433,8 @@ class ImageGraphicsView(PanZoomGraphicsView):
self._apply_all_guide_points_to_cost() self._apply_all_guide_points_to_cost()
self._rebuild_full_path() self._rebuild_full_path()
def _find_item_near(self, view_pos: "QPoint", threshold=10): def _find_item_near(self, view_pos: QPoint, threshold=10):
"""Find the index of an item near a given position."""
scene_pos = self.mapToScene(view_pos) scene_pos = self.mapToScene(view_pos)
x_click, y_click = scene_pos.x(), scene_pos.y() x_click, y_click = scene_pos.x(), scene_pos.y()
...@@ -476,7 +456,7 @@ class ImageGraphicsView(PanZoomGraphicsView): ...@@ -476,7 +456,7 @@ class ImageGraphicsView(PanZoomGraphicsView):
return max(mn, min(val, mx)) return max(mn, min(val, mx))
def _clear_all_points(self): def _clear_all_points(self):
"""Clear all anchor points and guide points."""
for it in self.point_items: for it in self.point_items:
self.scene.removeItem(it) self.scene.removeItem(it)
self.point_items.clear() self.point_items.clear()
......
...@@ -11,16 +11,6 @@ class LabeledPointItem(QGraphicsEllipseItem): ...@@ -11,16 +11,6 @@ class LabeledPointItem(QGraphicsEllipseItem):
This class creates a circular point. This class creates a circular point.
The point can be customized with different colors, sizes, and labels, and can The point can be customized with different colors, sizes, and labels, and can
be marked as removable. be marked as removable.
Attributes:
x (float): The x-coordinate of the point.
y (float): The y-coordinate of the point.
label (str): The label text for the point.
radius (int): The radius of the point.
color (QColor): The color of the point.
removable (bool): Indicates if the point can be removed.
z_value (float): The z-value of the point for stacking order.
parent (QGraphicsItem): The parent QGraphicsItem, if any.
""" """
def __init__(self, x: float, y: float, label: str ="", radius:int =4, def __init__(self, x: float, y: float, label: str ="", radius:int =4,
......
...@@ -4,16 +4,6 @@ from PyQt5.QtCore import Qt ...@@ -4,16 +4,6 @@ from PyQt5.QtCore import Qt
class PanZoomGraphicsView(QGraphicsView): class PanZoomGraphicsView(QGraphicsView):
""" """
A QGraphicsView subclass that supports panning and zooming with the mouse. A QGraphicsView subclass that supports panning and zooming with the mouse.
Attributes:
_panning (bool): Indicates whether panning is currently active.
_pan_start (QPoint): The starting point of the panning action.
Methods:
wheel_event(event): Zooms in or out based on the mouse wheel movement.
mouse_press_event(event): Starts panning if the left mouse button is pressed.
mouse_move_event(event): Translates the scene if panning is active.
mouse_release_event(event): Ends panning when the left mouse button is released.
""" """
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment