paint: File open/save, fix polygon solection, initial text.

This commit is contained in:
Martin Fitzpatrick 2018-02-10 16:46:48 +01:00
parent 0b722367e4
commit 746f754312
3 changed files with 248 additions and 47 deletions

View File

@ -602,12 +602,13 @@ class Ui_MainWindow(object):
self.colorButton_28.setObjectName("colorButton_28")
self.gridLayout_2.addWidget(self.colorButton_28, 1, 13, 1, 1)
self.horizontalLayout_2.addWidget(self.widget_2)
self.stampchoiceButton = QtWidgets.QPushButton(self.centralWidget)
self.stampchoiceButton.setMinimumSize(QtCore.QSize(78, 55))
self.stampchoiceButton.setMaximumSize(QtCore.QSize(78, 55))
self.stampchoiceButton.setText("")
self.stampchoiceButton.setObjectName("stampchoiceButton")
self.horizontalLayout_2.addWidget(self.stampchoiceButton)
self.stampnextButton = QtWidgets.QPushButton(self.centralWidget)
self.stampnextButton.setMinimumSize(QtCore.QSize(78, 55))
self.stampnextButton.setMaximumSize(QtCore.QSize(78, 55))
self.stampnextButton.setText("")
self.stampnextButton.setIconSize(QtCore.QSize(80, 50))
self.stampnextButton.setObjectName("stampnextButton")
self.horizontalLayout_2.addWidget(self.stampnextButton)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout_2)
@ -619,12 +620,8 @@ class Ui_MainWindow(object):
self.menuFIle.setObjectName("menuFIle")
self.menuEdit = QtWidgets.QMenu(self.menuBar)
self.menuEdit.setObjectName("menuEdit")
self.menuView = QtWidgets.QMenu(self.menuBar)
self.menuView.setObjectName("menuView")
self.menuImage = QtWidgets.QMenu(self.menuBar)
self.menuImage.setObjectName("menuImage")
self.menuColors = QtWidgets.QMenu(self.menuBar)
self.menuColors.setObjectName("menuColors")
self.menuHelp = QtWidgets.QMenu(self.menuBar)
self.menuHelp.setObjectName("menuHelp")
MainWindow.setMenuBar(self.menuBar)
@ -633,15 +630,35 @@ class Ui_MainWindow(object):
MainWindow.setStatusBar(self.statusBar)
self.actionCopy = QtWidgets.QAction(MainWindow)
self.actionCopy.setObjectName("actionCopy")
self.actionPaste = QtWidgets.QAction(MainWindow)
self.actionPaste.setObjectName("actionPaste")
self.actionClearImage = QtWidgets.QAction(MainWindow)
self.actionClearImage.setObjectName("actionClearImage")
self.actionClearselection = QtWidgets.QAction(MainWindow)
self.actionClearselection.setObjectName("actionClearselection")
self.actionOpenImage = QtWidgets.QAction(MainWindow)
self.actionOpenImage.setObjectName("actionOpenImage")
self.actionSaveImage = QtWidgets.QAction(MainWindow)
self.actionSaveImage.setObjectName("actionSaveImage")
self.actionInvertColors = QtWidgets.QAction(MainWindow)
self.actionInvertColors.setObjectName("actionInvertColors")
self.actionFlipHorizontal = QtWidgets.QAction(MainWindow)
self.actionFlipHorizontal.setObjectName("actionFlipHorizontal")
self.actionFlipVertical = QtWidgets.QAction(MainWindow)
self.actionFlipVertical.setObjectName("actionFlipVertical")
self.actionNewImage = QtWidgets.QAction(MainWindow)
self.actionNewImage.setObjectName("actionNewImage")
self.menuFIle.addAction(self.actionNewImage)
self.menuFIle.addAction(self.actionOpenImage)
self.menuFIle.addAction(self.actionSaveImage)
self.menuEdit.addAction(self.actionCopy)
self.menuEdit.addAction(self.actionPaste)
self.menuEdit.addSeparator()
self.menuEdit.addAction(self.actionClearImage)
self.menuImage.addAction(self.actionInvertColors)
self.menuImage.addSeparator()
self.menuImage.addAction(self.actionFlipHorizontal)
self.menuImage.addAction(self.actionFlipVertical)
self.menuBar.addAction(self.menuFIle.menuAction())
self.menuBar.addAction(self.menuEdit.menuAction())
self.menuBar.addAction(self.menuView.menuAction())
self.menuBar.addAction(self.menuImage.menuAction())
self.menuBar.addAction(self.menuColors.menuAction())
self.menuBar.addAction(self.menuHelp.menuAction())
self.retranslateUi(MainWindow)
@ -652,12 +669,16 @@ class Ui_MainWindow(object):
MainWindow.setWindowTitle(_translate("MainWindow", "Piecasso"))
self.menuFIle.setTitle(_translate("MainWindow", "FIle"))
self.menuEdit.setTitle(_translate("MainWindow", "Edit"))
self.menuView.setTitle(_translate("MainWindow", "View"))
self.menuImage.setTitle(_translate("MainWindow", "Image"))
self.menuColors.setTitle(_translate("MainWindow", "Colors"))
self.menuHelp.setTitle(_translate("MainWindow", "Help"))
self.actionCopy.setText(_translate("MainWindow", "Copy"))
self.actionCopy.setShortcut(_translate("MainWindow", "Ctrl+C"))
self.actionPaste.setText(_translate("MainWindow", "Paste"))
self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
self.actionClearImage.setText(_translate("MainWindow", "Clear Image"))
self.actionClearselection.setText(_translate("MainWindow", "Clear Selection"))
self.actionOpenImage.setText(_translate("MainWindow", "Open Image..."))
self.actionSaveImage.setText(_translate("MainWindow", "Save Image As..."))
self.actionInvertColors.setText(_translate("MainWindow", "Invert Colors"))
self.actionFlipHorizontal.setText(_translate("MainWindow", "Flip Horizontal"))
self.actionFlipVertical.setText(_translate("MainWindow", "Flip Vertical"))
self.actionNewImage.setText(_translate("MainWindow", "New Image"))

