Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add transparency slider for human annotation & english/spanish support for controls window #140

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,6 @@ venv.bak/

# mypy
.mypy_cache/

#tests
tsts.md
1 change: 1 addition & 0 deletions painter/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ matplotlib==3.5.2
pyqtgraph==0.12.4
qimage2ndarray==1.9.0
humanfriendly==10.0
PyInstaller
66 changes: 49 additions & 17 deletions painter/src/main/python/controls_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,10 @@ def __init__(self, parent=None):
self.setWindowTitle("Controls")
self.setMinimumSize(300, 200)

# controls_text = """
# Controls:
# - Left Click: Draw
# - Right Click: Erase
# - Scroll: Zoom
# - Arrow Keys: Move around
# - Ctrl + Z: Undo
# - Ctrl + Y: Redo
# """

controls_text = """
self.settings = QtCore.QSettings("rp", "painter")
self.language = self.settings.value("language", "English")

self.controls_text_en = """
Controls:
- Show/Hide Predicted Segmentation: S
- Show/Hide Human Annotations: A
Expand All @@ -56,14 +49,53 @@ def __init__(self, parent=None):
- Zoom: Scroll

- Pan: cmd + drag the on the image with right click

"""

layout = QtWidgets.QVBoxLayout()
label = QtWidgets.QLabel(controls_text)
layout.addWidget(label)
self.setLayout(layout)
self.controls_text_es = """
Controles:
- Mostrar/Ocultar Segmentación AI: S
- Mostrar/Ocultar Anotacion Humana: A
- Mostrar/Ocultar Imagen: I

- Rojo(Si): Q
- Verde(No): W
- Borrar: E

- Tamaño del Pincel: Shift + rueda de el mouse

- Deshacer: Z
- Rehacer: Shift + cmd + Z

- Acercar: Shift + '+'
- Alejar: -
- Zoom: rueda de el mouse

- mover imagen: cmd + arrastrar sobre la imagen con clic derecho
"""

self.layout = QtWidgets.QVBoxLayout()

self.language_selector = QtWidgets.QComboBox()
self.language_selector.addItem("English")
self.language_selector.addItem("Spanish")
self.language_selector.setCurrentText(self.language)
self.language_selector.currentTextChanged.connect(self.update_text)

self.layout.addWidget(self.language_selector)

self.label = QtWidgets.QLabel()
self.layout.addWidget(self.label)

self.setLayout(self.layout)
self.update_text(self.language)

def update_text(self, language):
if language == "Spanish":
self.label.setText(self.controls_text_es)
else:
self.label.setText(self.controls_text_en)
self.settings.setValue("language", language)

def closeEvent(self, event):
self.closed.emit()
super().closeEvent(event)
super().closeEvent(event)
55 changes: 50 additions & 5 deletions painter/src/main/python/graphics_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Facilitates use of zoom and pan.

Copyright (C) 2020 Abraham George Smith
Copyright (C) 2024 Felipe Galindo

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -18,10 +19,9 @@
"""

#pylint: disable=I1101,E0611,C0111
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtCore import Qt
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QPinchGesture
from PyQt5.QtCore import Qt, QEvent

