def mouseMoveEvent(self, QMouseEvent): diff = QPoint(QMouseEvent.pos() - self.mouse_position) self.mouse_position = QMouseEvent.pos() self.scroll_area.verticalScrollBar().setValue( self.scroll_area.verticalScrollBar().value() - diff.y()) self.scroll_area.horizontalScrollBar().setValue( self.scroll_area.horizontalScrollBar().value() - diff.x())
class MandelbrotWidget(QWidget): def __init__(self, parent=None): super(MandelbrotWidget, self).__init__(parent) self.thread = RenderThread() self.pixmap = QPixmap() self.pixmapOffset = QPoint() self.lastDragPos = QPoint() self.centerX = DefaultCenterX self.centerY = DefaultCenterY self.pixmapScale = DefaultScale self.curScale = DefaultScale self.thread.renderedImage.connect(self.updatePixmap) self.setWindowTitle("Mandelbrot") self.setCursor(Qt.CrossCursor) self.resize(550, 400) def paintEvent(self, event): painter = QPainter(self) painter.fillRect(self.rect(), Qt.black) if self.pixmap.isNull(): painter.setPen(Qt.white) painter.drawText(self.rect(), Qt.AlignCenter, "Rendering initial image, please wait...") return if self.curScale == self.pixmapScale: painter.drawPixmap(self.pixmapOffset, self.pixmap) else: scaleFactor = self.pixmapScale / self.curScale newWidth = int(self.pixmap.width() * scaleFactor) newHeight = int(self.pixmap.height() * scaleFactor) newX = self.pixmapOffset.x() + (self.pixmap.width() - newWidth) / 2 newY = self.pixmapOffset.y() + (self.pixmap.height() - newHeight) / 2 painter.save() painter.translate(newX, newY) painter.scale(scaleFactor, scaleFactor) exposed, _ = painter.transform().inverted() exposed = exposed.mapRect(self.rect()).adjusted(-1, -1, 1, 1) painter.drawPixmap(exposed, self.pixmap, exposed) painter.restore() text = "Use mouse wheel or the '+' and '-' keys to zoom. Press and " \ "hold left mouse button to scroll." metrics = painter.fontMetrics() textWidth = metrics.horizontalAdvance(text) painter.setPen(Qt.NoPen) painter.setBrush(QColor(0, 0, 0, 127)) painter.drawRect((self.width() - textWidth) / 2 - 5, 0, textWidth + 10, metrics.lineSpacing() + 5) painter.setPen(Qt.white) painter.drawText((self.width() - textWidth) / 2, metrics.leading() + metrics.ascent(), text) def resizeEvent(self, event): self.thread.render(self.centerX, self.centerY, self.curScale, self.size()) def keyPressEvent(self, event): if event.key() == Qt.Key_Plus: self.zoom(ZoomInFactor) elif event.key() == Qt.Key_Minus: self.zoom(ZoomOutFactor) elif event.key() == Qt.Key_Left: self.scroll(-ScrollStep, 0) elif event.key() == Qt.Key_Right: self.scroll(+ScrollStep, 0) elif event.key() == Qt.Key_Down: self.scroll(0, -ScrollStep) elif event.key() == Qt.Key_Up: self.scroll(0, +ScrollStep) else: super(MandelbrotWidget, self).keyPressEvent(event) def wheelEvent(self, event): numDegrees = event.angleDelta().y() / 8 numSteps = numDegrees / 15.0 self.zoom(pow(ZoomInFactor, numSteps)) def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: self.lastDragPos = QPoint(event.pos()) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.pixmapOffset += event.pos() - self.lastDragPos self.lastDragPos = QPoint(event.pos()) self.update() def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.pixmapOffset += event.pos() - self.lastDragPos self.lastDragPos = QPoint() deltaX = (self.width() - self.pixmap.width()) / 2 - self.pixmapOffset.x() deltaY = (self.height() - self.pixmap.height()) / 2 - self.pixmapOffset.y() self.scroll(deltaX, deltaY) def updatePixmap(self, image, scaleFactor): if not self.lastDragPos.isNull(): return self.pixmap = QPixmap.fromImage(image) self.pixmapOffset = QPoint() self.lastDragPosition = QPoint() self.pixmapScale = scaleFactor self.update() def zoom(self, zoomFactor): self.curScale *= zoomFactor self.update() self.thread.render(self.centerX, self.centerY, self.curScale, self.size()) def scroll(self, deltaX, deltaY): self.centerX += deltaX * self.curScale self.centerY += deltaY * self.curScale self.update() self.thread.render(self.centerX, self.centerY, self.curScale, self.size())
def is_two_dimensional(p1: QPoint, p2: QPoint) -> bool: if p1.x() != p2.x() and p1.y() != p2.y(): return True else: return False
class GLWidget(QOpenGLWidget, QOpenGLFunctions): xRotationChanged = Signal(int) yRotationChanged = Signal(int) zRotationChanged = Signal(int) def __init__(self, parent=None): QOpenGLWidget.__init__(self, parent) QOpenGLFunctions.__init__(self) self.core = "--coreprofile" in QCoreApplication.arguments() self.xRot = 0 self.yRot = 0 self.zRot = 0 self.lastPos = 0 self.logo = Logo() self.vao = QOpenGLVertexArrayObject() self.logoVbo = QOpenGLBuffer() self.program = QOpenGLShaderProgram() self.projMatrixLoc = 0 self.mvMatrixLoc = 0 self.normalMatrixLoc = 0 self.lightPosLoc = 0 self.proj = QMatrix4x4() self.camera = QMatrix4x4() self.world = QMatrix4x4() self.transparent = "--transparent" in QCoreApplication.arguments() if self.transparent: fmt = self.format() fmt.setAlphaBufferSize(8) self.setFormat(fmt) def xRotation(self): return self.xRot def yRotation(self): return self.yRot def zRotation(self): return self.zRot def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(400, 400) def normalizeAngle(self, angle): while angle < 0: angle += 360 * 16 while angle > 360 * 16: angle -= 360 * 16 return angle def setXRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.xRot: self.xRot = angle self.emit(SIGNAL("xRotationChanged(int)"), angle) self.update() def setYRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.yRot: self.yRot = angle self.emit(SIGNAL("yRotationChanged(int)"), angle) self.update() def setZRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.zRot: self.zRot = angle self.emit(SIGNAL("zRotationChanged(int)"), angle) self.update() def cleanup(self): self.makeCurrent() self.logoVbo.destroy() del self.program self.program = None self.doneCurrent() def vertexShaderSourceCore(self): return """#version 150 in vec4 vertex; in vec3 normal; out vec3 vert; out vec3 vertNormal; uniform mat4 projMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; void main() { vert = vertex.xyz; vertNormal = normalMatrix * normal; gl_Position = projMatrix * mvMatrix * vertex; }""" def fragmentShaderSourceCore(self): return """#version 150 in highp vec3 vert; in highp vec3 vertNormal; out highp vec4 fragColor; uniform highp vec3 lightPos; void main() { highp vec3 L = normalize(lightPos - vert); highp float NL = max(dot(normalize(vertNormal), L), 0.0); highp vec3 color = vec3(0.39, 1.0, 0.0); highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); fragColor = vec4(col, 1.0); }""" def vertexShaderSource(self): return """attribute vec4 vertex; attribute vec3 normal; varying vec3 vert; varying vec3 vertNormal; uniform mat4 projMatrix; uniform mat4 mvMatrix; uniform mat3 normalMatrix; void main() { vert = vertex.xyz; vertNormal = normalMatrix * normal; gl_Position = projMatrix * mvMatrix * vertex; }""" def fragmentShaderSource(self): return """varying highp vec3 vert; varying highp vec3 vertNormal; uniform highp vec3 lightPos; void main() { highp vec3 L = normalize(lightPos - vert); highp float NL = max(dot(normalize(vertNormal), L), 0.0); highp vec3 color = vec3(0.39, 1.0, 0.0); highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0); gl_FragColor = vec4(col, 1.0); }""" def initializeGL(self): self.context().aboutToBeDestroyed.connect(self.cleanup) self.initializeOpenGLFunctions() self.glClearColor(0, 0, 0, 1) self.program = QOpenGLShaderProgram() if self.core: self.vertexShader = self.vertexShaderSourceCore() self.fragmentShader = self.fragmentShaderSourceCore() else: self.vertexShader = self.vertexShaderSource() self.fragmentShader = self.fragmentShaderSource() self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, self.vertexShader) self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, self.fragmentShader) self.program.bindAttributeLocation("vertex", 0) self.program.bindAttributeLocation("normal", 1) self.program.link() self.program.bind() self.projMatrixLoc = self.program.uniformLocation("projMatrix") self.mvMatrixLoc = self.program.uniformLocation("mvMatrix") self.normalMatrixLoc = self.program.uniformLocation("normalMatrix") self.lightPosLoc = self.program.uniformLocation("lightPos") self.vao.create() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.logoVbo.create() self.logoVbo.bind() float_size = ctypes.sizeof(ctypes.c_float) self.logoVbo.allocate(self.logo.constData(), self.logo.count() * float_size) self.setupVertexAttribs() self.camera.setToIdentity() self.camera.translate(0, 0, -1) self.program.setUniformValue(self.lightPosLoc, QVector3D(0, 0, 70)) self.program.release() vaoBinder = None def setupVertexAttribs(self): self.logoVbo.bind() f = QOpenGLContext.currentContext().functions() f.glEnableVertexAttribArray(0) f.glEnableVertexAttribArray(1) float_size = ctypes.sizeof(ctypes.c_float) null = VoidPtr(0) pointer = VoidPtr(3 * float_size) f.glVertexAttribPointer(0, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, null) f.glVertexAttribPointer(1, 3, int(GL.GL_FLOAT), int(GL.GL_FALSE), 6 * float_size, pointer) self.logoVbo.release() def paintGL(self): self.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.glEnable(GL.GL_DEPTH_TEST) self.glEnable(GL.GL_CULL_FACE) self.world.setToIdentity() self.world.rotate(180 - (self.xRot / 16), 1, 0, 0) self.world.rotate(self.yRot / 16, 0, 1, 0) self.world.rotate(self.zRot / 16, 0, 0, 1) vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) self.program.bind() self.program.setUniformValue(self.projMatrixLoc, self.proj) self.program.setUniformValue(self.mvMatrixLoc, self.camera * self.world) normalMatrix = self.world.normalMatrix() self.program.setUniformValue(self.normalMatrixLoc, normalMatrix) self.glDrawArrays(GL.GL_TRIANGLES, 0, self.logo.vertexCount()) self.program.release() vaoBinder = None def resizeGL(self, width, height): self.proj.setToIdentity() self.proj.perspective(45, width / height, 0.01, 100) def mousePressEvent(self, event): self.lastPos = QPoint(event.pos()) def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.setXRotation(self.xRot + 8 * dy) self.setYRotation(self.yRot + 8 * dx) elif event.buttons() & Qt.RightButton: self.setXRotation(self.xRot + 8 * dy) self.setZRotation(self.zRot + 8 * dx) self.lastPos = QPoint(event.pos())
class CustomQGraphicsView(QGraphicsView): # Ustawienia na potrzeby poprawnego rysowania: class Selection(Enum): SELECT = 1 RECTANGLE = 2 TRIANGLE = 3 ELLIPSE = 4 LINE = 5 POLYGON = 6 # Signal - zmiana, która być może powinna być zapisana przed wyjściem! changed = Signal() def __init__(self, scene, central_widget, gim): super(CustomQGraphicsView, self).__init__(scene, central_widget) # Ustawienia na potrzeby poprawnego rysowania: self.actual_selection: Enum = self.Selection.SELECT self.drawing: bool = False self.preparing: bool = False self.prepare_points: list[QPointF] = [] self.previous_point: QPoint = QPoint(0, 0) # Obiekty na potrzeby sposobu rysowania self.actual_pen: QPen = QPen(QBrush( Qt.black), 1) # QPen: kolor i szerokosc linii + dodatki self.actual_brush: QBrush = QBrush(Qt.darkGreen) # kolor wypełnienia # Model dla itemów sceny self.gim: GraphicItemModel = gim # Wykrywanie chęci nanoszenia lub zaznaczenia kształtu def mousePressEvent(self, e: QMouseEvent) -> None: if self.actual_selection.value == 1: super(CustomQGraphicsView, self).mousePressEvent(e) elif self.actual_selection.value in [2, 3, 4, 5]: self.drawing = True self.previous_point = QPoint(int(e.position().x()), int(e.position().y())) elif self.actual_selection.value == 6: self.previous_point = self.mapToScene( QPoint(int(e.position().x()), int(e.position().y()))) if not self.preparing: self.preparing = True self.prepare_points = list() self.prepare_points.append(self.previous_point) else: below_point: float = 5 / self.transform().m11() DrawTools.draw_simple_shape_to_scene(self, self.prepare_points[-1], self.previous_point, to_gim=False) if fabs(self.previous_point.x() - self.prepare_points[0].x()) < below_point and \ fabs(self.previous_point.y() - self.prepare_points[0].y()) < below_point: if len(self.prepare_points) <= 2: items = self.scene().items() for i in range(len(self.prepare_points)): self.scene().removeItem(items[i]) self.preparing = False return DrawTools.draw_polygon(self, self.prepare_points) self.preparing = False else: self.prepare_points.append(self.previous_point) # Działania po zakończeniu akcji rysowania def mouseReleaseEvent(self, e: QMouseEvent) -> None: super(CustomQGraphicsView, self).mouseReleaseEvent(e) if self.drawing: self.drawing = False p_prev: QPointF = self.mapToScene(self.previous_point) p_act: QPointF = self.mapToScene( QPoint(int(e.position().x()), int(e.position().y()))) if p_prev.x() != p_act.x() and p_act.y() != p_prev.y(): if self.actual_selection.value in [ 2, 3, 4 ] and DrawTools.is_two_dimensional(p_prev, p_act): DrawTools.draw_simple_shape_to_scene(self, p_prev, p_act) elif self.actual_selection.value == 5: DrawTools.draw_simple_shape_to_scene(self, p_prev, p_act) self.changed.emit() def keyPressEvent(self, e: QKeyEvent) -> None: if len(self.scene().selectedItems()) > 0: if e.key() == Qt.Key_Delete: OperationTools.delete_selected(self.scene(), self.gim) self.changed.emit() elif e.key() == Qt.Key_Right or e.key() == Qt.Key_Left: OperationTools.rotate_selected(self.scene(), e) self.changed.emit() elif e.key() in [Qt.Key_W, Qt.Key_S, Qt.Key_A, Qt.Key_D]: OperationTools.rescale_selected(self.scene(), e) self.changed.emit() # Przybliżanie i oddalanie widoku na scenę def wheelEvent(self, e: QWheelEvent) -> None: ViewTools.change_view_zoom(self, e)
def distance(a: QtCore.QPoint, b: QtCore.QPoint): return np.sqrt(np.power(a.x() - b.x(), 2) + np.power(a.y() - b.y(), 2))