Krita Source Code Documentation
Loading...
Searching...
No Matches
photobash_images_modulo.py
Go to the documentation of this file.
1# Photobash Images is a Krita plugin to get CC0 images based on a search,
2# straight from the Krita Interface. Useful for textures and concept art!
3# Copyright (C) 2020 Pedro Reis.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18from krita import Krita
19try:
20 from PyQt6.QtGui import QPolygon, QPainter, QPen, QBrush, QImage, QPixmap, QDrag, QPalette
21 from PyQt6.QtCore import Qt, QPoint, QMimeData, QUrl, QSize, pyqtSignal
22 from PyQt6.QtWidgets import QApplication, QWidget, QMenu
23except:
24 from PyQt5.QtGui import QPolygon, QPainter, QPen, QBrush, QImage, QPixmap, QDrag, QPalette
25 from PyQt5.QtCore import Qt, QPoint, QMimeData, QUrl, QSize, pyqtSignal
26 from PyQt5.QtWidgets import QApplication, QWidget, QMenu
27
28DRAG_DELTA = 30
29TRIANGLE_SIZE = 20
30
31FAVOURITE_TRIANGLE = QPolygon([
32 QPoint(0, 0),
33 QPoint(0, TRIANGLE_SIZE),
34 QPoint(TRIANGLE_SIZE, 0)
35])
36
37def customPaintEvent(instance, event):
38 painter = QPainter(instance)
39 painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
40 painter.setPen(QPen(Qt.GlobalColor.black, 2, Qt.PenStyle.SolidLine))
41 painter.setBrush(QBrush(Qt.GlobalColor.white, Qt.BrushStyle.SolidPattern))
42
43 # Calculations
44 total_width = event.rect().width()
45 total_height = event.rect().height()
46 image_width = instance.qimage.width()
47 image_height = instance.qimage.height()
48
49 try:
50 var_w = total_width / image_width
51 var_h = total_height / image_height
52 except:
53 var_w = 1
54 var_h = 1
55
56 size = 0
57
58 if var_w <= var_h:
59 size = var_w
60 if var_w > var_h:
61 size = var_h
62
63 wt2 = total_width * 0.5
64 ht2 = total_height * 0.5
65
66 instance.scaled_width = image_width * size
67 instance.scaled_height = image_height * size
68
69 offset_x = wt2 - (instance.scaled_width * 0.5)
70 offset_y = ht2 - (instance.scaled_height * 0.5)
71
72 # Save State for Painter
73 painter.save()
74 painter.translate(offset_x, offset_y)
75 painter.scale(size, size)
76 painter.drawImage(0,0,instance.qimage)
77 # paint something if it is a favourite
78 if hasattr(instance, 'isFavourite'):
79 if instance.isFavourite:
80 # reset scale to draw favourite triangle
81 painter.scale(1/size, 1/size)
82 painter.drawPolygon(FAVOURITE_TRIANGLE)
83
84 # Restore Space
85 painter.restore()
86
87def customSetImage(instance, image):
88 instance.qimage = QImage() if image is None else image
89 instance.pixmap = QPixmap(50, 50).fromImage(instance.qimage)
90
91 instance.update()
92
93def customMouseMoveEvent(self, event):
94 if event.modifiers() != Qt.KeyboardModifier.ShiftModifier and event.modifiers() != Qt.KeyboardModifier.AltModifier:
95 self.PREVIOUS_DRAG_X = None
96 return
97
98 # alt modifier is reserved for scrolling through
99 if self.PREVIOUS_DRAG_X and event.modifiers() == Qt.KeyboardModifier.AltModifier:
100 if self.PREVIOUS_DRAG_X < event.x() - DRAG_DELTA:
101 self.SIGNAL_WUP.emit(0)
102 self.PREVIOUS_DRAG_X = event.x()
103 elif self.PREVIOUS_DRAG_X > event.x() + DRAG_DELTA:
104 self.SIGNAL_WDN.emit(0)
105 self.PREVIOUS_DRAG_X = event.x()
106
107 return
108
109 # MimeData
110 mimedata = QMimeData()
111 url = QUrl().fromLocalFile(self.path)
112 mimedata.setUrls([url])
113
114 # create appropriate res image that will placed
115 doc = Krita.instance().activeDocument()
116
117 # Saving a non-existent document causes crashes, so lets check for that first.
118 if doc is None:
119 return
120
121 scale = self.scale / 100
122
123 # only scale to document if it exists
124 if self.fitCanvasChecked and not doc is None:
125 fullImage = QImage(self.path).scaled(doc.width() * scale, doc.height() * scale, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
126 else:
127 fullImage = QImage(self.path)
128 # scale image, now knowing the bounds
129 fullImage = fullImage.scaled(fullImage.width() * scale, fullImage.height() * scale, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
130
131 fullPixmap = QPixmap(50, 50).fromImage(fullImage)
132 mimedata.setImageData(fullPixmap)
133
134 # Clipboard
135 QApplication.clipboard().setImage(self.qimage)
136
137 # drag, using information about the smaller version of the image
138 drag = QDrag(self)
139 drag.setMimeData(mimedata)
140 drag.setPixmap(self.pixmap)
141 drag.setHotSpot(QPoint(self.qimage.width() / 2, self.qimage.height() / 2))
142 drag.exec(Qt.DropAction.CopyAction)
143
144class Photobash_Display(QWidget):
145 SIGNAL_HOVER = pyqtSignal(str)
146 SIGNAL_CLOSE = pyqtSignal(int)
147 fitCanvasChecked = False
148 scale = 100
149
150 def __init__(self, parent):
151 super(Photobash_Display, self).__init__(parent)
152 customSetImage(self, None)
153
154 def sizeHint(self):
155 return QSize(5000,5000)
156
157 def enterEvent(self, event):
158 self.SIGNAL_HOVER.emit("D")
159
160 def leaveEvent(self, event):
161 self.SIGNAL_HOVER.emit("None")
162
163 def mousePressEvent(self, event):
164 if (event.modifiers() == Qt.KeyboardModifier.NoModifier and event.buttons() == Qt.MouseButton.LeftButton):
165 self.SIGNAL_CLOSE.emit(0)
166
167 def mouseMoveEvent(self, event):
168 customMouseMoveEvent(self, event)
169
170 def setFitCanvas(self, newFit):
172
173 def setImageScale(self, newScale):
174 self.scalescale = newScale
175
176 def setImage(self, path, image):
177 self.path = path
178 customSetImage(self, image)
179
180 def paintEvent(self, event):
181 customPaintEvent(self, event)
182
183class Photobash_Button(QWidget):
184 SIGNAL_HOVER = pyqtSignal(str)
185 SIGNAL_LMB = pyqtSignal(int)
186 SIGNAL_WUP = pyqtSignal(int)
187 SIGNAL_WDN = pyqtSignal(int)
188 SIGNAL_PREVIEW = pyqtSignal(str)
189 SIGNAL_FAVOURITE = pyqtSignal(str)
190 SIGNAL_UN_FAVOURITE = pyqtSignal(str)
191 SIGNAL_OPEN_NEW = pyqtSignal(str)
192 SIGNAL_REFERENCE = pyqtSignal(str)
193 SIGNAL_DRAG = pyqtSignal(int)
194 PREVIOUS_DRAG_X = None
195 fitCanvasChecked = False
196 scale = 100
197 isFavourite = False
198
199 def __init__(self, parent):
200 super(Photobash_Button, self).__init__(parent)
201 # Variables
202 self.number = -1
203 # QImage
204 customSetImage(self, None)
205
208
209 def setFavourite(self, newFavourite):
210 self.isFavouriteisFavourite = newFavourite
211
212 def setImageScale(self, newScale):
213 self.scalescale = newScale
214
215 def setFitCanvas(self, newFit):
217
218 def setNumber(self, number):
219 self.number = number
220
221 def sizeHint(self):
222 return QSize(2000,2000)
223
224 def enterEvent(self, event):
225 self.SIGNAL_HOVER.emit(str(self.number))
226
227 def leaveEvent(self, event):
228 self.SIGNAL_HOVER.emit("None")
229
230 def mousePressEvent(self, event):
231 if event.modifiers() == Qt.KeyboardModifier.NoModifier and event.buttons() == Qt.MouseButton.LeftButton:
232 self.SIGNAL_LMB.emit(self.number)
233 if event.modifiers() == Qt.KeyboardModifier.AltModifier:
234 self.PREVIOUS_DRAG_X = event.x()
235
236 def mouseDoubleClickEvent(self, event):
237 # Prevent double click to open the same image twice
238 pass
239
240 def mouseMoveEvent(self, event):
241 customMouseMoveEvent(self, event)
242
243 def wheelEvent(self,event):
244 delta = event.angleDelta()
245 if delta.y() > 20:
246 self.SIGNAL_WUP.emit(0)
247 elif delta.y() < -20:
248 self.SIGNAL_WDN.emit(0)
249
250 # menu opened with right click
251 def contextMenuEvent(self, event):
252 cmenu = QMenu(self)
253
254 cmenuDisplay = cmenu.addAction("Preview in Docker")
255 favouriteString = "Unpin" if self.isFavouriteisFavourite else "Pin to Beginning"
256 cmenuFavourite = cmenu.addAction(favouriteString)
257 cmenuOpenNew = cmenu.addAction("Open as New Document")
258 cmenuReference = cmenu.addAction("Place as Reference")
259
260 background = QApplication().instance().palette().color(QPalette.ColorRole.Window).name().split("#")[1]
261 cmenuStyleSheet = f"""QMenu {{ background-color: #AA{background}; border: 1px solid #{background}; }}"""
262 cmenu.setStyleSheet(cmenuStyleSheet)
263
264 action = cmenu.exec(self.mapToGlobal(event.pos()))
265 if action == cmenuDisplay:
266 self.SIGNAL_PREVIEW.emit(self.path)
267 if action == cmenuFavourite:
269 self.SIGNAL_UN_FAVOURITE.emit(self.path)
270 else:
271 self.SIGNAL_FAVOURITE.emit(self.path)
272 if action == cmenuOpenNew:
273 self.SIGNAL_OPEN_NEW.emit(self.path)
274 if action == cmenuReference:
275 self.SIGNAL_REFERENCE.emit(self.path)
276
277 def setImage(self, path, image):
278 self.path = path
279 customSetImage(self, image)
280
281 def paintEvent(self, event):
282 customPaintEvent(self, event)
PythonPluginManager * instance
static Krita * instance()
instance retrieve the singleton instance of the Application object.
Definition Krita.cpp:390
rgba palette[MAX_PALETTE]
Definition palette.c:35