首先。我已经在python和PyQt上运行了一些代码,用户在其中绘制图像并且程序返回绘制的图像。我想做的是让用户在完成后修改绘图。例如,他可以点击他绘制的点并拖动它来修改绘画。
有人可以给我创意或图书馆吗?
这是我现有的代码:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import cv2
##
# MAIN WINDOW LAYOUT
##
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.view = View(self)
# Button to clear both image and drawing
self.button = QPushButton('Clear Drawing', self)
self.button.clicked.connect(self.handleClearView)
# 'Load image' button
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Load image')
self.btnLoad.clicked.connect(self.loadImage)
# Save
self.btnSave = QToolButton(self)
self.btnSave.setText('Save image')
self.btnSave.clicked.connect(self.file_save)
# Save as
self.btnSaveAs = QToolButton(self)
self.btnSaveAs.setText('Save as...')
self.btnSaveAs.clicked.connect(self.file_save_as)
# Arrange Layout
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view) # Drawing
self.layout.addWidget(self.button) # Clear view
self.layout.addWidget(self.btnLoad) # Load photo
self.layout.addWidget(self.btnSave) # Save
self.layout.addWidget(self.btnSaveAs) # Save as...
self.setGeometry(0, 25, 1365, 700)
self.setWindowTitle('Processed Slices')
self.show()
def file_save_as(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
filename, _ = QFileDialog.getSaveFileName(self,"QFileDialog.getSaveFileName()","","All Files (*);;Text Files (*.txt)", options=options)
cv2.imwrite(filename + '.png', self.view.cvImage)
def file_save(self):
cv2.imwrite(self.view._filename, self.view.cvImage)
def openFileNameDialog(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "","All Files (*);;Python Files (*.py)", options=options)
if fileName:
self.view._filename = fileName
def loadImage(self):
self.view._empty = False
# Load Image to pixmap
self.openFileNameDialog()
self.view.cvImage = cv2.imread(self.view._filename)
self.view.height, self.view.width , self.view.bytesPerComponent= self.view.cvImage.shape
self.view.bytesPerLine = self.view.bytesPerComponent * self.view.width
cv2.cvtColor(self.view.cvImage, cv2.COLOR_BGR2RGB, self.view.cvImage)
self.view.mQImage = QImage(self.view.cvImage.data, self.view.width, self.view.height, self.view.bytesPerLine, QImage.Format_RGB888)
self.pixmap = QPixmap(self.view.mQImage)
# Include pixmap on the drawing scene
self.view.setScene(QGraphicsScene(self))
self.view.setSceneRect(QRectF(0,0,self.view.width, self.view.height)) # Scene has same dimension as image so that we can map the segmented area to the cv2 image
self.view.scene().addPixmap(self.pixmap)
self.view.fitInView()
def handleClearView(self):
self.view.scene().clear()
self.view.scene().addPixmap(self.pixmap)
self.view.contour = []
self.view.fitInView()
##
# DRAWING AND ZOOMING
##
class View(QGraphicsView):
def __init__(self, parent):
super().__init__()
# Attributes
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self._photo = QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self._filename = ""
# Resettings for Zooming
self.setScene(self._scene)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QBrush(QColor(30, 30, 30)))
self.setFrameShape(QFrame.NoFrame)
self.contour = [] # Contains points of the contour for cv2 drawing
self.first = QPointF(0,0)
self.ii = 0
""" PAINTING """
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._start = event.pos() # Get point where we pressed
self.contour.append(QPointF(self.mapToScene(self._start)))
if self.first == QPointF(0,0): # If it is the first we press, save the point which will be used to close
# the shape whenever the right button is pressed
self.first = QPointF(self.mapToScene(self._start))
self.ii = 0
else: # If it is not the first point draw a line joining the point we previously
# pressed and the one we have just clicked
self.scene().addItem(QGraphicsLineItem(QLineF(self.contour[self.ii+1], self.contour[self.ii])))
self.ii += 1
def mouseReleaseEvent(self, event):
# RIGHT BUTTON
if event.button() == Qt.RightButton:
self.scene().addItem(QGraphicsLineItem(QLineF(self.contour[-1], self.first))) # close contour
self.first = QPointF(0,0)
self.contour.append((self.contour[0]))
#Drawing CV_IMAGE
for i in range(len(self.contour) - 1):
A = ( int( self.contour[i].x() ), int( self.contour[i].y() ) )
B = ( int( self.contour[i+1].x() ), int( self.contour[i+1].y() ))
cv2.line(self.cvImage, A, B, (0,0,255), 2)
cv2.imshow('drawed slice', self.cvImage)
cv2.waitKey()
""" ZOOMING """
def hasPhoto(self):
return not self._empty
def fitInView(self, scale=True):
rect = QRectF(0, 0, self.width, self.height)
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def wheelEvent(self, event):
if self.hasPhoto():
if event.angleDelta().y() > 0: # event.angleDelta() returns the distance that the wheel is rotated, in eighths of a degree.
factor = 1.25 # Zooming in
self._zoom += 1
else:
factor = 0.8 # Zooming out
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else: # Cannot zoom out from the original size
self._zoom = 0
##
# RUN PROGRAM
##
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = Window()
sys.exit(app.exec_())
只需复制粘贴程序,单击加载图像并通过多次左键单击绘制图形,结束图形并查看必须右键单击的结果。
答案 0 :(得分:0)
所以没有简单的答案。我已经做到了,但很多年前。你需要实现某种命中测试机制和“拖动句柄”。这是一个简单的架构,但Qt没有内置的。
基本上,您必须将场景视为创建项目的一系列步骤的产物。您的行self.scene().addItem(...)
是关键。您需要返回场景并查看存在的项目(我忘记了场景能够告诉您有关项目的内容)或启用您自己的外部跟踪。
但是一旦您知道场景中的项目,您需要修改mousePress事件以查看它们是否单击已存在的内容,如果是,请以所需方式修改该对象。这将总是需要一些阈值处理,因此您必须在鼠标点击周围查找大约1到5个像素的项目,并将其包括在内。一旦您确定用户点击了有效的内容,您就可以应用这些事件。所以你需要为mousePress添加一个测试,除非你有一个切换(模式,工具等)进行编辑而不是创建。
这些都不是特定于PyQt的,一般只是Qt。