In QMainWindow, I draw an image from a QPixmap in PaintEvent as follows:

painter.begin(self.pixmapHolder) painter.drawPixmap(0, 0, self.imageWidth, self.imageHeight, self.cellPixmap, self.x0, self.y0, self.xe, self.ye) painter.end() self.lbl.setPixmap(self.pixmapHolder) self.lbl.resize(min(width, self.maxImageWidth), min(height, self.maxImageHeight)) 

The variables self.x0 and self.y0 are responsible for the beginning of the rectangle to be displayed on the image if it does not fit completely, and self.xe and self.ye are its end. The resulting image:

When dragging an image, traces remain as in the winXP lagging: enter image description here

Dragging is done by changing x0, y0, xe, ye:

 def mouseMoveEvent(self, event): if self.isDraged and self.isImageLoaded: self.isMoving = True self.x0 -= event.x() - self.cursorX self.y0 -= event.y() - self.cursorY self.xe -= event.x() - self.cursorX self.ye -= event.y() - self.cursorY self.cursorX = event.x() self.cursorY = event.y() 

As far as I understand, the problem is precisely in cleaning the "form". How can this be realized?

 import sys from collections import namedtuple from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5.QtGui import qRgb from PyQt5.QtGui import QPixmap, QPainter, QBrush, QColor, QImage, QPalette from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QAction, qApp, QMessageBox from PyQt5.QtWidgets import QFileDialog, QLabel, QPushButton, QSlider, QCheckBox from PyQt5.QtCore import Qt, QSize from utils.targetbuild import * class ImageClickEvents(QMainWindow): def __init__(self, screen_width, screen_height, isImageLoaded=False): super(ImageClickEvents, self).__init__() self.isImageLoaded = False self.isDrawingRect = False self.newRectCoord = None self.isDraged = False self.isMoving = False self.x0 = 0 self.y0 = 0 self.xe = screen_width self.ye = screen_height self.xScaled = 1.0 # мСстополоТСния курсора ΠΌΡ‹ΡˆΠΈ для drag self.cursorX = 0 self.cursorY = 0 self.lbl = QLabel(self) self.cellPixmap = QPixmap(screen_width, screen_height) self.cellPixmapUnscaled = QPixmap(screen_width, screen_height) self.pixmapHolder = QPixmap(700, 400) self.imageWidth = 700 self.imageHeight = 400 self.resize(QSize(875, 400)) # const codes of button in qt # here's mouse buttons: self.NoneButton = 0x00000000 self.LeftButton = 0x00000001 self.RightButton = 0x00000002 self.AllButtons = 0x07ffffff self.initGUI() def initGUI(self): self.lbl.setGeometry(0, 0, 700, 400) openImageBtn = QPushButton('&ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅', self) openImageBtn.setGeometry(720, 100, 130, 25) openImageBtn.clicked.connect(self.showOpenFileDialog) saveImageBtn = QPushButton('&Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅', self) saveImageBtn.setGeometry(720, 210, 130, 25) saveImageBtn.clicked.connect(self.showSaveFileDialog) self.update() self.show() def wheelEvent(self, event): numPixels = event.pixelDelta() numDegrees = event.angleDelta() if not numPixels.isNull(): self.xScaled += 0.1*signum(numPixels.y()) elif not numDegrees.isNull(): self.xScaled += 0.1*signum(numDegrees.y()) if self.xScaled < 0.5: self.xScaled = 0.5 elif self.xScaled >= 10.0: self.xScaled = 10.0 self.lbl.setPixmap(QPixmap(1, 1)); self.cellPixmap = self.cellPixmapUnscaled.scaled(self.xScaled * self.imageWidth, self.xScaled * self.imageHeight, Qt.KeepAspectRatioByExpanding) def mousePressEvent(self, event): btn = event.button() if self.isImageLoaded and btn == self.LeftButton: self.isDraged = True print(event.x()) self.cursorX = event.x() self.cursorY = event.y() def mouseMoveEvent(self, event): if self.isDraged and self.isImageLoaded: self.isMoving = True self.x0 -= event.x() - self.cursorX self.y0 -= event.y() - self.cursorY self.xe -= event.x() - self.cursorX self.ye -= event.y() - self.cursorY self.cursorX = event.x() self.cursorY = event.y() def mouseReleaseEvent(self, QMouseEvent): if not self.isImageLoaded: return mx, my = QMouseEvent.pos().x(), QMouseEvent.pos().y() if mx > 700: return pressedButton = QMouseEvent.button() if not self.isImageLoaded: return if self.isDraged and self.isMoving: self.isDraged = False self.isMoving = False return if not self.isDrawingRect: self.isDraged = False def showOpenFileDialog(self): file_name = QFileDialog.getOpenFileName(self, 'Open image', '.')[0] print(file_name) if file_name == '': return self.cellPixmapUnscaled = QPixmap(file_name) self.cellPixmap = self.cellPixmapUnscaled.copy() self.isImageLoaded = True self.x0 = 0 self.y0 = 0 self.xe = min(self.cellPixmap.width(), self.imageWidth) self.ye = min(self.cellPixmap.height(), self.imageHeight) self.xScaled = 1.0 self.update() def showSaveFileDialog(self): if not self.isImageLoaded: nope = QMessageBox() nope.setText("Π—Π°Ρ€ΡƒΠ·ΠΈΡ‚Π΅ ΠΈΠ·Π±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅") nope.exec() return file_name = QFileDialog.getSaveFileName(self, 'Save image', '.')[0] if file_name == '': return if file_name[-4:] != '.png': file_name += '.png' print(file_name) saving_image = QPixmap.toImage(self.cellPixmap) saving_image.save(file_name, 'PNG') def paintEvent(self, event): super().paintEvent(event) painter = QPainter() painter.begin(self.pixmapHolder) painter.drawPixmap(0, 0, self.imageWidth, self.imageHeight, self.cellPixmap, self.x0, self.y0, self.xe, self.ye) painter.end() if self.isImageLoaded: self.lbl.setPixmap(self.pixmapHolder) height = self.cellPixmap.height() width = self.cellPixmap.width() self.lbl.resize(min(width, self.imageWidth), min(height, self.imageHeight)) start_x = 0 start_y = 0 if width < self.imageWidth: start_x = (self.imageWidth - width) / 2 if height < self.imageHeight: start_y = (self.imageHeight - height) / 2 self.lbl.move(start_x, start_y) if __name__ == '__main__': app = QApplication(sys.argv) image_click = ImageClickEvents(700, 400, False) sys.exit(app.exec_()) 
  • Please post a minimum-complete example to demonstrate your problem. - S. Nick
  • Added code to the question. - Depth

