diff --git a/fixtracks/centralwidget.py b/fixtracks/centralwidget.py index 2858499..8b9a5e6 100644 --- a/fixtracks/centralwidget.py +++ b/fixtracks/centralwidget.py @@ -1,5 +1,6 @@ import logging from PyQt6.QtWidgets import QWidget, QStackedLayout +from PyQt6.QtCore import Qt from fixtracks.taskwidgets import FixTracks from fixtracks.detectionmerge import MergeDetections @@ -10,7 +11,9 @@ class CentralWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) layout = QStackedLayout() + layout.setAlignment(Qt.AlignmentFlag.AlignVCenter) self._tw = TasksWidget() + self._tw.setMaximumHeight(200) self._tw.clicked.connect(self._select_task) self._tasksindex = layout.addWidget(self._tw) self._mergewidget = MergeDetections(self) diff --git a/fixtracks/detectionmerge.py b/fixtracks/detectionmerge.py index 4cfd1a2..697f5c3 100644 --- a/fixtracks/detectionmerge.py +++ b/fixtracks/detectionmerge.py @@ -1,12 +1,51 @@ import logging -from PyQt6.QtWidgets import QWidget, QGridLayout, QVBoxLayout, QLabel, QPushButton, QComboBox, QSizePolicy, QSpinBox, QGraphicsView, QGraphicsScene -from PyQt6.QtCore import QThreadPool, QLineF, QPointF +from PyQt6.QtWidgets import QWidget, QGridLayout, QVBoxLayout, QLabel, QPushButton, QComboBox, QSizePolicy, QSpinBox, QGraphicsView, QGraphicsScene, QGraphicsLineItem +from PyQt6.QtCore import QThreadPool, QLineF, QPointF, QRectF, Qt from PyQt6.QtGui import QImage, QPixmap, QColor, QPen from fixtracks.util import ImageReader +class VideoPreview(QWidget): + def __init__(self, left=True, parent=None): + super().__init__(None) + self._image = None + self._line_pen = QPen(QColor.fromString("red"), 4) + self._view = QGraphicsView() + self._view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + self._scene = None + self._pixmap = None + self._left = left + layout = QVBoxLayout() + layout.addWidget(self._view) + self.setLayout(layout) + + def set_image(self, img): + self._scene = QGraphicsScene() + self._pixmap = self._scene.addPixmap(QPixmap.fromImage(img)) + self._view.setScene(self._scene) + self._view.fitInView(self._scene.sceneRect(), mode=Qt.AspectRatioMode.KeepAspectRatio) + image_rect = self._pixmap.boundingRect() + start_x = image_rect.right() if self._left else image_rect.left() + start_y = image_rect.top() + end_y = image_rect.bottom() + + self._scene.addLine(start_x, start_y, start_x, end_y, self._line_pen) + self._view.show() + + def set_lineposition(self, position): + image_rect = self._pixmap.boundingRect() + for i in self._scene.items(): + if isinstance(i, QGraphicsLineItem): + self._scene.removeItem(i) + + start_x = image_rect.left() + position + start_y = image_rect.top() + end_y = image_rect.bottom() + self._scene.addLine(start_x, start_y, start_x, end_y, self._line_pen) + + class MergeDetections(QWidget): def __init__(self, parent=None): super().__init__(parent) @@ -24,27 +63,24 @@ class MergeDetections(QWidget): self.left_videocombo.addItems(self._files) self.left_posspinner = QSpinBox() self.left_posspinner.valueChanged.connect(self.on_leftmergelinemove) - self.left_preview = QGraphicsView() - self.left_preview.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - self.left_line = None - + self.left_preview = VideoPreview() + self.right_datacombo = QComboBox() self.right_videocombo = QComboBox() self.right_datacombo.addItems(self._files) self.right_videocombo.addItems(self._files) self.right_posspinner = QSpinBox() - self.right_preview = QLabel() - self.right_preview.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - self.right_line = None + self.right_posspinner.valueChanged.connect(self.on_rightmergelinemove) + self.right_preview = VideoPreview(left=False) layout.addLayout(self.layout_controls()) splitter = QGridLayout() - # splitter.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) splitter.addWidget(self.left_preview, 0, 0) splitter.addWidget(self.right_preview, 0, 1) splitter.setColumnStretch(0, 1) splitter.setColumnStretch(1, 1) layout.addLayout(splitter) + self.setLayout(layout) def layout_controls(self): @@ -71,7 +107,6 @@ class MergeDetections(QWidget): grd.setColumnStretch(3, 50) grd.setColumnMinimumWidth(0, 50) grd.setColumnMinimumWidth(2, 50) - # grd.setRowStretch(4, 100) return grd @property @@ -103,8 +138,13 @@ class MergeDetections(QWidget): self.right_datacombo.setCurrentIndex(0) self.right_videocombo.currentIndexChanged.connect(self.on_rightvideoSelection) self.left_videocombo.currentIndexChanged.connect(self.on_leftvideoSelection) - # self._files = file_list + def _toImage(self, frame): + height, width, _ = frame.shape + bytesPerLine = 3 * width + img = QImage(frame.data, width, height, bytesPerLine, QImage.Format.Format_BGR888).rgbSwapped() + return img + def on_rightvideoSelection(self): logging.debug("Video selection of the %s side", "right") self.right_imagereader = ImageReader(self.right_videocombo.currentText(), 100) @@ -114,13 +154,10 @@ class MergeDetections(QWidget): def right_imgreaderDone(self, state): logging.debug("Right image reader done with state %s", str(state)) frame = self.right_imagereader.frame - height, width, _ = frame.shape - bytesPerLine = 3 * width - img = QImage(frame.data, width, height, bytesPerLine, QImage.Format.Format_BGR888).rgbSwapped() - self.right_preview.setPixmap(QPixmap.fromImage(img).scaledToHeight(self.right_preview.height())) - self.right_posspinner.setMaximum(width) - self.left_posspinner.setValue(width-1) - + img = self._toImage(frame) + self.right_preview.set_image(img) + self.right_posspinner.setMaximum(img.width() - 1) + self.right_posspinner.setValue(0) def on_leftvideoSelection(self): logging.debug("Video selection of the %s side", "left") @@ -131,25 +168,13 @@ class MergeDetections(QWidget): def left_imgreaderDone(self, state): logging.debug("Left image reader done with state %s", str(state)) frame = self.left_imagereader.frame - height, width, _ = frame.shape - bytesPerLine = 3 * width - img = QImage(frame.data, width, height, bytesPerLine, QImage.Format.Format_BGR888).rgbSwapped() - scene = QGraphicsScene() - scene.addPixmap(QPixmap.fromImage(img).scaledToHeight(self.left_preview.height())) - self.left_preview.setScene(scene) - # self.left_preview.setPixmap(QPixmap.fromImage(img).scaledToHeight(self.left_preview.height())) - self.left_posspinner.setMaximum(width-1) - self.left_posspinner.setValue(width-1) - origin = self.left_preview.mapToScene(int(width//2), 0) - destination = self.left_preview.mapToScene(width//2, height) - line = QLineF(origin, destination) - p = QPen(5) - c = QColor.fromString("red") - p.setColor(c) - self.left_line = scene.addLine(line, p) - self.left_preview.show() + img = self._toImage(frame) + self.left_preview.set_image(img) + self.left_posspinner.setMaximum(img.width() - 1) + self.left_posspinner.setValue(img.width() - 1) def on_leftmergelinemove(self): - print(self.left_posspinner.value()) - if self.left_line is not None: - self.left_line.setX(self.left_posspinner.value()) \ No newline at end of file + self.left_preview.set_lineposition(self.left_posspinner.value()) + + def on_rightmergelinemove(self): + self.right_preview.set_lineposition(self.right_posspinner.value()) diff --git a/fixtracks/taskwidget.py b/fixtracks/taskwidget.py index 167cb70..a967355 100644 --- a/fixtracks/taskwidget.py +++ b/fixtracks/taskwidget.py @@ -1,8 +1,8 @@ import logging import pathlib -from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog -from PyQt6.QtCore import pyqtSignal +from PyQt6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton, QFileDialog, QSizePolicy +from PyQt6.QtCore import pyqtSignal, Qt class TasksWidget(QWidget): @@ -10,20 +10,28 @@ class TasksWidget(QWidget): def __init__(self, parent = None): super().__init__(parent) - l = QVBoxLayout() - l.addWidget(QLabel("Tasks:")) + l = QHBoxLayout() + l.setAlignment(Qt.AlignmentFlag.AlignVCenter) folderBtn = QPushButton("Select data folder") folderBtn.setEnabled(True) + folderBtn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) folderBtn.clicked.connect(self._open_folder) l.addWidget(folderBtn) + + sublayout = QVBoxLayout() self.mergeBtn = QPushButton("Merge detections") self.mergeBtn.setEnabled(False) self.mergeBtn.clicked.connect(self._merge_clicked) + self.mergeBtn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + self.tracksBtn = QPushButton("Join tracks") self.tracksBtn.setEnabled(False) self.tracksBtn.clicked.connect(self._tracks_clicked) - l.addWidget(self.mergeBtn) - l.addWidget(self.tracksBtn) + self.tracksBtn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + + sublayout.addWidget(self.mergeBtn) + sublayout.addWidget(self.tracksBtn) + l.addLayout(sublayout) self.setLayout(l) self._file_list = []