Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dev = [
"pre-commit>=3.6",
"ty>=0.0.30",
"pandas-stubs>=3.0.0.260204",
"pyside6-stubs>=6.7.3.0",
]
build = [
"pyinstaller>=6.15.0",
Expand Down Expand Up @@ -124,5 +125,3 @@ strict_optional = true
module = "PySide6.*"
ignore_missing_imports = true

[tool.ty.rules]
unresolved-attribute = "ignore"
6 changes: 4 additions & 2 deletions src/dbs_annotator/logging_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ def qt_handler(mode: QtMsgType, context: QMessageLogContext, message: str) -> No
else:
level = logging.CRITICAL
suffix = ""
if context.file:
suffix = f" ({context.file}:{context.line})"
ctx_file = getattr(context, "file", None)
ctx_line = getattr(context, "line", None)
if ctx_file:
suffix = f" ({ctx_file}:{ctx_line})"
logging.getLogger("qt").log(level, "%s%s", message, suffix)

qInstallMessageHandler(qt_handler)
Expand Down
68 changes: 40 additions & 28 deletions src/dbs_annotator/models/electrode_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ def __init__(self, parent=None):
self.setContentsMargins(2, 2, 2, 2)
self.setAutoFillBackground(False)
self.setMouseTracking(True)
self.setCursor(Qt.PointingHandCursor)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self.export_mode = False
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)

def set_export_mode(self, enabled: bool) -> None:
"""Toggle export mode (tighter padding, larger scale for PNG output)."""
Expand Down Expand Up @@ -198,7 +198,7 @@ def set_ring_state(self, ring_idx, state):
@typing.override
def mousePressEvent(self, event):
"""Handle clicks on contacts, rings and case"""
if event.button() == Qt.LeftButton:
if event.button() == Qt.MouseButton.LeftButton:
# Check if a contact was clicked
contact_id = self.get_contact_at_pos(event.pos())
if contact_id:
Expand Down Expand Up @@ -277,8 +277,8 @@ def paintEvent(self, event):
return

painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.setRenderHint(QPainter.SmoothPixmapTransform)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)

# Background
palette = self.palette()
Expand Down Expand Up @@ -330,17 +330,21 @@ def paintEvent(self, event):
case_x + 2, case_y + 1, case_width * 0.4, case_height * 0.3
)
painter.setBrush(QColor(255, 255, 255, 40))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(highlight_rect, 3, 3)

# Case label - smaller font in export mode
painter.setPen(Qt.white if self.case_state != ContactState.OFF else Qt.black)
painter.setPen(
Qt.GlobalColor.white
if self.case_state != ContactState.OFF
else Qt.GlobalColor.black
)
case_font_size = (
max(7, int(scale * 0.35)) if self.export_mode else max(8, int(scale * 0.4))
)
font = QFont("Arial", case_font_size, QFont.Bold)
font = QFont("Arial", case_font_size, QFont.Weight.Bold)
painter.setFont(font)
painter.drawText(self.case_rect, Qt.AlignCenter, "CASE")
painter.drawText(self.case_rect, Qt.AlignmentFlag.AlignCenter, "CASE")

# Draw electrode body (lead)
lead_width = self.model.lead_diameter * scale * 1.8
Expand All @@ -367,14 +371,14 @@ def paintEvent(self, event):
center_x - lead_width / 2, start_y, center_x + lead_width / 2, start_y
)

base_color = palette.color(QPalette.Midlight)
base_color = palette.color(QPalette.ColorRole.Midlight)
lead_gradient.setColorAt(0, base_color.darker(120))
lead_gradient.setColorAt(0.3, base_color)
lead_gradient.setColorAt(0.7, base_color)
lead_gradient.setColorAt(1, base_color.darker(120))

painter.setBrush(QBrush(lead_gradient))
painter.setPen(QPen(palette.color(QPalette.Dark), 2))
painter.setPen(QPen(palette.color(QPalette.ColorRole.Dark), 2))

