from PyQt5 import QtGui, QtWidgets, QtCore import sys sys.path.append("C://Users//xbrjos//Desktop//Python") import gepard from gepard import dataset import helpers class FilterView(QtWidgets.QGraphicsView): def __init__(self): super(FilterView, self).__init__() self.setWindowTitle('FilterView') self.dataset: dataset.DataSet = None scene = QtWidgets.QGraphicsScene(self) scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex) scene.setBackgroundBrush(QtCore.Qt.darkGray) self.setScene(scene) self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground) self.setViewportUpdateMode(QtWidgets.QGraphicsView.BoundingRectViewportUpdate) self.setRenderHint(QtGui.QPainter.Antialiasing) self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) self.drag = None self.filter: FilterGraphItem = FilterGraphItem() self.scene().addItem(self.filter) self.measuringBoxes: list = [] self.contourItems: list = [] def update_measure_boxes(self, topLefts: list, boxSize: float) -> None: self._remove_measure_boxes() offset = self.filter.circleOffset for x, y in topLefts: newBox: MeasureBoxGraphItem = MeasureBoxGraphItem(x+offset[0], y+offset[1], boxSize, boxSize) self.measuringBoxes.append(newBox) self.scene().addItem(newBox) self._update_measured_contours() def _remove_measure_boxes(self) -> None: for item in self.measuringBoxes: self.scene().removeItem(item) self.measuringBoxes = [] def load_and_update_from_dataset(self, fname: str) -> None: self.dataset = dataset.loadData(fname) offset, diameter, widthHeight = helpers.get_filterDimensions_from_dataset(self.dataset) offsetx = helpers.convert_length_to_pixels(self.dataset, offset[0]) offsety = helpers.convert_length_to_pixels(self.dataset, offset[1]) diameter = helpers.convert_length_to_pixels(self.dataset, diameter) width = helpers.convert_length_to_pixels(self.dataset, widthHeight[0]) height = helpers.convert_length_to_pixels(self.dataset, widthHeight[1]) self.filter.update_filterSize(width, height, diameter, (offsetx, offsety)) self._update_particle_contours() self._fit_to_window() def _update_particle_contours(self) -> None: self._remove_particle_contours() if self.dataset is not None: for particle in self.dataset.particleContainer.particles: newContour: ParticleContour = ParticleContour(particle.contour) self.scene().addItem(newContour) self.contourItems.append(newContour) def _remove_particle_contours(self) -> None: for cntItem in self.contourItems: self.scene().removeItem(cntItem) self.contourItems = [] def _update_measured_contours(self) -> None: # offset = self.filter.circleOffset # offset = (-offset[0], -offset[1]) offset = (0, 0) for contourItem in self.contourItems: contourItem.isMeasured = False for measBox in self.measuringBoxes: topLeftXY = (measBox.posX, measBox.posY) boxWidthHeight = (measBox.width, measBox.height) if helpers.box_overlaps_contour(topLeftXY, boxWidthHeight, contourItem.polygon, offset=offset): contourItem.isMeasured = True contourItem.update() break def wheelEvent(self, event: QtGui.QWheelEvent) -> None: factor: float = 1.01 ** (event.angleDelta().y() / 8) newScale: float = self.filter.scale() * factor self.scale(newScale, newScale) def mousePressEvent(self, event): if event.button() == QtCore.Qt.MiddleButton: self.drag = event.pos() def mouseMoveEvent(self, event): if self.drag is not None: p0 = event.pos() move = self.drag - p0 self.horizontalScrollBar().setValue(move.x() + self.horizontalScrollBar().value()) self.verticalScrollBar().setValue(move.y() + self.verticalScrollBar().value()) self.drag = p0 def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None: self.drag = None def _fit_to_window(self) -> None: brect = self.scene().itemsBoundingRect() self.fitInView(0, 0, brect.width(), brect.height(), QtCore.Qt.KeepAspectRatio) class FilterGraphItem(QtWidgets.QGraphicsItem): """ The Graphical Representation of the actual filter """ def __init__(self, filterWidth: float = 500, filterHeight: float = 500, diameter: float = 500): super(FilterGraphItem, self).__init__() self.width: float = filterWidth self.height: float = filterHeight self.diameter: float = diameter self.circleOffset: tuple = (0, 0) self.setPos(0, 0) self.rect = QtCore.QRectF(0, 0, self.width, self.height) self.circleRect = QtCore.QRectF(self.circleOffset[0], self.circleOffset[1], self.diameter, self.diameter) def boundingRect(self) -> QtCore.QRectF: return self.rect def update_filterSize(self, width: float, height: float, diameter: float, offset: tuple) -> None: self.width = width self.height = height self.rect = QtCore.QRectF(0, 0, self.width, self.height) self.diameter = diameter self.circleOffset = offset self.circleRect = QtCore.QRectF(self.circleOffset[0], self.circleOffset[1], self.diameter, self.diameter) def paint(self, painter: QtGui.QPainter, option, widget) -> None: painter.setPen(QtCore.Qt.black) painter.setBrush(QtCore.Qt.white) painter.drawRect(self.rect) painter.setPen(QtCore.Qt.darkGray) painter.setBrush(QtCore.Qt.lightGray) painter.drawEllipse(self.circleRect) class MeasureBoxGraphItem(QtWidgets.QGraphicsItem): """ Displays a box in which particles will be measured """ def __init__(self, posX: float = 50, posY: float = 50, width: float = 50, height: float = 50) -> None: super(MeasureBoxGraphItem, self).__init__() self.setZValue(5) self.posX: float = posX self.posY: float = posY self.height: float = height self.width: float = width self.setPos(posX, posY) self.rect = QtCore.QRectF(0, 0, self.width, self.height) self.setToolTip(f'x0: {round(self.posX)}, y0: {round(self.posY)}, \n' f'x1: {round(self.posX + self.width)}, y1: {round(self.posY + self.height)}') def boundingRect(self) -> QtCore.QRectF: return self.rect def paint(self, painter, option, widget) -> None: painter.setBrush(QtGui.QColor(0, 255, 0, 180)) painter.setPen(QtCore.Qt.darkGreen) painter.drawRects(self.rect) class ParticleContour(QtWidgets.QGraphicsItem): def __init__(self, contourData, pos=(0, 0)) -> None: super(ParticleContour, self).__init__() self.setZValue(1) self.setPos(pos[0], pos[1]) self.brect: QtCore.QRectF = QtCore.QRectF(0, 0, 1, 1) self.isMeasured: bool = False # Wether the particle overlaps with a measuring box or nt self.contourData = contourData self.polygon = None self.getBrectAndPolygon() def getBrectAndPolygon(self) -> None: """ Calculates the bounding rect (needed for drawing the QGraphicsView) and converts the contourdata to a polygon. :return: """ self.polygon = QtGui.QPolygonF() x0 = self.contourData[:, 0, 0].min() x1 = self.contourData[:, 0, 0].max() y0 = self.contourData[:, 0, 1].min() y1 = self.contourData[:, 0, 1].max() for point in self.contourData: self.polygon.append(QtCore.QPointF(point[0, 0], point[0, 1])) self.brect.setCoords(x0, y0, x1, y1) def boundingRect(self) -> QtCore.QRectF: return self.brect def paint(self, painter, option, widget) -> None: if self.polygon is not None: if self.isMeasured: painter.setPen(QtCore.Qt.darkRed) painter.setBrush(QtCore.Qt.red) else: painter.setPen(QtCore.Qt.darkCyan) painter.setBrush(QtCore.Qt.cyan) painter.drawPolygon(self.polygon)