View File

@ -1345,7 +1345,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="stampchoiceButton">
<widget class="QPushButton" name="stampnextButton">
<property name="minimumSize">
<size>
<width>78</width>
@ -1361,6 +1361,12 @@
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>80</width>
<height>50</height>
</size>
</property>
</widget>
</item>
<item>
@ -1393,28 +1399,26 @@
<property name="title">
<string>FIle</string>
</property>
<addaction name="actionNewImage"/>
<addaction name="actionOpenImage"/>
<addaction name="actionSaveImage"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionCopy"/>
<addaction name="actionPaste"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="separator"/>
<addaction name="actionClearImage"/>
</widget>
<widget class="QMenu" name="menuImage">
<property name="title">
<string>Image</string>
</property>
</widget>
<widget class="QMenu" name="menuColors">
<property name="title">
<string>Colors</string>
</property>
<addaction name="actionInvertColors"/>
<addaction name="separator"/>
<addaction name="actionFlipHorizontal"/>
<addaction name="actionFlipVertical"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
@ -1423,9 +1427,7 @@
</widget>
<addaction name="menuFIle"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
<addaction name="menuImage"/>
<addaction name="menuColors"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
@ -1437,12 +1439,44 @@
<string>Ctrl+C</string>
</property>
</action>
<action name="actionPaste">
<action name="actionClearImage">
<property name="text">
<string>Paste</string>
<string>Clear Image</string>
</property>
<property name="shortcut">
<string>Ctrl+V</string>
</action>
<action name="actionClearselection">
<property name="text">
<string>Clear Selection</string>
</property>
</action>
<action name="actionOpenImage">
<property name="text">
<string>Open Image...</string>
</property>
</action>
<action name="actionSaveImage">
<property name="text">
<string>Save Image As...</string>
</property>
</action>
<action name="actionInvertColors">
<property name="text">
<string>Invert Colors</string>
</property>
</action>
<action name="actionFlipHorizontal">
<property name="text">
<string>Flip Horizontal</string>
</property>
</action>
<action name="actionFlipVertical">
<property name="text">
<string>Flip Vertical</string>
</property>
</action>
<action name="actionNewImage">
<property name="text">
<string>New Image</string>
</property>
</action>
</widget>