class CustomGraphicsView(QtWidgets.QGraphicsView):
"""
Expand All @@ -31,10 +31,12 @@ class CustomGraphicsView(QtWidgets.QGraphicsView):
mouse_scroll_event = QtCore.pyqtSignal(QtGui.QWheelEvent)
zoom_change = QtCore.pyqtSignal()

def __init__(self):
def __init__(self, parent=None):
super().__init__()
self.zoom = 1
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self.grabGesture(Qt.PinchGesture)
self.panning_enabled = False

def update_zoom(self):
""" Transform the view based on current zoom value """
Expand Down Expand Up @@ -75,3 +77,46 @@ def fitin():
fitin()
QtCore.QTimer.singleShot(100, fitin)
self.zoom_change.emit()

def event(self, event):
if event.type() == QEvent.Gesture:
return self.gestureEvent(event)
return super().event(event)

def gestureEvent(self, event):
pinch = event.gesture(Qt.PinchGesture)
if pinch:
self.pinchTriggered(pinch)
return True

def pinchTriggered(self, gesture):
changeFlags = gesture.changeFlags()
if changeFlags & QPinchGesture.ScaleFactorChanged:
self.zoom *= gesture.scaleFactor()
self.update_zoom()
return True

def enable_panning(self, enable):
self.panning_enabled = enable
if enable:
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
else:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)

def mousePressEvent(self, event):
if self.panning_enabled:
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
super().mousePressEvent(event)

def mouseReleaseEvent(self, event):
if self.panning_enabled:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
super().mouseReleaseEvent(event)

def wheelEvent(self, event):
scroll_up = event.angleDelta().y() > 0
if scroll_up:
self.zoom *= 1.1
else:
self.zoom /= 1.1
self.update_zoom()
31 changes: 27 additions & 4 deletions painter/src/main/python/root_painter.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,19 @@ def update_window_title(self):
self.setWindowTitle(f"RootPainter {proj_dirname}"
f" {os.path.basename(self.image_path)}")

def update_annotation_transparency(self, value):
if value < 10:
self.annot_pixmap_holder.setPixmap(self.blank_pixmap)
self.annot_visible = False
else:
opacity = value / 100.0
self.set_annotation_opacity(opacity)
if not self.annot_visible:
self.scene.annot_pixmap_holder.setPixmap(self.scene.annot_pixmap)
self.annot_visible = True

self.vis_widget.annot_checkbox.setChecked(self.annot_visible)

def init_active_project_ui(self):
# container for both nav and graphics view.
container = QtWidgets.QWidget()
Expand Down Expand Up @@ -758,6 +771,8 @@ def init_active_project_ui(self):
self.vis_widget.im_checkbox.stateChanged.connect(self.im_checkbox_change)
bottom_bar_layout.addWidget(self.vis_widget)

self.vis_widget.transparency_slider.valueChanged.connect(self.update_annotation_transparency)

# bottom bar right
bottom_bar_r = QtWidgets.QWidget()
bottom_bar_r_layout = QtWidgets.QVBoxLayout()
Expand Down Expand Up @@ -804,8 +819,8 @@ def init_active_project_ui(self):
self.controls_checkbox = QtWidgets.QCheckBox("Controls (C)")
self.controls_checkbox.stateChanged.connect(self.show_controls)
info_container_right_layout.addWidget(self.controls_checkbox)

# Add keyboard shortcut to show controls
# Add keyboard shortcut to show controls
shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("C"), self)
shortcut.activated.connect(self.check_controls_checkbox)

Expand Down Expand Up @@ -1160,12 +1175,20 @@ def annot_checkbox_change(self, state):
checked = (state == QtCore.Qt.Checked)
if checked is not self.annot_visible:
self.show_hide_annot()
# Enable or disable panning based on the annotation checkbox state
self.graphics_view.enable_panning(not checked)

def im_checkbox_change(self, state):
checked = (state == QtCore.Qt.Checked)
if checked is not self.image_visible:
self.show_hide_image()

def set_annotation_opacity(self, opacity):
if hasattr(self, 'annot_pixmap_holder') and self.annot_pixmap_holder:
opacity_effect = QtWidgets.QGraphicsOpacityEffect()
opacity_effect.setOpacity(opacity)
self.annot_pixmap_holder.setGraphicsEffect(opacity_effect)

def show_hide_seg(self):
# show or hide the current segmentation.
if self.seg_visible:
Expand All @@ -1190,8 +1213,8 @@ def show_hide_image(self):
def show_hide_annot(self):
# show or hide the current annotations.
# Could be useful to help inspect the background image
if self.annot_visible:
self.annot_pixmap_holder.setPixmap(self.blank_pixmap)
if self.annot_visible:
self.annot_pixmap_holder.setPixmap(self.blank_pixmap) # annotation disabled, show blank
self.annot_visible = False
else:
self.scene.annot_pixmap_holder.setPixmap(self.scene.annot_pixmap)
Expand Down
16 changes: 16 additions & 0 deletions painter/src/main/python/visibility_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Show visibility status of segmentation, image and annotation.

Copyright (C) 2020 Abraham George Smith
Copyright (C) 2024 Felipe Galindo

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -18,6 +19,7 @@

# pylint: disable=E0611, C0111, C0111, R0903, I1101
from PyQt5 import QtWidgets
from PyQt5 import QtCore

class VisibilityWidget(QtWidgets.QWidget):

Expand All @@ -37,6 +39,20 @@ def initUI(self):
self.setLayout(container_layout)
container_layout.setContentsMargins(0, 0, 0, 0)

# add 'Transparency' label
transparency_label = QtWidgets.QLabel("Transparency:")
container_layout.addWidget(transparency_label)


# Add transparency slider
self.transparency_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.transparency_slider.setMinimum(0)
self.transparency_slider.setMaximum(100)
self.transparency_slider.setValue(50)
self.transparency_slider.setTickInterval(10)
self.transparency_slider.setTickPosition(QtWidgets.QSlider.TicksBelow)
container_layout.addWidget(self.transparency_slider)

seg_checkbox = QtWidgets.QCheckBox("Segmentation (S)")
container_layout.addWidget(seg_checkbox)

Expand Down
Loading