if getattr(self.model, "tip_contact", False):
# Boston Scientific: flat bottom so lead seamlessly meets the tip contact
Expand Down Expand Up @@ -417,7 +421,7 @@ def paintEvent(self, event):
shadow_left.setColorAt(0, QColor(0, 0, 0, 30))
shadow_left.setColorAt(1, QColor(0, 0, 0, 0))
painter.setBrush(QBrush(shadow_left))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRect(
int(center_x - lead_width / 2),
int(start_y),
Expand Down Expand Up @@ -485,7 +489,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
shadow_poly = QPolygonF(poly)
shadow_poly.translate(1, 2)
painter.setBrush(QColor(0, 0, 0, 30))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawPolygon(shadow_poly)

# Draw main segment
Expand Down Expand Up @@ -514,7 +518,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
]
)
painter.setBrush(QColor(255, 255, 255, 40))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawPolygon(highlight_poly)

self.contact_rects[contact_id] = bounds
Expand All @@ -528,15 +532,19 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
).united(path)

# Label - smaller font in export mode
painter.setPen(Qt.white if state != ContactState.OFF else Qt.black)
painter.setPen(
Qt.GlobalColor.white
if state != ContactState.OFF
else Qt.GlobalColor.black
)
font_size = (
max(6, int(scale * 0.3))
if self.export_mode
else max(7, int(scale * 0.4))
)
font = QFont("Arial", font_size, QFont.Bold)
font = QFont("Arial", font_size, QFont.Weight.Bold)
painter.setFont(font)
painter.drawText(bounds, Qt.AlignCenter, label)
painter.drawText(bounds, Qt.AlignmentFlag.AlignCenter, label)

# Segment 'a' (left) - extends left
contact_id_a = (contact_idx, 0)
Expand Down Expand Up @@ -673,7 +681,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
shadow_path = QPainterPath(tip_path)
shadow_path.translate(0, 2)
painter.setBrush(QColor(0, 0, 0, 20))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawPath(shadow_path)

painter.setBrush(QBrush(contact_gradient_tip))
Expand All @@ -688,7 +696,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
contact_height_px * 0.4,
)
painter.setBrush(QColor(255, 255, 255, 50))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(highlight_rect, 2, 2)

self.contact_rects[contact_id] = tip_bounds
Expand All @@ -702,7 +710,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
shadow_rect = QRectF(rect)
shadow_rect.translate(0, 2)
painter.setBrush(QColor(0, 0, 0, 20))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(shadow_rect, 3, 3)

painter.setBrush(QBrush(contact_gradient))
Expand All @@ -717,7 +725,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
rect.height() * 0.4,
)
painter.setBrush(QColor(255, 255, 255, 50))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(highlight_rect, 2, 2)

self.contact_rects[contact_id] = rect
Expand All @@ -730,13 +738,13 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
).united(path)

# Contact number on the left - smaller font in export mode
painter.setPen(palette.color(QPalette.Text))
painter.setPen(palette.color(QPalette.ColorRole.Text))
elabel_size = (
max(7, int(scale * 0.35))
if self.export_mode
else max(10, int(scale * 0.5))
)
font = QFont("Arial", elabel_size, QFont.Bold)
font = QFont("Arial", elabel_size, QFont.Weight.Bold)
painter.setFont(font)

label_extension = base_extension if is_directional_contact else 0
Expand All @@ -761,7 +769,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
int(current_y),
35,
int(contact_height_px),
Qt.AlignVCenter | Qt.AlignRight,
Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignRight,
contact_label,
)

Expand Down Expand Up @@ -802,7 +810,7 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
shadow_rect = QRectF(ring_cap_rect)
shadow_rect.translate(0, 1)
painter.setBrush(QColor(0, 0, 0, 25))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(shadow_rect, 3, 3)

# Draw ring cap
Expand All @@ -819,19 +827,23 @@ def draw_3d_segment(poly, state, is_hovered, contact_id, label):
ring_cap_rect.height() * 0.4,
)
painter.setBrush(QColor(255, 255, 255, 50))
painter.setPen(Qt.NoPen)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(highlight, 2, 2)

# Ring label - smaller font in export mode
painter.setPen(Qt.white if ring_state != ContactState.OFF else Qt.black)
painter.setPen(
Qt.GlobalColor.white
if ring_state != ContactState.OFF
else Qt.GlobalColor.black
)
ring_font_size = (
max(6, int(scale * 0.3))
if self.export_mode
else max(7, int(scale * 0.3))
)
font = QFont("Arial", ring_font_size)
painter.setFont(font)
painter.drawText(ring_cap_rect, Qt.AlignCenter, "Ring")
painter.drawText(ring_cap_rect, Qt.AlignmentFlag.AlignCenter, "Ring")

