2011-06-30 44 views
1

我想在PYQT中實現像繪畫一樣的程序。我試圖在PYQT包中使用Scribble示例代碼,該代碼可以在C:\ Python26 \ Lib \ site-packages \ PyQt4 \ examples \ widgets中找到。現在關於這個我有兩個問題:使用PYQT繪畫

  1. 在Scribble程序中,當繪畫時,當您持着鼠標按鈕和塗鴉時,繪畫不會實時更新。我發現這個問題來自於功能drawLineTo從類ScribbleArea,行:

self.update(QtCore.QRect(self.lastPoint, endPoint).normalized().adjusted(-rad, -rad, +rad, +rad))

現在,如果我只是用

self.update() 

問題更換此行是解決了,但光標不在確切的位置。

你知道我可以在self.update()中添加哪些參數來解決這兩個問題嗎?

  1. 我想作爲塗鴉確實打開圖像並在其上作畫,然後只保存漆空白讓說,白色背景(沒有回地面原始圖像)。你能告訴我如何做到這一點?

我會感謝您對任一問題的回答。

謝謝!

回答

3

剛學PyQt的,但我修改了代碼照顧光標不匹配問題(並修改了openImage方法也保持圖像的大小同步與窗口):

#!/usr/bin/env python 


############################################################################# 
## 
## Copyright (C) 2010 Riverbank Computing Limited. 
## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 
## All rights reserved. 
## 
## This file is part of the examples of PyQt. 
## 
## $QT_BEGIN_LICENSE:BSD$ 
## You may use this file under the terms of the BSD license as follows: 
## 
## "Redistribution and use in source and binary forms, with or without 
## modification, are permitted provided that the following conditions are 
## met: 
## * Redistributions of source code must retain the above copyright 
##  notice, this list of conditions and the following disclaimer. 
## * Redistributions in binary form must reproduce the above copyright 
##  notice, this list of conditions and the following disclaimer in 
##  the documentation and/or other materials provided with the 
##  distribution. 
## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 
##  the names of its contributors may be used to endorse or promote 
##  products derived from this software without specific prior written 
##  permission. 
## 
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 
## $QT_END_LICENSE$ 
## 
############################################################################# 


# These are only needed for Python v2 but are harmless for Python v3. 
import sip 
sip.setapi('QString', 2) 
sip.setapi('QVariant', 2) 

from PyQt4 import QtCore, QtGui 


class ScribbleArea(QtGui.QWidget): 
    """ 
     this scales the image but it's not good, too many refreshes really mess it up!!! 
    """ 
    def __init__(self, parent=None): 
     super(ScribbleArea, self).__init__(parent) 

     self.setAttribute(QtCore.Qt.WA_StaticContents) 
     self.modified = False 
     self.scribbling = False 
     self.myPenWidth = 1 
     self.myPenColor = QtCore.Qt.blue 
     imageSize = QtCore.QSize(500, 500) 
#  self.image = QtGui.QImage() 
     self.image = QtGui.QImage(imageSize, QtGui.QImage.Format_RGB32) 
     self.lastPoint = QtCore.QPoint() 

    def openImage(self, fileName): 
     loadedImage = QtGui.QImage() 
     if not loadedImage.load(fileName): 
      return False 

     w = loadedImage.width() 
     h = loadedImage.height()  
     self.mainWindow.resize(w, h) 

#  newSize = loadedImage.size().expandedTo(self.size()) 
#  self.resizeImage(loadedImage, newSize) 
     self.image = loadedImage 
     self.modified = False 
     self.update() 
     return True 

    def saveImage(self, fileName, fileFormat): 
     visibleImage = self.image 
     self.resizeImage(visibleImage, self.size()) 

     if visibleImage.save(fileName, fileFormat): 
      self.modified = False 
      return True 
     else: 
      return False 

    def setPenColor(self, newColor): 
     self.myPenColor = newColor 

    def setPenWidth(self, newWidth): 
     self.myPenWidth = newWidth 

    def clearImage(self): 
     self.image.fill(QtGui.qRgb(255, 255, 255)) 
     self.modified = True 
     self.update() 

    def mousePressEvent(self, event): 
#  print "self.image.width() = %d" % self.image.width() 
#  print "self.image.height() = %d" % self.image.height() 
#  print "self.image.size() = %s" % self.image.size() 
#  print "self.size() = %s" % self.size() 
#  print "event.pos() = %s" % event.pos() 
     if event.button() == QtCore.Qt.LeftButton: 
      self.lastPoint = event.pos() 
      self.scribbling = True 

    def mouseMoveEvent(self, event): 
     if (event.buttons() & QtCore.Qt.LeftButton) and self.scribbling: 
      self.drawLineTo(event.pos()) 

    def mouseReleaseEvent(self, event): 
     if event.button() == QtCore.Qt.LeftButton and self.scribbling: 
      self.drawLineTo(event.pos()) 
      self.scribbling = False 

    def paintEvent(self, event): 
     painter = QtGui.QPainter(self) 
     painter.drawImage(event.rect(), self.image) 

    def resizeEvent(self, event): 