1 answer 1

I suggest you try the option without mathematical calculations. All you need is to set the QGraphicsItem::ItemIsMovable , which supports interactive movement with the mouse. If you click an element and drag it, the element will move with the mouse cursor. If the item has children, all children also move. If an item is part of a selection, all selected items are also moved. This feature is provided для удобства благодаря Π±Π°Π·ΠΎΠ²ΠΎΠΉ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² событий ΠΌΡ‹ΡˆΠΈ QGraphicsItem .

 import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class Widget(QWidget): def __init__(self): super().__init__() self.openImageBtn = QPushButton('&ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅', self) self.saveImageBtn = QPushButton('&Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅', self) layout = QVBoxLayout(self) layout.addWidget(self.openImageBtn) layout.addWidget(self.saveImageBtn) class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self._itemImage = None self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.widget = Widget() self.widget.openImageBtn.clicked.connect(self.showOpenFileDialog) # self.saveImageBtn.clicked.connect(self.showSaveFileDialog) self._scene = QGraphicsScene() self.view = QGraphicsView(self._scene) self.showImage('im.png') self.view.setScene(self._scene) self.layout = QHBoxLayout(self.centralWidget) self.layout.addWidget(self.view) self.layout.addWidget(self.widget) def showOpenFileDialog(self): file_name = QFileDialog.getOpenFileName( self, 'ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π°, Π²Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅', '.', 'Image Files (*.png *.jpg *.jpeg *.bmp)' )[0] if not file_name: return if self._itemImage: # Π£Π΄Π°Π»ΠΈΡ‚ΡŒ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰ΠΈΠΉ элСмСнт self._scene.removeItem(self._itemImage) del self._itemImage self.showImage(file_name) def showImage(self, file_name): self._itemImage = self._scene.addPixmap(QPixmap(file_name)) self._itemImage.setFlag(QGraphicsItem.ItemIsMovable) # <<<===== def closeEvent(self, event): """ ΠžΡ‡ΠΈΡΡ‚ΠΈΡ‚Π΅ всС элСмСнты Π² сцСнС, ΠΊΠΎΠ³Π΄Π° ΠΎΠΊΠ½ΠΎ `Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎ` """ self._scene.clear() self._itemImage = None super(MainWindow, self).closeEvent(event) if __name__ == '__main__': app = QApplication(sys.argv) mw = MainWindow() mw.resize(QSize(700, 400)) mw.show() sys.exit(app.exec_()) 

enter image description here

  • Thanks, looks much nicer. Is it possible to make item not intercept wheelEvent? And how is it better to draw on the image during user actions? - Depth