File indexing completed on 2024-09-22 04:08:47

0001 # Photobash Images is a Krita plugin to get CC0 images based on a search,
0002 # straight from the Krita Interface. Useful for textures and concept art!
0003 # Copyright (C) 2020  Pedro Reis.
0004 #
0005 # This program is free software: you can redistribute it and/or modify
0006 # it under the terms of the GNU General Public License as published by
0007 # the Free Software Foundation, either version 3 of the License, or
0008 # (at your option) any later version.
0009 #
0010 # This program is distributed in the hope that it will be useful,
0011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 # GNU General Public License for more details.
0014 #
0015 # You should have received a copy of the GNU General Public License
0016 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 
0018 from krita import *
0019 from PyQt5 import QtWidgets, QtCore
0020 
0021 DRAG_DELTA = 30
0022 TRIANGLE_SIZE = 20
0023 
0024 FAVOURITE_TRIANGLE = QPolygon([
0025     QPoint(0, 0),
0026     QPoint(0, TRIANGLE_SIZE),
0027     QPoint(TRIANGLE_SIZE, 0)
0028 ])
0029 
0030 def customPaintEvent(instance, event):
0031     painter = QPainter(instance)
0032     painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
0033     painter.setPen(QPen(Qt.black, 2, Qt.SolidLine))
0034     painter.setBrush(QBrush(Qt.white, Qt.SolidPattern))
0035 
0036     # Calculations
0037     total_width = event.rect().width()
0038     total_height = event.rect().height()
0039     image_width = instance.qimage.width()
0040     image_height = instance.qimage.height()
0041 
0042     try:
0043         var_w = total_width / image_width
0044         var_h = total_height / image_height
0045     except:
0046         var_w = 1
0047         var_h = 1
0048 
0049     size = 0
0050 
0051     if var_w <= var_h:
0052         size = var_w
0053     if var_w > var_h:
0054         size = var_h
0055 
0056     wt2 = total_width * 0.5
0057     ht2 = total_height * 0.5
0058 
0059     instance.scaled_width = image_width * size
0060     instance.scaled_height = image_height * size
0061 
0062     offset_x = wt2 - (instance.scaled_width * 0.5)
0063     offset_y = ht2 - (instance.scaled_height * 0.5)
0064 
0065     # Save State for Painter
0066     painter.save()
0067     painter.translate(offset_x, offset_y)
0068     painter.scale(size, size)
0069     painter.drawImage(0,0,instance.qimage)
0070     # paint something if it is a favourite
0071     if hasattr(instance, 'isFavourite'):
0072         if instance.isFavourite: 
0073             # reset scale to draw favourite triangle
0074             painter.scale(1/size, 1/size)
0075             painter.drawPolygon(FAVOURITE_TRIANGLE)
0076 
0077     # Restore Space
0078     painter.restore()
0079 
0080 def customSetImage(instance, image):
0081     instance.qimage = QImage() if image is None else image
0082     instance.pixmap = QPixmap(50, 50).fromImage(instance.qimage)
0083 
0084     instance.update()
0085 
0086 def customMouseMoveEvent(self, event):
0087     if event.modifiers() != QtCore.Qt.ShiftModifier and event.modifiers() != QtCore.Qt.AltModifier:
0088         self.PREVIOUS_DRAG_X = None
0089         return 
0090 
0091     # alt modifier is reserved for scrolling through
0092     if self.PREVIOUS_DRAG_X and event.modifiers() == QtCore.Qt.AltModifier:
0093         if self.PREVIOUS_DRAG_X < event.x() - DRAG_DELTA:
0094             self.SIGNAL_WUP.emit(0)
0095             self.PREVIOUS_DRAG_X = event.x()
0096         elif self.PREVIOUS_DRAG_X > event.x() + DRAG_DELTA:
0097             self.SIGNAL_WDN.emit(0)
0098             self.PREVIOUS_DRAG_X = event.x()
0099 
0100         return 
0101 
0102     # MimeData
0103     mimedata = QMimeData()
0104     url = QUrl().fromLocalFile(self.path)
0105     mimedata.setUrls([url])
0106 
0107     # create appropriate res image that will placed
0108     doc = Krita.instance().activeDocument()
0109 
0110     # Saving a non-existent document causes crashes, so lets check for that first.
0111     if doc is None:
0112         return 
0113 
0114     scale = self.scale / 100
0115 
0116     # only scale to document if it exists
0117     if self.fitCanvasChecked and not doc is None:
0118         fullImage = QImage(self.path).scaled(doc.width() * scale, doc.height() * scale, Qt.KeepAspectRatio, Qt.SmoothTransformation)
0119     else:
0120         fullImage = QImage(self.path)
0121         # scale image, now knowing the bounds
0122         fullImage = fullImage.scaled(fullImage.width() * scale, fullImage.height() * scale, Qt.KeepAspectRatio, Qt.SmoothTransformation)
0123 
0124     fullPixmap = QPixmap(50, 50).fromImage(fullImage)
0125     mimedata.setImageData(fullPixmap)
0126 
0127     # Clipboard
0128     QApplication.clipboard().setImage(self.qimage)
0129 
0130     # drag, using information about the smaller version of the image
0131     drag = QDrag(self)
0132     drag.setMimeData(mimedata)
0133     drag.setPixmap(self.pixmap)
0134     drag.setHotSpot(QPoint(self.qimage.width() / 2, self.qimage.height() / 2))
0135     drag.exec_(Qt.CopyAction)
0136 
0137 class Photobash_Display(QWidget):
0138     SIGNAL_HOVER = QtCore.pyqtSignal(str)
0139     SIGNAL_CLOSE = QtCore.pyqtSignal(int)
0140     fitCanvasChecked = False
0141     scale = 100
0142 
0143     def __init__(self, parent):
0144         super(Photobash_Display, self).__init__(parent)
0145         customSetImage(self, None)
0146 
0147     def sizeHint(self):
0148         return QtCore.QSize(5000,5000)
0149 
0150     def enterEvent(self, event):
0151         self.SIGNAL_HOVER.emit("D")
0152 
0153     def leaveEvent(self, event):
0154         self.SIGNAL_HOVER.emit("None")
0155 
0156     def mousePressEvent(self, event):
0157         if (event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton):
0158             self.SIGNAL_CLOSE.emit(0)
0159 
0160     def mouseMoveEvent(self, event):
0161         customMouseMoveEvent(self, event)
0162 
0163     def setFitCanvas(self, newFit):
0164         self.fitCanvasChecked = newFit
0165 
0166     def setImageScale(self, newScale):
0167         self.scale = newScale
0168 
0169     def setImage(self, path, image):
0170         self.path = path
0171         customSetImage(self, image)
0172 
0173     def paintEvent(self, event):
0174         customPaintEvent(self, event)
0175 
0176 class Photobash_Button(QWidget):
0177     SIGNAL_HOVER = QtCore.pyqtSignal(str)
0178     SIGNAL_LMB = QtCore.pyqtSignal(int)
0179     SIGNAL_WUP = QtCore.pyqtSignal(int)
0180     SIGNAL_WDN = QtCore.pyqtSignal(int)
0181     SIGNAL_PREVIEW = QtCore.pyqtSignal(str)
0182     SIGNAL_FAVOURITE = QtCore.pyqtSignal(str)
0183     SIGNAL_UN_FAVOURITE = QtCore.pyqtSignal(str)
0184     SIGNAL_OPEN_NEW = QtCore.pyqtSignal(str)
0185     SIGNAL_REFERENCE = QtCore.pyqtSignal(str)
0186     SIGNAL_DRAG = QtCore.pyqtSignal(int)
0187     PREVIOUS_DRAG_X = None
0188     fitCanvasChecked = False
0189     scale = 100
0190     isFavourite = False
0191 
0192     def __init__(self, parent):
0193         super(Photobash_Button, self).__init__(parent)
0194         # Variables
0195         self.number = -1
0196         # QImage
0197         customSetImage(self, None)
0198 
0199         self.scaled_width = 1
0200         self.scaled_height = 1
0201 
0202     def setFavourite(self, newFavourite):
0203         self.isFavourite = newFavourite
0204 
0205     def setImageScale(self, newScale):
0206         self.scale = newScale
0207 
0208     def setFitCanvas(self, newFit):
0209         self.fitCanvasChecked = newFit
0210 
0211     def setNumber(self, number):
0212         self.number = number
0213 
0214     def sizeHint(self):
0215         return QtCore.QSize(2000,2000)
0216 
0217     def enterEvent(self, event):
0218         self.SIGNAL_HOVER.emit(str(self.number))
0219 
0220     def leaveEvent(self, event):
0221         self.SIGNAL_HOVER.emit("None")
0222 
0223     def mousePressEvent(self, event):
0224         if event.modifiers() == QtCore.Qt.NoModifier and event.buttons() == QtCore.Qt.LeftButton:
0225             self.SIGNAL_LMB.emit(self.number)
0226         if event.modifiers() == QtCore.Qt.AltModifier:
0227             self.PREVIOUS_DRAG_X = event.x()
0228 
0229     def mouseDoubleClickEvent(self, event):
0230         # Prevent double click to open the same image twice
0231         pass
0232 
0233     def mouseMoveEvent(self, event):
0234         customMouseMoveEvent(self, event)
0235 
0236     def wheelEvent(self,event):
0237         delta = event.angleDelta()
0238         if delta.y() > 20:
0239             self.SIGNAL_WUP.emit(0)
0240         elif delta.y() < -20:
0241             self.SIGNAL_WDN.emit(0)
0242 
0243     # menu opened with right click
0244     def contextMenuEvent(self, event):
0245         cmenu = QMenu(self)
0246 
0247         cmenuDisplay = cmenu.addAction("Preview in Docker")
0248         favouriteString = "Unpin" if self.isFavourite else "Pin to Beginning"
0249         cmenuFavourite = cmenu.addAction(favouriteString)
0250         cmenuOpenNew = cmenu.addAction("Open as New Document")
0251         cmenuReference = cmenu.addAction("Place as Reference")
0252 
0253         background = qApp.palette().color(QPalette.Window).name().split("#")[1]
0254         cmenuStyleSheet = f"""QMenu {{ background-color: #AA{background}; border: 1px solid #{background}; }}"""
0255         cmenu.setStyleSheet(cmenuStyleSheet)
0256 
0257         action = cmenu.exec_(self.mapToGlobal(event.pos()))
0258         if action == cmenuDisplay:
0259             self.SIGNAL_PREVIEW.emit(self.path)
0260         if action == cmenuFavourite:
0261             if self.isFavourite:
0262                 self.SIGNAL_UN_FAVOURITE.emit(self.path)
0263             else:
0264                 self.SIGNAL_FAVOURITE.emit(self.path)
0265         if action == cmenuOpenNew:
0266             self.SIGNAL_OPEN_NEW.emit(self.path)
0267         if action == cmenuReference:
0268             self.SIGNAL_REFERENCE.emit(self.path)
0269 
0270     def setImage(self, path, image):
0271         self.path = path
0272         customSetImage(self, image)
0273 
0274     def paintEvent(self, event):
0275         customPaintEvent(self, event)