#  print "resize event" 
#  print "event = %s" % event 
#  print "event.oldSize() = %s" % event.oldSize() 
#  print "event.size() = %s" % event.size() 

     self.resizeImage(self.image, event.size()) 

#  if self.width() > self.image.width() or self.height() > self.image.height(): 
#   newWidth = max(self.width() + 128, self.image.width()) 
#   newHeight = max(self.height() + 128, self.image.height()) 
#   print "newWidth = %d, newHeight = %d" % (newWidth, newHeight) 
#   self.resizeImage(self.image, QtCore.QSize(newWidth, newHeight)) 
#   self.update() 

     super(ScribbleArea, self).resizeEvent(event) 

    def drawLineTo(self, endPoint): 
     painter = QtGui.QPainter(self.image) 
     painter.setPen(QtGui.QPen(self.myPenColor, self.myPenWidth, 
      QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) 
     painter.drawLine(self.lastPoint, endPoint) 
     self.modified = True 

     # rad = self.myPenWidth/2 + 2 
     # self.update(QtCore.QRect(self.lastPoint, endPoint).normalized().adjusted(-rad, -rad, +rad, +rad)) 
     self.update() 
     self.lastPoint = QtCore.QPoint(endPoint) 

    def resizeImage(self, image, newSize): 
     if image.size() == newSize: 
      return 

#  print "image.size() = %s" % repr(image.size()) 
#  print "newSize = %s" % newSize 

# this resizes the canvas without resampling the image 
     newImage = QtGui.QImage(newSize, QtGui.QImage.Format_RGB32) 
     newImage.fill(QtGui.qRgb(255, 255, 255)) 
     painter = QtGui.QPainter(newImage) 
     painter.drawImage(QtCore.QPoint(0, 0), image) 


## this resampled the image but it gets messed up with so many events...  
##  painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True) 
##  painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True) 
#  
#  newImage = QtGui.QImage(newSize, QtGui.QImage.Format_RGB32) 
#  newImage.fill(QtGui.qRgb(255, 255, 255)) 
#  painter = QtGui.QPainter(newImage) 
#  srcRect = QtCore.QRect(QtCore.QPoint(0,0), image.size()) 
#  dstRect = QtCore.QRect(QtCore.QPoint(0,0), newSize) 
##  print "srcRect = %s" % srcRect 
##  print "dstRect = %s" % dstRect 
#  painter.drawImage(dstRect, image, srcRect) 


     self.image = newImage 

    def print_(self): 
     printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution) 

     printDialog = QtGui.QPrintDialog(printer, self) 
     if printDialog.exec_() == QtGui.QDialog.Accepted: 
      painter = QtGui.QPainter(printer) 
      rect = painter.viewport() 
      size = self.image.size() 
      size.scale(rect.size(), QtCore.Qt.KeepAspectRatio) 
      painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) 
      painter.setWindow(self.image.rect()) 
      painter.drawImage(0, 0, self.image) 
      painter.end() 

    def isModified(self): 
     return self.modified 

    def penColor(self): 
     return self.myPenColor 

    def penWidth(self): 
     return self.myPenWidth 


