Merge lp:~henrikno/lookit/trunk into lp:lookit

Proposed by Henrik Nordvik
Status: Needs review
Proposed branch: lp:~henrikno/lookit/trunk
Merge into: lp:lookit
Diff against target: 378 lines (+166/-79)
5 files modified
src/lookit/data/pref.xml (+33/-0)
src/lookit/lookit (+15/-12)
src/lookit/prefdlg.py (+8/-0)
src/lookit/screencapper.py (+94/-65)
src/lookit/uploader.py (+16/-2)
To merge this branch: bzr merge lp:~henrikno/lookit/trunk
Reviewer Review Type Date Requested Status
Zach Tibbitts Pending
Review via email: mp+26013@code.launchpad.net
To post a comment you must log in.
lp:~henrikno/lookit/trunk updated
9. By Henrik Nordvik

Avoid errors when not dragging a selection area.

10. By Henrik Nordvik

Added an option to select directory to save screenshots.

11. By Henrik Nordvik

Using Xlib and grab_pointer to get window/area selection.

Unmerged revisions

11. By Henrik Nordvik

Using Xlib and grab_pointer to get window/area selection.

10. By Henrik Nordvik

Added an option to select directory to save screenshots.

9. By Henrik Nordvik

Avoid errors when not dragging a selection area.

8. By Henrik Nordvik