@typing.override
def resizeEvent(self, event):
Expand Down
29 changes: 19 additions & 10 deletions src/dbs_annotator/models/session_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ def write_clinical_scales(
stimulation: Stimulation parameters
notes: Additional notes for this entry
"""
if not self.tsv_writer:
writer = self.tsv_writer
tsv_file = self.tsv_file
if writer is None or tsv_file is None:
raise ValueError("TSV file not opened. Call open_file() first.")

tz = self._resolve_timezone()
Expand Down Expand Up @@ -227,7 +229,7 @@ def write_clinical_scales(
"notes": notes,
**stim_dict,
}
self.tsv_writer.writerow(row)
writer.writerow(row)
else:
# Write one row per scale
for scale in valid_scales:
Expand All @@ -245,9 +247,9 @@ def write_clinical_scales(
"notes": notes,
**stim_dict,
}
self.tsv_writer.writerow(row)
writer.writerow(row)

self.tsv_file.flush()
tsv_file.flush()
self.block_id += 1

def write_session_scales(
Expand All @@ -266,7 +268,9 @@ def write_session_scales(
stimulation: Stimulation parameters
notes: Additional notes for this entry
"""
if not self.tsv_writer:
writer = self.tsv_writer
tsv_file = self.tsv_file
if writer is None or tsv_file is None:
raise ValueError("TSV file not opened. Call open_file() first.")

tz = self._resolve_timezone()
Expand Down Expand Up @@ -295,7 +299,7 @@ def write_session_scales(
"notes": notes,
**stim_dict,
}
self.tsv_writer.writerow(row)
writer.writerow(row)
else:
# Write one row per scale
for scale in valid_scales:
Expand All @@ -313,9 +317,9 @@ def write_session_scales(
"notes": notes,
**stim_dict,
}
self.tsv_writer.writerow(row)
writer.writerow(row)

self.tsv_file.flush()
tsv_file.flush()
self.block_id += 1

def is_file_open(self) -> bool:
Expand Down Expand Up @@ -430,6 +434,11 @@ def write_simple_annotation(self, annotation: str) -> None:
if not self.is_file_open():
raise ValueError("No file is open. Call initialize_simple_file first.")

writer = self.tsv_writer
tsv_file = self.tsv_file
if writer is None or tsv_file is None:
raise ValueError("No file is open. Call initialize_simple_file first.")

# Get current time
from datetime import datetime

Expand All @@ -445,5 +454,5 @@ def write_simple_annotation(self, annotation: str) -> None:
"timezone": tz_str,
"annotation": annotation,
}
self.tsv_writer.writerow(row)
self.tsv_file.flush()
writer.writerow(row)
tsv_file.flush()
4 changes: 2 additions & 2 deletions src/dbs_annotator/ui/amplitude_split_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def _create_regular_row(self, lbl: str, index: int) -> None:
# Percentage edit
pct_edit = QLineEdit()
pct_edit.setMaximumWidth(55)
pct_edit.setAlignment(Qt.AlignRight)
pct_edit.setAlignment(Qt.AlignmentFlag.AlignRight)
pct_edit.setValidator(QDoubleValidator(0.0, 100.0, 1))
pct_edit.setText(f"{self._percentages.get(lbl, 0.0):g}")
row_layout.addWidget(pct_edit)
Expand Down Expand Up @@ -256,7 +256,7 @@ def _create_segment_rows_only(self, parent_lbl: str) -> None:
# Percentage edit (editable for segments)
pct_edit = QLineEdit()
pct_edit.setMaximumWidth(55)
pct_edit.setAlignment(Qt.AlignRight)
pct_edit.setAlignment(Qt.AlignmentFlag.AlignRight)
pct_edit.setValidator(QDoubleValidator(0.0, 100.0, 1))
pct_edit.setText(f"{segment_percentage:g}")
row_layout.addWidget(pct_edit)
Expand Down
Loading
Loading