class MainWindow(QtGui.QMainWindow): 
    def __init__(self): 
     super(MainWindow, self).__init__() 

     self.saveAsActs = [] 

     self.scribbleArea = ScribbleArea(self) 
     self.scribbleArea.clearImage() 
     self.scribbleArea.mainWindow = self # maybe not using this? 
     self.setCentralWidget(self.scribbleArea) 

     self.createActions() 
     self.createMenus() 

     self.setWindowTitle("Scribble") 
     self.resize(500, 500) 

    def closeEvent(self, event): 
     if self.maybeSave(): 
      event.accept() 
     else: 
      event.ignore() 

    def open(self): 
     if self.maybeSave(): 
      fileName = QtGui.QFileDialog.getOpenFileName(self, "Open File", 
       QtCore.QDir.currentPath()) 
      if fileName: 
       self.scribbleArea.openImage(fileName) 

    def save(self): 
     action = self.sender() 
     fileFormat = action.data() 
     self.saveFile(fileFormat) 

    def penColor(self): 
     newColor = QtGui.QColorDialog.getColor(self.scribbleArea.penColor()) 
     if newColor.isValid(): 
      self.scribbleArea.setPenColor(newColor) 

    def penWidth(self): 
     newWidth, ok = QtGui.QInputDialog.getInteger(self, "Scribble", 
      "Select pen width:", self.scribbleArea.penWidth(), 1, 50, 1) 
     if ok: 
      self.scribbleArea.setPenWidth(newWidth) 

    def about(self): 
     QtGui.QMessageBox.about(self, "About Scribble", 
      "<p>The <b>Scribble</b> example shows how to use " 
      "QMainWindow as the base widget for an application, and how " 
      "to reimplement some of QWidget's event handlers to receive " 
      "the events generated for the application's widgets:</p>" 
      "<p> We reimplement the mouse event handlers to facilitate " 
      "drawing, the paint event handler to update the application " 
      "and the resize event handler to optimize the application's " 
      "appearance. In addition we reimplement the close event " 
      "handler to intercept the close events before terminating " 
      "the application.</p>" 
      "<p> The example also demonstrates how to use QPainter to " 
      "draw an image in real time, as well as to repaint " 
      "widgets.</p>") 

    def createActions(self): 
     self.openAct = QtGui.QAction("&Open...", self, shortcut="Ctrl+O", 
      triggered=self.open) 

     for format in QtGui.QImageWriter.supportedImageFormats(): 
      format = str(format) 

      text = format.upper() + "..." 

      action = QtGui.QAction(text, self, triggered=self.save) 
      action.setData(format) 
      self.saveAsActs.append(action) 

     self.printAct = QtGui.QAction("&Print...", self, 
      triggered=self.scribbleArea.print_) 

     self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", 
      triggered=self.close) 

     self.penColorAct = QtGui.QAction("&Pen Color...", self, 
      triggered=self.penColor) 

     self.penWidthAct = QtGui.QAction("Pen &Width...", self, 
      triggered=self.penWidth) 

     self.clearScreenAct = QtGui.QAction("&Clear Screen", self, 
      shortcut="Ctrl+L", triggered=self.scribbleArea.clearImage) 

     self.aboutAct = QtGui.QAction("&About", self, triggered=self.about) 

     self.aboutQtAct = QtGui.QAction("About &Qt", self, 
      triggered=QtGui.qApp.aboutQt) 

    def createMenus(self): 
     self.saveAsMenu = QtGui.QMenu("&Save As", self) 
     for action in self.saveAsActs: 
      self.saveAsMenu.addAction(action) 

     fileMenu = QtGui.QMenu("&File", self) 
     fileMenu.addAction(self.openAct) 
     fileMenu.addMenu(self.saveAsMenu) 
     fileMenu.addAction(self.printAct) 
     fileMenu.addSeparator() 
     fileMenu.addAction(self.exitAct) 

     optionMenu = QtGui.QMenu("&Options", self) 
     optionMenu.addAction(self.penColorAct) 
     optionMenu.addAction(self.penWidthAct) 
     optionMenu.addSeparator() 
     optionMenu.addAction(self.clearScreenAct) 

     helpMenu = QtGui.QMenu("&Help", self) 
     helpMenu.addAction(self.aboutAct) 
     helpMenu.addAction(self.aboutQtAct) 

     self.menuBar().addMenu(fileMenu) 
     self.menuBar().addMenu(optionMenu) 
     self.menuBar().addMenu(helpMenu) 

    def maybeSave(self): 
     if self.scribbleArea.isModified(): 
      ret = QtGui.QMessageBox.warning(self, "Scribble", 
       "The image has been modified.\n" 
       "Do you want to save your changes?", 
       QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | 
       QtGui.QMessageBox.Cancel) 
      if ret == QtGui.QMessageBox.Save: 
       return self.saveFile('png') 
      elif ret == QtGui.QMessageBox.Cancel: 
       return False 

     return True 

    def saveFile(self, fileFormat): 
     initialPath = QtCore.QDir.currentPath() + '/untitled.' + fileFormat 

     fileName = QtGui.QFileDialog.getSaveFileName(self, "Save As", 
      initialPath, 
      "%s Files (*.%s);;All Files (*)" % (fileFormat.upper(), fileFormat)) 
     if fileName: 
      return self.scribbleArea.saveImage(fileName, fileFormat) 

     return False 


if __name__ == '__main__': 

    import sys 

    app = QtGui.QApplication(sys.argv) 
    window = MainWindow() 
    window.show() 
    sys.exit(app.exec_()) 
+0

忽略三重報價在ScribbleArea的類定義的開頭註釋,該代碼在resizeImage方法中被註釋掉。 – jbillfinger

+0

在ScribbleArea的__init__方法中,我將圖像大小硬編碼爲500到500,以匹配窗口__init__方法中窗口大小的硬編碼設置。從窗口大小獲取圖像大小可能會更好。 – jbillfinger