29 |
30 |
30 |
31 |
31 class SnapshotWaylandGrabber(QObject): |
32 class SnapshotWaylandGrabber(QObject): |
32 """ |
33 """ |
33 Class implementing a grabber object for non-Wayland desktops. |
34 Class implementing a grabber object for non-Wayland desktops. |
34 |
35 |
35 @signal grabbed(QPixmap) emitted after the grab operation is finished |
36 @signal grabbed(QPixmap) emitted after the grab operation is finished |
36 """ |
37 """ |
|
38 |
37 grabbed = pyqtSignal(QPixmap) |
39 grabbed = pyqtSignal(QPixmap) |
38 |
40 |
39 def __init__(self, parent=None): |
41 def __init__(self, parent=None): |
40 """ |
42 """ |
41 Constructor |
43 Constructor |
42 |
44 |
43 @param parent reference to the parent object |
45 @param parent reference to the parent object |
44 @type QObject |
46 @type QObject |
45 """ |
47 """ |
46 super().__init__(parent) |
48 super().__init__(parent) |
47 |
49 |
48 from .SnapshotTimer import SnapshotTimer |
50 from .SnapshotTimer import SnapshotTimer |
|
51 |
49 self.__grabTimer = SnapshotTimer() |
52 self.__grabTimer = SnapshotTimer() |
50 self.__grabTimer.timeout.connect(self.__performGrab) |
53 self.__grabTimer.timeout.connect(self.__performGrab) |
51 |
54 |
52 def supportedModes(self): |
55 def supportedModes(self): |
53 """ |
56 """ |
54 Public method to get the supported screenshot modes. |
57 Public method to get the supported screenshot modes. |
55 |
58 |
56 @return tuple of supported screenshot modes |
59 @return tuple of supported screenshot modes |
57 @rtype tuple of SnapshotModes |
60 @rtype tuple of SnapshotModes |
58 """ |
61 """ |
59 if DBusAvailable and Globals.isKdeDesktop(): |
62 if DBusAvailable and Globals.isKdeDesktop(): |
60 # __IGNORE_WARNING_Y114__ |
63 # __IGNORE_WARNING_Y114__ |
90 """ |
92 """ |
91 if not DBusAvailable: |
93 if not DBusAvailable: |
92 # just to play it safe |
94 # just to play it safe |
93 self.grabbed.emit(QPixmap()) |
95 self.grabbed.emit(QPixmap()) |
94 return |
96 return |
95 |
97 |
96 self.__mode = mode |
98 self.__mode = mode |
97 self.__captureCursor = captureCursor |
99 self.__captureCursor = captureCursor |
98 self.__captureDecorations = captureDecorations |
100 self.__captureDecorations = captureDecorations |
99 if delay: |
101 if delay: |
100 self.__grabTimer.start(delay) |
102 self.__grabTimer.start(delay) |
101 else: |
103 else: |
102 QTimer.singleShot(200, self.__performGrab) |
104 QTimer.singleShot(200, self.__performGrab) |
103 |
105 |
104 def __performGrab(self): |
106 def __performGrab(self): |
105 """ |
107 """ |
106 Private method to perform the grab operations. |
108 Private method to perform the grab operations. |
107 |
109 |
108 @exception RuntimeError raised to indicate an unsupported grab mode |
110 @exception RuntimeError raised to indicate an unsupported grab mode |
109 """ |
111 """ |
110 if self.__mode not in ( |
112 if self.__mode not in ( |
111 SnapshotModes.FULLSCREEN, SnapshotModes.SELECTEDSCREEN, |
113 SnapshotModes.FULLSCREEN, |
112 SnapshotModes.SELECTEDWINDOW, SnapshotModes.RECTANGLE, |
114 SnapshotModes.SELECTEDSCREEN, |
|
115 SnapshotModes.SELECTEDWINDOW, |
|
116 SnapshotModes.RECTANGLE, |
113 ): |
117 ): |
114 raise RuntimeError("unsupported grab mode given") |
118 raise RuntimeError("unsupported grab mode given") |
115 |
119 |
116 if self.__mode == SnapshotModes.FULLSCREEN: |
120 if self.__mode == SnapshotModes.FULLSCREEN: |
117 self.__grabFullscreen() |
121 self.__grabFullscreen() |
118 elif self.__mode == SnapshotModes.SELECTEDSCREEN: |
122 elif self.__mode == SnapshotModes.SELECTEDSCREEN: |
119 self.__grabSelectedScreen() |
123 self.__grabSelectedScreen() |
120 elif self.__mode == SnapshotModes.SELECTEDWINDOW: |
124 elif self.__mode == SnapshotModes.SELECTEDWINDOW: |
121 self.__grabSelectedWindow() |
125 self.__grabSelectedWindow() |
122 else: |
126 else: |
123 self.__grabRectangle() |
127 self.__grabRectangle() |
124 |
128 |
125 def __grabFullscreen(self): |
129 def __grabFullscreen(self): |
126 """ |
130 """ |
127 Private method to grab the complete desktop. |
131 Private method to grab the complete desktop. |
128 """ |
132 """ |
129 snapshot = QPixmap() |
133 snapshot = QPixmap() |
130 |
134 |
131 if Globals.isKdeDesktop(): |
135 if Globals.isKdeDesktop(): |
132 interface = QDBusInterface( |
136 interface = QDBusInterface( |
133 "org.kde.KWin", |
137 "org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot" |
134 "/Screenshot", |
138 ) |
135 "org.kde.kwin.Screenshot" |
139 reply = interface.call("screenshotFullscreen", self.__captureCursor) |
136 ) |
|
137 reply = interface.call( |
|
138 "screenshotFullscreen", |
|
139 self.__captureCursor |
|
140 ) |
|
141 if self.__checkReply(reply, 1): |
140 if self.__checkReply(reply, 1): |
142 filename = reply.arguments()[0] |
141 filename = reply.arguments()[0] |
143 if filename: |
142 if filename: |
144 snapshot = QPixmap(filename) |
143 snapshot = QPixmap(filename) |
145 with contextlib.suppress(OSError): |
144 with contextlib.suppress(OSError): |
147 elif Globals.isGnomeDesktop(): |
146 elif Globals.isGnomeDesktop(): |
148 path = self.__temporaryFilename() |
147 path = self.__temporaryFilename() |
149 interface = QDBusInterface( |
148 interface = QDBusInterface( |
150 "org.gnome.Shell", |
149 "org.gnome.Shell", |
151 "/org/gnome/Shell/Screenshot", |
150 "/org/gnome/Shell/Screenshot", |
152 "org.gnome.Shell.Screenshot" |
151 "org.gnome.Shell.Screenshot", |
153 ) |
152 ) |
154 reply = interface.call( |
153 reply = interface.call("Screenshot", self.__captureCursor, False, path) |
155 "Screenshot", |
|
156 self.__captureCursor, |
|
157 False, |
|
158 path |
|
159 ) |
|
160 if self.__checkReply(reply, 2): |
154 if self.__checkReply(reply, 2): |
161 filename = reply.arguments()[1] |
155 filename = reply.arguments()[1] |
162 if filename: |
156 if filename: |
163 snapshot = QPixmap(filename) |
157 snapshot = QPixmap(filename) |
164 with contextlib.suppress(OSError): |
158 with contextlib.suppress(OSError): |
165 os.remove(filename) |
159 os.remove(filename) |
166 |
160 |
167 self.grabbed.emit(snapshot) |
161 self.grabbed.emit(snapshot) |
168 |
162 |
169 def __grabSelectedScreen(self): |
163 def __grabSelectedScreen(self): |
170 """ |
164 """ |
171 Private method to grab a selected screen. |
165 Private method to grab a selected screen. |
172 """ |
166 """ |
173 snapshot = QPixmap() |
167 snapshot = QPixmap() |
174 |
168 |
175 if Globals.isKdeDesktop(): |
169 if Globals.isKdeDesktop(): |
176 screen = QApplication.screenAt(QCursor.pos()) |
170 screen = QApplication.screenAt(QCursor.pos()) |
177 try: |
171 try: |
178 screenId = QApplication.screens().index(screen) |
172 screenId = QApplication.screens().index(screen) |
179 except ValueError: |
173 except ValueError: |
180 # default to screen 0 |
174 # default to screen 0 |
181 screenId = 0 |
175 screenId = 0 |
182 |
176 |
183 # Step 2: grab the screen |
177 # Step 2: grab the screen |
184 interface = QDBusInterface( |
178 interface = QDBusInterface( |
185 "org.kde.KWin", |
179 "org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot" |
186 "/Screenshot", |
180 ) |
187 "org.kde.kwin.Screenshot" |
181 reply = interface.call("screenshotScreen", screenId, self.__captureCursor) |
188 ) |
|
189 reply = interface.call( |
|
190 "screenshotScreen", |
|
191 screenId, |
|
192 self.__captureCursor |
|
193 ) |
|
194 if self.__checkReply(reply, 1): |
182 if self.__checkReply(reply, 1): |
195 filename = reply.arguments()[0] |
183 filename = reply.arguments()[0] |
196 if filename: |
184 if filename: |
197 snapshot = QPixmap(filename) |
185 snapshot = QPixmap(filename) |
198 with contextlib.suppress(OSError): |
186 with contextlib.suppress(OSError): |
201 # Step 1: grab entire desktop |
189 # Step 1: grab entire desktop |
202 path = self.__temporaryFilename() |
190 path = self.__temporaryFilename() |
203 interface = QDBusInterface( |
191 interface = QDBusInterface( |
204 "org.gnome.Shell", |
192 "org.gnome.Shell", |
205 "/org/gnome/Shell/Screenshot", |
193 "/org/gnome/Shell/Screenshot", |
206 "org.gnome.Shell.Screenshot" |
194 "org.gnome.Shell.Screenshot", |
207 ) |
195 ) |
208 reply = interface.call( |
196 reply = interface.call( |
209 "ScreenshotWindow", |
197 "ScreenshotWindow", |
210 self.__captureDecorations, |
198 self.__captureDecorations, |
211 self.__captureCursor, |
199 self.__captureCursor, |
212 False, |
200 False, |
213 path |
201 path, |
214 ) |
202 ) |
215 if self.__checkReply(reply, 2): |
203 if self.__checkReply(reply, 2): |
216 filename = reply.arguments()[1] |
204 filename = reply.arguments()[1] |
217 if filename: |
205 if filename: |
218 snapshot = QPixmap(filename) |
206 snapshot = QPixmap(filename) |
219 with contextlib.suppress(OSError): |
207 with contextlib.suppress(OSError): |
220 os.remove(filename) |
208 os.remove(filename) |
221 |
209 |
222 # Step 2: extract the area of the screen containing |
210 # Step 2: extract the area of the screen containing |
223 # the cursor |
211 # the cursor |
224 if not snapshot.isNull(): |
212 if not snapshot.isNull(): |
225 screen = QApplication.screenAt(QCursor.pos()) |
213 screen = QApplication.screenAt(QCursor.pos()) |
226 geom = screen.geometry() |
214 geom = screen.geometry() |
227 snapshot = snapshot.copy(geom) |
215 snapshot = snapshot.copy(geom) |
228 |
216 |
229 self.grabbed.emit(snapshot) |
217 self.grabbed.emit(snapshot) |
230 |
218 |
231 def __grabSelectedWindow(self): |
219 def __grabSelectedWindow(self): |
232 """ |
220 """ |
233 Private method to grab a selected window. |
221 Private method to grab a selected window. |
234 """ |
222 """ |
235 snapshot = QPixmap() |
223 snapshot = QPixmap() |
236 |
224 |
237 if Globals.isKdeDesktop(): |
225 if Globals.isKdeDesktop(): |
238 mask = 0 |
226 mask = 0 |
239 if self.__captureDecorations: |
227 if self.__captureDecorations: |
240 mask |= 1 |
228 mask |= 1 |
241 if self.__captureCursor: |
229 if self.__captureCursor: |
242 mask |= 2 |
230 mask |= 2 |
243 interface = QDBusInterface( |
231 interface = QDBusInterface( |
244 "org.kde.KWin", |
232 "org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot" |
245 "/Screenshot", |
233 ) |
246 "org.kde.kwin.Screenshot" |
234 reply = interface.call("interactive", mask) |
247 ) |
|
248 reply = interface.call( |
|
249 "interactive", |
|
250 mask |
|
251 ) |
|
252 if self.__checkReply(reply, 1): |
235 if self.__checkReply(reply, 1): |
253 filename = reply.arguments()[0] |
236 filename = reply.arguments()[0] |
254 if filename: |
237 if filename: |
255 snapshot = QPixmap(filename) |
238 snapshot = QPixmap(filename) |
256 with contextlib.suppress(OSError): |
239 with contextlib.suppress(OSError): |
258 elif Globals.isGnomeDesktop(): |
241 elif Globals.isGnomeDesktop(): |
259 path = self.__temporaryFilename() |
242 path = self.__temporaryFilename() |
260 interface = QDBusInterface( |
243 interface = QDBusInterface( |
261 "org.gnome.Shell", |
244 "org.gnome.Shell", |
262 "/org/gnome/Shell/Screenshot", |
245 "/org/gnome/Shell/Screenshot", |
263 "org.gnome.Shell.Screenshot" |
246 "org.gnome.Shell.Screenshot", |
264 ) |
247 ) |
265 reply = interface.call( |
248 reply = interface.call( |
266 "ScreenshotWindow", |
249 "ScreenshotWindow", |
267 self.__captureDecorations, |
250 self.__captureDecorations, |
268 self.__captureCursor, |
251 self.__captureCursor, |
269 False, |
252 False, |
270 path |
253 path, |
271 ) |
254 ) |
272 if self.__checkReply(reply, 2): |
255 if self.__checkReply(reply, 2): |
273 filename = reply.arguments()[1] |
256 filename = reply.arguments()[1] |
274 if filename: |
257 if filename: |
275 snapshot = QPixmap(filename) |
258 snapshot = QPixmap(filename) |
276 with contextlib.suppress(OSError): |
259 with contextlib.suppress(OSError): |
277 os.remove(filename) |
260 os.remove(filename) |
278 |
261 |
279 self.grabbed.emit(snapshot) |
262 self.grabbed.emit(snapshot) |
280 |
263 |
281 def __grabRectangle(self): |
264 def __grabRectangle(self): |
282 """ |
265 """ |
283 Private method to grab a rectangular desktop area. |
266 Private method to grab a rectangular desktop area. |
284 """ |
267 """ |
285 snapshot = QPixmap() |
268 snapshot = QPixmap() |
286 |
269 |
287 if Globals.isGnomeDesktop(): |
270 if Globals.isGnomeDesktop(): |
288 # Step 1: let the user select the area |
271 # Step 1: let the user select the area |
289 interface = QDBusInterface( |
272 interface = QDBusInterface( |
290 "org.gnome.Shell", |
273 "org.gnome.Shell", |
291 "/org/gnome/Shell/Screenshot", |
274 "/org/gnome/Shell/Screenshot", |
292 "org.gnome.Shell.Screenshot" |
275 "org.gnome.Shell.Screenshot", |
293 ) |
276 ) |
294 reply = interface.call("SelectArea") |
277 reply = interface.call("SelectArea") |
295 if self.__checkReply(reply, 4): |
278 if self.__checkReply(reply, 4): |
296 x, y, width, height = reply.arguments()[:4] |
279 x, y, width, height = reply.arguments()[:4] |
297 |
280 |
298 # Step 2: grab the selected area |
281 # Step 2: grab the selected area |
299 path = self.__temporaryFilename() |
282 path = self.__temporaryFilename() |
300 reply = interface.call( |
283 reply = interface.call( |
301 "ScreenshotArea", |
284 "ScreenshotArea", x, y, width, height, False, path |
302 x, y, width, height, |
|
303 False, |
|
304 path |
|
305 ) |
285 ) |
306 if self.__checkReply(reply, 2): |
286 if self.__checkReply(reply, 2): |
307 filename = reply.arguments()[1] |
287 filename = reply.arguments()[1] |
308 if filename: |
288 if filename: |
309 snapshot = QPixmap(filename) |
289 snapshot = QPixmap(filename) |
310 with contextlib.suppress(OSError): |
290 with contextlib.suppress(OSError): |
311 os.remove(filename) |
291 os.remove(filename) |
312 |
292 |
313 self.grabbed.emit(snapshot) |
293 self.grabbed.emit(snapshot) |
314 |
294 |
315 def __checkReply(self, reply, argumentsCount): |
295 def __checkReply(self, reply, argumentsCount): |
316 """ |
296 """ |
317 Private method to check, if a reply is valid. |
297 Private method to check, if a reply is valid. |
318 |
298 |
319 @param reply reference to the reply message |
299 @param reply reference to the reply message |
320 @type QDBusMessage |
300 @type QDBusMessage |
321 @param argumentsCount number of expected arguments |
301 @param argumentsCount number of expected arguments |
322 @type int |
302 @type int |
323 @return flag indicating validity |
303 @return flag indicating validity |
324 @rtype bool |
304 @rtype bool |
325 """ |
305 """ |
326 if reply.type() == QDBusMessage.MessageType.ReplyMessage: |
306 if reply.type() == QDBusMessage.MessageType.ReplyMessage: |
327 if len(reply.arguments()) == argumentsCount: |
307 if len(reply.arguments()) == argumentsCount: |
328 return True |
308 return True |
329 |
309 |
330 EricMessageBox.warning( |
310 EricMessageBox.warning( |
331 None, |
311 None, |
332 self.tr("Screenshot Error"), |
312 self.tr("Screenshot Error"), |
333 self.tr("<p>Received an unexpected number of reply arguments." |
313 self.tr( |
334 " Expected {0} but got {1}</p>").format( |
314 "<p>Received an unexpected number of reply arguments." |
|
315 " Expected {0} but got {1}</p>" |
|
316 ).format( |
335 argumentsCount, |
317 argumentsCount, |
336 len(reply.arguments()), |
318 len(reply.arguments()), |
337 )) |
319 ), |
338 |
320 ) |
|
321 |
339 elif reply.type() == QDBusMessage.MessageType.ErrorMessage: |
322 elif reply.type() == QDBusMessage.MessageType.ErrorMessage: |
340 EricMessageBox.warning( |
323 EricMessageBox.warning( |
341 None, |
324 None, |
342 self.tr("Screenshot Error"), |
325 self.tr("Screenshot Error"), |
343 self.tr("<p>Received error <b>{0}</b> from DBus while" |
326 self.tr( |
344 " performing screenshot.</p><p>{1}</p>").format( |
327 "<p>Received error <b>{0}</b> from DBus while" |
|
328 " performing screenshot.</p><p>{1}</p>" |
|
329 ).format( |
345 reply.errorName(), |
330 reply.errorName(), |
346 reply.errorMessage(), |
331 reply.errorMessage(), |
347 )) |
332 ), |
348 |
333 ) |
|
334 |
349 elif reply.type() == QDBusMessage.MessageType.InvalidMessage: |
335 elif reply.type() == QDBusMessage.MessageType.InvalidMessage: |
350 EricMessageBox.warning( |
336 EricMessageBox.warning( |
351 None, |
337 None, self.tr("Screenshot Error"), self.tr("Received an invalid reply.") |
352 self.tr("Screenshot Error"), |
338 ) |
353 self.tr("Received an invalid reply.")) |
339 |
354 |
|
355 else: |
340 else: |
356 EricMessageBox.warning( |
341 EricMessageBox.warning( |
357 None, |
342 None, |
358 self.tr("Screenshot Error"), |
343 self.tr("Screenshot Error"), |
359 self.tr("Received an unexpected reply.")) |
344 self.tr("Received an unexpected reply."), |
360 |
345 ) |
|
346 |
361 return False |
347 return False |
362 |
348 |
363 def __temporaryFilename(self): |
349 def __temporaryFilename(self): |
364 """ |
350 """ |
365 Private method to generate a temporary filename. |
351 Private method to generate a temporary filename. |
366 |
352 |
367 @return path name for a unique, temporary file |
353 @return path name for a unique, temporary file |
368 @rtype str |
354 @rtype str |
369 """ |
355 """ |
370 return "/tmp/eric-snap-{0}.png".format(uuid.uuid4().hex) # secok |
356 return "/tmp/eric-snap-{0}.png".format(uuid.uuid4().hex) # secok |