View File

@ -48,7 +48,6 @@ class Canvas(QLabel):
primary_color = QColor(Qt.black)
secondary_color = None
background_color = QColor(Qt.white)
primary_color_updated = pyqtSignal(str)
secondary_color_updated = pyqtSignal(str)
@ -58,13 +57,19 @@ class Canvas(QLabel):
timer_event = None
current_stamp = None
def initialize(self):
self.background_color = QColor(self.secondary_color) if self.secondary_color else QColor(Qt.white)
self.eraser_color = QColor(self.secondary_color) if self.secondary_color else QColor(Qt.white)
self.eraser_color.setAlpha(100)
self.reset()
def reset(self):
# Create the pixmap for display.
self.setPixmap(QPixmap(*CANVAS_DIMENSIONS))
self.eraser_color = self.secondary_color or QColor(Qt.white)
self.eraser_color.setAlpha(100)
# Clear the canvas.
self.pixmap().fill(self.background_color)
def set_primary_color(self, hex):
@ -79,10 +84,18 @@ class Canvas(QLabel):
# Reset mode-specific vars (all)
self.active_shape_fn = None
self.active_shape_args = ()
self.last_pos = None
self.current_pos = None
self.origin_pos = None
self.current_pos = None
self.last_pos = None
self.history_pos = None
self.last_history = []
self.current_text = ""
self.last_text = ""
self.dash_offset = 0
self.locked = False
# Apply the mode
@ -236,7 +249,7 @@ class Canvas(QLabel):
def stamp_mousePressEvent(self, e):
p = QPainter(self.pixmap())
stamp = QPixmap(random.choice(STAMPS))
stamp = self.current_stamp
p.drawPixmap(e.x() - stamp.width() // 2, e.y() - stamp.height() // 2, stamp)
self.update()
@ -291,7 +304,50 @@ class Canvas(QLabel):
self.update()
def spray_mouseReleaseEvent(self, e):
return self.generic_mouseReleaseEvent(e)
self.generic_mouseReleaseEvent(e)
# Text events
def keyPressEvent(self, e):
if self.mode == 'text':
if e.key() == Qt.Key_Backspace:
self.current_text = self.current_text[:-1]
else:
self.current_text = self.current_text + e.text()
def text_mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
self.current_pos = e.pos()
self.current_text = ""
self.timer_event = self.text_timerEvent
elif e.button() == Qt.RightButton and self.current_pos:
self.timer_cleanup()
# Draw the text to the image
p = QPainter(self.pixmap())
p.setRenderHints(QPainter.Antialiasing)
p.setFont(QFont("Times", 30))
pen = QPen(self.primary_color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
p.setPen(pen)
p.drawText(self.current_pos, self.current_text)
self.update()
self.reset_mode()
def text_timerEvent(self, final=False):
p = QPainter(self.pixmap())
p.setFont(QFont("Times", 30))
p.setCompositionMode(QPainter.RasterOp_SourceXorDestination)
pen = PREVIEW_PEN
p.setPen(pen)
if self.last_text:
p.drawText(self.current_pos, self.last_text)
if not final:
p.drawText(self.current_pos, self.current_text)
self.last_text = self.current_text
self.update()
# Fill events
@ -468,8 +524,8 @@ class Canvas(QLabel):
pen = self.preview_pen
pen.setDashOffset(self.dash_offset)
p.setPen(pen)
if self.last_pos:
getattr(p, self.active_shape_fn)(*self.history_pos + [self.last_pos])
if self.last_history:
getattr(p, self.active_shape_fn)(*self.last_history)
if not final:
self.dash_offset -= 1
@ -479,6 +535,7 @@ class Canvas(QLabel):
self.update()
self.last_pos = self.current_pos
self.last_history = self.history_pos + [self.current_pos]
def generic_poly_mouseMoveEvent(self, e):
self.current_pos = e.pos()
@ -589,9 +646,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Replace canvas placeholder from QtDesigner.
self.horizontalLayout.removeWidget(self.canvas)
self.canvas = Canvas()
self.canvas.reset()
self.canvas.initialize()
# We need to enable mouse tracking to follow the mouse without the button pressed.
self.canvas.setMouseTracking(True)
# Enable focus to capture key inputs.
self.canvas.setFocusPolicy(Qt.StrongFocus)
self.horizontalLayout.addWidget(self.canvas)
# Setup the mode buttons
@ -639,6 +698,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.canvas.primary_color_updated.connect(self.set_primary_color)
self.canvas.secondary_color_updated.connect(self.set_secondary_color)
# Setup the stamp state.
self.current_stamp_n = -1
self.next_stamp()
self.stampnextButton.pressed.connect(self.next_stamp)
# Menu options
self.actionNewImage.triggered.connect(self.canvas.initialize)
self.actionOpenImage.triggered.connect(self.open_file)
self.actionSaveImage.triggered.connect(self.save_file)
self.actionClearImage.triggered.connect(self.canvas.reset)
self.actionInvertColors.triggered.connect(self.invert)
self.actionFlipHorizontal.triggered.connect(self.flip_horizontal)
self.actionFlipVertical.triggered.connect(self.flip_vertical)
self.show()
def choose_color(self, callback):
@ -654,6 +727,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.canvas.set_secondary_color(hex)
self.secondaryButton.setStyleSheet('QPushButton { background-color: %s; }' % hex)
def next_stamp(self):
self.current_stamp_n += 1
if self.current_stamp_n >= len(STAMPS):
self.current_stamp_n = 0
pixmap = QPixmap(STAMPS[self.current_stamp_n])
self.stampnextButton.setIcon(QIcon(pixmap))
self.canvas.current_stamp = pixmap
def copy_to_clipboard(self):
clipboard = QApplication.clipboard()
@ -666,6 +749,69 @@ class MainWindow(QMainWindow, Ui_MainWindow):
else:
clipboard.setPixmap(self.canvas.pixmap())
def open_file(self):
"""
Open image file for editing, scaling the smaller dimension and cropping the remainder.
:return:
"""
path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "PNG image files (*.png); JPEG image files (*jpg); All files (*.*)")
if path:
pixmap = QPixmap()
pixmap.load(path)
# We need to crop down to the size of our canvas. Get the size of the loaded image.
iw = pixmap.width()
ih = pixmap.height()
# Get the size of the space we're filling.
cw, ch = CANVAS_DIMENSIONS
if iw/cw < ih/ch: # The height is relatively bigger than the width.
pixmap = pixmap.scaledToWidth(cw)
hoff = (pixmap.height() - ch) // 2
pixmap = pixmap.copy(
QRect(QPoint(0, hoff), QPoint(cw, pixmap.height()-hoff))
)
elif iw/cw > ih/ch: # The height is relatively bigger than the width.
pixmap = pixmap.scaledToHeight(ch)
woff = (pixmap.width() - cw) // 2
pixmap = pixmap.copy(
QRect(QPoint(woff, 0), QPoint(pixmap.width()-woff, ch))
)
self.canvas.setPixmap(pixmap)
def save_file(self):
"""
Save active canvas to image file.
:return:
"""
path, _ = QFileDialog.getSaveFileName(self, "Save file", "", "PNG Image file (*.png)")
if path:
pixmap = self.canvas.pixmap()
pixmap.save(path, "PNG" )
def invert(self):
img = QImage(self.canvas.pixmap())
img.invertPixels()
pixmap = QPixmap()
pixmap.convertFromImage(img)
self.canvas.setPixmap(pixmap)
def flip_horizontal(self):
pixmap = self.canvas.pixmap()
self.canvas.setPixmap(pixmap.transformed(QTransform().scale(-1, 1)))
def flip_vertical(self):
pixmap = self.canvas.pixmap()
self.canvas.setPixmap(pixmap.transformed(QTransform().scale(1, -1)))
if __name__ == '__main__':
app = QApplication([])