def mousePressEvent(self, mouseEvent: QMouseEvent): if (not self.pressed and not self.scrolling and mouseEvent.modifiers() == QtCore.Qt.KeyboardModifier.NoModifier): if mouseEvent.buttons() == QtCore.Qt.MouseButton.LeftButton: self.pressed = True self.scrolling = False self.handIsClosed = False QApplication.setOverrideCursor( QtCore.Qt.CursorShape.OpenHandCursor) self.scrollMousePress = self.scrollPosition() self.positionMousePress = mouseEvent.pos()
def measure_angle(self): self.lea = QLineEdit(self) self.lea.move(130, 22) text, ok = QInputDialog.getText(self, 'Input Dialog', 'Angle name') if ok: self.lea.setText(str(text)) self.angleNames.append(self.lea.text()) QApplication.setOverrideCursor(QtCore.Qt.CrossCursor) #change cursor self.bezier.setEnabled(False) self.iw.measuring_angle = True self.iw._lastpos = None self.iw._thispos = None self.statusbar.showMessage('Click initial point for angle measurement') else: self.angleButton.setChecked(False)
def measure_area(self): self.lea = QLineEdit(self) self.lea.move(130, 22) text, ok = QInputDialog.getText(self, 'Input Dialog', 'Area name') if ok: self.lea.setText(str(text)) self.areaNames.append(self.lea.text()) QApplication.setOverrideCursor(QtCore.Qt.CrossCursor) #change cursor self.bezier.setEnabled(False) self.iw.line_count = 0 self.iw.measuring_area = True self.iw._lastpos = None self.iw._thispos = None self.iw.A = posData( np.empty(shape=(0, 0)), np.empty(shape=(0, 0))) #preallocate self.statusbar.showMessage('Click initial point for area measurement') else: self.areaButton.setChecked(False)
def measure_length(self): self.lel = QLineEdit(self) self.lel.move(130, 22) text, ok = QInputDialog.getText(self, 'Input Dialog', 'Length name') if ok: self.lel.setText(str(text)) self.lengthNames.append(self.lel.text()) QApplication.setOverrideCursor(QtCore.Qt.CursorShape.CrossCursor) #change cursor self.widthsButton.setChecked(False) self.widthsButton.setEnabled(False) self.iw.line_count = 0 self.iw.measuring_length = True self.iw.L = posData( np.empty(shape=(0, 0)), np.empty(shape=(0, 0))) #preallocate self.iw._lastpos = None self.iw._thispos = None self.statusbar.showMessage('Click initial point for length measurement') else: self.lengthButton.setChecked(False)
def mouseMoveEvent(self, mouseEvent: QMouseEvent): if self.scrolling: if not self.handIsClosed: QApplication.restoreOverrideCursor() QApplication.setOverrideCursor( QtCore.Qt.CursorShape.OpenHandCursor) self.handIsClosed = True if self.scrollMousePress != None: delta = mouseEvent.pos() - self.positionMousePress self.setScrollPosition( QPoint(int(self.scrollMousePress.x()), int(self.scrollMousePress.y())) - delta) return if self.pressed: self.pressed = False self.scrolling = True self.webViewScrolled.emit(True) return if self.hoveCheck(self.mapPosFromEvent(mouseEvent)): QApplication.setOverrideCursor( QtCore.Qt.CursorShape.PointingHandCursor) else: QApplication.setOverrideCursor(QtCore.Qt.CursorShape.ArrowCursor) return
def __enter__(self): QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
def mousePressEvent(self, event): #http://pyqt.sourceforge.net/Docs/PyQt4/qgraphicsscenemouseevent.html #https://stackoverflow.com/questions/21197658/how-to-get-pixel-on-qgraphicspixmapitem-on-a-qgraphicsview-from-a-mouse-click data = self.mapToScene(event.pos()) #draw piecewise lines for non-width measurements rules = [self.measuring_length, self.measuring_angle, self.measuring_area] if self.scene.testline and self._thispos and any(rules): start = self._thispos end = QtCore.QPointF(data) if self._lastpos and self.measuring_angle: a = self._lastpos - self._thispos b = data - self._thispos a = np.array([a.x(), a.y()]) b = np.array([b.x(), b.y()]) self.measuring_angle = False t = np.arccos(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))) t *= 180 / np.pi #convert to degrees self.T.update(t) self.angleValues = np.append(self.angleValues,t) self.parent().statusbar.showMessage('Angle measurement complete') self.parent().angleButton.setChecked(False) self.parent().bezier.setEnabled(True) self.scene.realline = QGraphicsLineItem(QtCore.QLineF(start, end)) self.scene.addItem(self.scene.realline) #Collect piecewise line start/end points self._lastpos = self._thispos # save old position value self._thispos = QtCore.QPointF(data) # update current position if self.measuring_length: self.L.update(data.x(), data.y()) # update total length self.line_count += 1 elif self.measuring_area: self.line_count += 1 intersect = False if self.line_count > 2: #cant make polygon w/ two lines intersect, xi, yi, k = self.A.checkIntersect(data.x(),data.y()) self.parent().areaButton.setEnabled(True) if intersect: self.measuring_area = False self.A.update(xi,yi) #update with intersect point self.A.x, self.A.y = self.A.x[k:], self.A.y[k:] #only use points after intersection A = self.A.calcArea() self.areaValues = np.append(self.areaValues, A) #add area values #draw permanent polygon points = [ QtCore.QPointF(x,y) for x,y in zip( self.A.x, self.A.y ) ] self.scene.polyItem2 = QGraphicsPolygonItem(QtGui.QPolygonF(points)) self.scene.polyItem2.setBrush( QtGui.QBrush(QtGui.QColor(255,255,255,127)) ) self.scene.removeItem(self.scene.polyItem) #remove mouseover polygon self.scene.polyItem = False #remove mouseover polygon self.scene.addItem(self.scene.polyItem2) #shade in polygon self.parent().statusbar.showMessage('Polygon area measurement completed') self.parent().areaButton.setChecked(False) self.parent().bezier.setEnabled(True) #make bezier fit available again QApplication.setOverrideCursor(QtCore.Qt.ArrowCursor) #change cursor else: self.A.update(data.x(),data.y()) #update with click point #https://stackoverflow.com/questions/30898846/qgraphicsview-items-not-being-placed-where-they-should-be if self.measuring_widths: #measure widths, snap to spines k = int(self.k / 2) + 1 #same origin for spine on either side x0, y0 = self.xp[k], self.yp[k] x1, y1 = data.x(), data.y() #perpindicular slopes vx = self.slopes[:,k][1] vy = -self.slopes[:,k][0] A = np.matrix([[vx, -vy], [vy, vx]]) b = np.array([x1 - x0, y1 - y0]) t = np.linalg.solve(A,b) xi = x0 + t[0]*vx yi = y0 + t[0]*vy self.W.update(xi,yi) p = QtCore.QPointF(xi, yi) s = 10 #dot size self.scene.ellipseItem = QGraphicsEllipseItem(0, 0, s, s) self.scene.ellipseItem.setPos(p.x() - s / 2, p.y() - s / 2) qb = QtGui.QBrush() qb.setColor(QtGui.QColor('red')) self.scene.ellipseItem.setBrush(qb) self.scene.ellipseItem.setFlag( QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations, False) #size stays small, but doesnt translate if false self.scene.addItem(self.scene.ellipseItem) self.k += 1 if self.k < self.nspines: self.d[str(self.k)].setPen(QtGui.QPen( QtGui.QColor('yellow'))) #Highlight next spine if self.k == self.nspines: self.parent().statusbar.showMessage('Width measurements complete') self.measuring_widths = False self.parent().widthsButton.setEnabled(False) self.parent().widthsButton.setChecked(False) self.parent().bezier.setEnabled(True) width = np.sqrt( (self.W.x[1::2] - self.W.x[0::2])**2 + (self.W.y[1::2] - self.W.y[0::2])**2) #calculate widths self.widths[-1] = width
def mouseDoubleClickEvent(self, event): def qpt2pt(x, y): Q = self.mapFromScene(self.mapToScene( int(x), int(y))) return Q.x(), Q.y() #only delete lines if bezier fit if self.measuring_length and self.parent().bezier.isChecked() and (len(np.vstack((self.L.x, self.L.y)).T) > 2): self.parent().statusbar.showMessage('Length measurement complete.') #Remove most recent items drawn (exact lines) nl = self.line_count for k, i in enumerate(self.scene.items()): if k < nl: self.scene.removeItem(i) #set item to false? if self._lastpos and self.measuring_length: # catmull roms spline instead? # or rational bezier curve - tuneable approximating/interpolating. ref. wikipedia # https://codeplea.com/introduction-to-splines if (self.parent().bezier.isChecked()) and (len(np.vstack((self.L.x, self.L.y)).T) > 2): nt = 2000 #max(1000, self.numwidths * 50) #num of interpolating points def bernstein(i, n, t): return comb(n,i) * t**(n-i) * (1-t)**i def bezier_rational(points, nt): """Rational Bezier Curve fit""" # https://gist.github.com/Alquimista/1274149 # https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html n = len(points) xp = np.array([p[0] for p in points]) yp = np.array([p[1] for p in points]) t = np.linspace(0.0, 1.0, nt) #Bezier curve B = np.array([ bernstein(i,n-1,t) for i in range(0,n) ]) xb = np.dot(xp, B)[::-1] yb = np.dot(yp, B)[::-1] #Analytic gradient for bezier curve Qx = n*np.diff(xp) Qy = n*np.diff(yp) Bq = np.array([ bernstein(i,n-2,t) for i in range(0,n-1) ]) dxb = np.dot(Qx, Bq)[::-1] dyb = np.dot(Qy, Bq)[::-1] m = np.vstack((dxb,dyb)) m *= (1/np.linalg.norm(m, axis=0)) return xb, yb, m points = np.vstack((self.L.x, self.L.y)).T self.xs, self.ys, self.m = bezier_rational(points, nt) pts = np.array(list(map(qpt2pt, self.xs, self.ys))) x, y = pts[:, 0], pts[:, 1] self.l = np.cumsum(np.hypot(np.gradient(x), np.gradient(y))) #integrate for length #draw cubic line to interpolated points for i in range(1, nt - 1): P0 = QtCore.QPointF( self.xs[i-1], self.ys[i-1] )#.toPoint() P1 = QtCore.QPointF( self.xs[i ], self.ys[i ] )#.toPoint() P2 = QtCore.QPointF( self.xs[i+1], self.ys[i+1] )#.toPoint() start = self.mapFromScene(self.mapToScene(P0.toPoint())) mid = self.mapFromScene(self.mapToScene(P1.toPoint())) end = self.mapFromScene(self.mapToScene(P2.toPoint())) path = QtGui.QPainterPath(P0) path.cubicTo(P0, P1, P2) self.scene.addPath(path) if (not self.parent().bezier.isChecked()) or (len(np.vstack((self.L.x, self.L.y)).T) <= 2): """Simple linear points if piecewise mode (or only two points used?)""" pts = np.array(list(map(qpt2pt, self.L.x, self.L.y))) x, y = pts[:, 0], pts[:, 1] slope = (y[-1] - y[0]) / (x[-1] - x[0]) theta = np.arctan(slope) distance = np.hypot( x[-1] - x[0], y[-1] - y[0] ) r = np.linspace(0, distance, 1000) self.xs, self.ys = x[0] + r*np.cos(theta), y[0] + r*np.sin(theta) self.m = np.vstack(( slope*(r*0 + 1), -slope*(r*0 + 1) )) #self.m = np.vstack(( (y[-1] - y[0])*(r*0 + 1), (x[-1] - x[0])*(r*0 + 1) )) self.m = np.vstack(( (x[-1] - x[0])*(r*0 + 1), (y[-1] - y[0])*(r*0 + 1) )) self.l = np.cumsum(np.hypot(np.diff(self.xs), np.diff(self.ys))) #integrate for length self.lengths[-1] = self.l[-1] self.lengths.extend([np.nan]) self.widths.append([]) self.widthNames.append([]) QApplication.setOverrideCursor(QtCore.Qt.CursorShape.ArrowCursor) #change cursor if self.parent().bezier.isChecked() or (len(np.vstack((self.L.x, self.L.y)).T) <= 2): #measure widths possible if bezier or if single piecewise segment self.parent().widthsButton.setEnabled(True) self.parent().lengthButton.setChecked(False) self.parent().angleButton.setChecked(False) self.measuring_length = False self.measuring_angle = False self._thispos = False
def mouseMoveEvent(self, event): data = self.mapToScene(event.position().toPoint()) rules = [self.measuring_length, self.measuring_angle, self.measuring_area] modifiers = QApplication.keyboardModifiers() if modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier and self.oldPos: QApplication.setOverrideCursor(QtCore.Qt.CursorShape.OpenHandCursor) self.newPos = data delta = self.newPos - self.oldPos self.translate(delta.x(), delta.y()) elif (any(rules) or self.measuring_widths): QApplication.setOverrideCursor(QtCore.Qt.CursorShape.CrossCursor) #change cursor else: QApplication.setOverrideCursor(QtCore.Qt.CursorShape.ArrowCursor) #change cursor #dragging line if self._thispos and any(rules): if self.measuring_length: self.parent().statusbar.showMessage( 'Click to place next point... double click to finish') if self.measuring_area: self.parent().statusbar.showMessage( 'Click to place next point... close polygon to finish') if self.measuring_angle: self.parent().statusbar.showMessage( 'Click point to define vector') end = QtCore.QPointF(data)#self.mapToScene(event.pos())) start = self._thispos if self.measuring_angle and self._lastpos: start = self._thispos if self.scene.testline: #remove old line self.scene.removeItem(self.scene.testline) self.scene.testline = False if self.measuring_area and self.line_count > 2: intersect, xi, yi, k = self.A.checkIntersect(data.x(),data.y()) if self.scene.area_ellipseItem: #remove existing intersect self.scene.removeItem(self.scene.area_ellipseItem) self.scene.area_ellipseItem = False if self.scene.polyItem: self.scene.removeItem(self.scene.polyItem) self.scene.polyItem = False if intersect: #indicate intersect point p = QtCore.QPointF(xi, yi) self.scene.area_ellipseItem = QGraphicsEllipseItem(0, 0, 10, 10) self.scene.area_ellipseItem.setPos(p.x() - 10 / 2, p.y() - 10 / 2) self.scene.area_ellipseItem.setBrush( QtGui.QBrush(QtCore.QtColor('blue'))) #, style=QtCore.Qt.BrushStyle.SolidPattern)) self.scene.area_ellipseItem.setFlag( QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations, False) #size stays small, but doesnt translate if set to false self.scene.addItem(self.scene.area_ellipseItem) #shade polygon region points = [ QtCore.QPointF(x,y) for x,y in zip( self.A.x[k:], self.A.y[k:] ) ] points.append(QtCore.QPointF(xi,yi)) self.scene.polyItem = QGraphicsPolygonItem(QtGui.QPolygonF(points)) self.scene.polyItem.setBrush( QtGui.QBrush(QtGui.QColor(255,255,255,127)) ) self.scene.addItem(self.scene.polyItem) self.scene.testline = QGraphicsLineItem(QtCore.QLineF(start, end)) self.scene.addItem(self.scene.testline)