Added some error checking code.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lookit/data/pref.xml'
2--- src/lookit/data/pref.xml 2010-05-25 23:53:57 +0000
3+++ src/lookit/data/pref.xml 2010-05-30 06:56:26 +0000
4@@ -52,6 +52,39 @@
5 </packing>
6 </child>
7 <child>
8+ <object class="GtkHBox" id="hbox2">
9+ <property name="visible">True</property>
10+ <child>
11+ <object class="GtkLabel" id="label10">
12+ <property name="visible">True</property>
13+ <property name="tooltip_text" translatable="yes">Select a directory to save the screenshots to.</property>
14+ <property name="label" translatable="yes">Directory:</property>
15+ </object>
16+ <packing>
17+ <property name="expand">False</property>
18+ <property name="position">0</property>
19+ </packing>
20+ </child>
21+ <child>
22+ <object class="GtkFileChooserButton" id="savedir">
23+ <property name="visible">True</property>
24+ <property name="action">select-folder</property>
25+ <property name="title" translatable="yes">Select A Directory to save Screenshots</property>
26+ </object>
27+ <packing>
28+ <property name="position">1</property>
29+ </packing>
30+ </child>
31+ </object>
32+ <packing>
33+ <property name="expand">False</property>
34+ <property name="position">2</property>
35+ </packing>
36+ </child>
37+ <child>
38+ <placeholder/>
39+ </child>
40+ <child>
41 <placeholder/>
42 </child>
43 </object>
44
45=== modified file 'src/lookit/lookit'
46--- src/lookit/lookit 2010-05-25 23:53:57 +0000
47+++ src/lookit/lookit 2010-05-30 06:56:26 +0000
48@@ -28,18 +28,18 @@
49 import urllib
50 import urlparse
51
52-try:
53+#try:
54 # Try to import these from current directory, in case running from src
55- import aboutdlg
56- import prefdlg
57- import screencapper
58- import uploader
59-except ImportError:
60- # Then fall back to globally installed versions
61- from lookit import aboutdlg
62- from lookit import prefdlg
63- from lookit import screencapper
64- from lookit import uploader
65+import aboutdlg
66+import prefdlg
67+import screencapper
68+import uploader
69+#except ImportError:
70+ ## Then fall back to globally installed versions
71+ #from lookit import aboutdlg
72+ #from lookit import prefdlg
73+ #from lookit import screencapper
74+ #from lookit import uploader
75
76 CONF_FILE = os.path.expanduser('~/.config/lookit.conf')
77 TRASH_DIR = os.path.expanduser('~/.local/share/Trash/files')
78@@ -142,7 +142,7 @@
79 if pb != None:
80 m = hashlib.md5()
81 m.update(pb.get_pixels())
82- hashstring = m.hexdigest() + '.png'
83+ hashstring = self.prefs['savedir'] + '/' + m.hexdigest() + '.png'
84 pb.save(hashstring, 'png')
85 self.upload_image(hashstring)
86 else:
87@@ -170,6 +170,9 @@
88 )
89 elif proto == 'Imgur':
90 success, data = uploader.upload_file_imgur(image)
91+ elif proto == 'None':
92+ self.show_notification('Image saved', image)
93+ return
94 else:
95 success = False
96 data = "Error: no such protocol: {0}".format(proto)
97
98=== modified file 'src/lookit/prefdlg.py'
99--- src/lookit/prefdlg.py 2010-05-25 23:53:57 +0000
100+++ src/lookit/prefdlg.py 2010-05-30 06:56:26 +0000
101@@ -1,4 +1,5 @@
102 import gtk
103+import gio
104 import os
105 import sys
106
107@@ -29,6 +30,7 @@
108
109 self.combobox = builder.get_object("combobox")
110 connections = gtk.ListStore(str)
111+ connections.append(['None'])
112 for connection in CONNECTION_TYPES:
113 connections.append([connection])
114 self.combobox.set_model(connections)
115@@ -43,6 +45,7 @@
116 self.password = builder.get_object("password")
117 self.directory = builder.get_object("directory")
118 self.url = builder.get_object("url")
119+ self.savedir = builder.get_object("savedir")
120
121 builder.connect_signals(self)
122
123@@ -61,6 +64,7 @@
124 self.password.set_text(prefs['password'])
125 self.directory.set_text(prefs['directory'])
126 self.url.set_text(prefs['url'])
127+ self.savedir.set_current_folder_file(gio.File(prefs['savedir']))
128 except KeyError:
129 if 'Imgur' in CONNECTION_TYPES:
130 self.combobox.set_active(
131@@ -90,10 +94,13 @@
132 self.port.get_adjustment().set_value(21)
133 elif proto == 'SSH':
134 self.port.get_adjustment().set_value(22)
135+ else:
136+ self.port.get_adjustment().set_value(21)
137
138
139
140 def on_pref_dialog_response(self, widget, data=None):
141+ print "on_pref_dialog_response"
142 if data == 1:
143 self.prefs['trash'] = self.trash.get_active()
144 self.prefs['shortenurl'] = self.shortenurl.get_active()
145@@ -104,6 +111,7 @@
146 self.prefs['password'] = self.password.get_text()
147 self.prefs['directory'] = self.directory.get_text()
148 self.prefs['url'] = self.url.get_text()
149+ self.prefs['savedir'] = self.savedir.get_current_folder_file().get_path()
150 else:
151 self.prefs = dict()
152 self.dialog.destroy()
153
154=== modified file 'src/lookit/screencapper.py'
155--- src/lookit/screencapper.py 2010-05-25 01:21:09 +0000
156+++ src/lookit/screencapper.py 2010-05-30 06:56:26 +0000
157@@ -1,50 +1,18 @@
158 import gtk
159 import gtk.gdk
160
161+from Xlib.display import Display
162+from Xlib import X
163+from Xlib import XK
164+from Xlib import Xcursorfont
165+import Xlib
166+
167 class ScreenCapper:
168 def __init__(self):
169+ self.pixbuf = None
170 self.root = gtk.gdk.get_default_root_window()
171 self.size = self.root.get_size()
172
173- def on_key_press(self, widget, event):
174- if event.keyval == gtk.keysyms.Escape:
175- self.pixbuf = None
176- self.overlay.destroy()
177- gtk.main_quit()
178-
179- def on_mouse_up(self, widget, event):
180- self.x2 = int(event.x)
181- self.y2 = int(event.y)
182-
183- sz_x = abs(self.x1 - self.x2)
184- sz_y = abs(self.y1 - self.y2)
185-
186- print sz_x, sz_y, self.x1, self.x2, self.y1, self.y2
187-
188- if sz_x == 0 and sz_y == 0:
189- # TODO: We should capture the window that was clicked
190- self.pixbuf = None
191- self.overlay.destroy()
192- gtk.main_quit()
193-
194- self.pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,
195- False, 8, sz_x, sz_y)
196- self.pixbuf.get_from_drawable(self.root,
197- self.root.get_colormap(),
198- min(self.x1, self.x2),
199- min(self.y1, self.y2),
200- 0, 0, sz_x, sz_y)
201-
202- self.overlay.destroy()
203- gtk.main_quit()
204-
205- def on_mouse_down(self, widget, event):
206- self.x1 = int(event.x)
207- self.y1 = int(event.y)
208-
209- def realize_area(self, widget):
210- self.overlay.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS))
211-
212 def capture_screen(self):
213 self.pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
214 self.size[0], self.size[1])
215@@ -54,31 +22,92 @@
216 self.size[0], self.size[1])
217 return self.pixbuf
218
219+ def grab_area(self, start_x, start_y, width, height):
220+ self.pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
221+ width, height)
222+ self.pixbuf.get_from_drawable(self.root, self.root.get_colormap(),
223+ start_x, start_y, 0, 0, width, height)
224+
225 def capture_area(self):
226- self.has_result = False
227-
228- self.overlay = gtk.Window()
229-
230- self.overlay.connect("key_press_event", self.on_key_press)
231- self.overlay.connect("button_press_event", self.on_mouse_down)
232- self.overlay.connect("button_release_event", self.on_mouse_up)
233- self.overlay.connect("realize", self.realize_area)
234-
235- self.overlay.set_events(gtk.gdk.BUTTON_PRESS_MASK |
236- gtk.gdk.BUTTON_RELEASE_MASK |
237- gtk.gdk.KEY_PRESS_MASK)
238-
239- warning = gtk.Label('Warning: You must be using a window ' +
240- 'manager that supports compositing. Press Esc to ' +
241- 'exit.')
242- warning.show()
243- self.overlay.add(warning)
244-
245- self.overlay.set_opacity(0)
246- self.overlay.fullscreen()
247-
248- self.overlay.show()
249-
250- gtk.main()
251-
252+ try:
253+ display = Display()
254+ root = display.screen().root
255+ cursor_font = display.open_font('cursor')
256+ mycursor = cursor_font.create_glyph_cursor(cursor_font,
257+ Xcursorfont.crosshair,
258+ Xcursorfont.crosshair+1, (0,0,0),
259+ (0xFFFF, 0xFFFF, 0xFFFF))
260+ root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask |
261+ X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, 0,
262+ mycursor, X.CurrentTime)
263+ root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync,
264+ X.CurrentTime)
265+ colormap = display.screen().default_colormap
266+
267+ # (r,g,b) blue = (0,0,65535)
268+ color = colormap.alloc_color(0, 0, 65535)
269+ # Xor it because we'll draw with X.GXxor function
270+ xor_color = color.pixel ^ 0xffffff
271+
272+ print hex(color.pixel), hex(xor_color)
273+
274+ self.gc = root.create_gc(
275+ line_width = 1,
276+ line_style = X.LineSolid,
277+ fill_style = X.FillSolid,
278+ fill_rule = X.WindingRule,
279+ cap_style = X.CapButt,
280+ join_style = X.JoinMiter,
281+ foreground = xor_color,
282+ background = display.screen().black_pixel,
283+ function = X.GXxor,
284+ graphics_exposures = False,
285+ subwindow_mode = X.IncludeInferiors,
286+ )
287+
288+ pressed = False
289+ done = False
290+ lastx, lasty = None, None
291+ while not done:
292+ event = display.next_event()
293+ if event.type == X.DestroyNotify:
294+ self.pixbuf = None
295+ done = True
296+ if event.type == X.KeyPress:
297+ self.pixbuf = None
298+ done = True
299+ if event.type == X.MotionNotify:
300+ if pressed:
301+ if lastx and lasty:
302+ ax = abs(startx - lastx)
303+ ay = abs(starty - lasty)
304+ root.rectangle(self.gc, min(startx, lastx), min(starty, lasty), ax, ay)
305+ lastx, lasty = event.root_x, event.root_y
306+ ax = abs(startx - event.root_x)
307+ ay = abs(starty - event.root_y)
308+ root.rectangle(self.gc, min(startx, event.root_x), min(starty, event.root_y), ax, ay)
309+
310+ elif event.type == X.ButtonPress and event.child != X.NONE:
311+ startx, starty = event.event_x, event.event_y
312+ pressed = True
313+
314+ elif event.type == X.ButtonRelease and event.child != X.NONE:
315+ endx, endy = event.event_x, event.event_y
316+ geometry = event.child.get_geometry()
317+ width, height = geometry.width, geometry.height
318+ window_x, window_y = geometry.x, geometry.y
319+ diff_x = abs(startx - endx)
320+ diff_y = abs(starty - endy)
321+
322+ if diff_x == 0 and diff_y == 0:
323+ self.grab_area(window_x, window_y, width, height)
324+ else:
325+ self.grab_area(startx, starty, diff_x, diff_y)
326+ done = True
327+ except:
328+ raise
329+ finally:
330+ display.ungrab_pointer(X.CurrentTime)
331+ display.ungrab_keyboard(X.CurrentTime)
332+ display.flush()
333 return self.pixbuf
334
335=== modified file 'src/lookit/uploader.py'
336--- src/lookit/uploader.py 2010-05-25 01:21:09 +0000
337+++ src/lookit/uploader.py 2010-05-30 06:56:26 +0000
338@@ -71,6 +71,10 @@
339 return PROTO_LIST
340
341 def upload_file_ftp(f, hostname, port, username, password, directory, url):
342+ if not 'FTP' in PROTO_LIST:
343+ errormsg = 'Error: FTP support not installed. Install \'ftplib\' package.'
344+ print errormsg
345+ return False, errormsg
346 i = open(f, 'r')
347
348 try:
349@@ -88,6 +92,10 @@
350 return True, None
351
352 def upload_file_sftp(f, hostname, port, username, password, directory, url):
353+ if not 'SSH' in PROTO_LIST:
354+ errormsg = 'Error: SSH support not installed. Install \'paramiko\' package.'
355+ print errormsg
356+ return False, errormsg
357 t = paramiko.Transport((hostname, port))
358 t.connect(username=username, password=password)
359 sftp = paramiko.SFTPClient.from_transport(t)
360@@ -102,10 +110,16 @@
361
362 def upload_file_imgur(f):
363 if not 'Imgur' in PROTO_LIST:
364- print 'Error: Imgur not supported'
365+ errormsg = 'Error: Imgur support not installed. Install \'pycurl\' package.'
366+ print errormsg
367+ return False, errormsg
368 i = ImgurUploader()
369 i.upload(f)
370- return True, i.mapping
371+ print i.mapping
372+ if not 'error_msg' in i.mapping:
373+ return True, i.mapping
374+ else:
375+ return False, i.mapping.get('error_msg')
376
377 def upload_file_ubuntuone(f):
378 pass

Subscribers

People subscribed via source and target branches