Merge lp:~rick-rickspencer3/quidgets/precise into lp:quidgets
- precise
- Merge into trunk
Proposed by
Rick Spencer
Status: | Merged |
---|---|
Merged at revision: | 166 |
Proposed branch: | lp:~rick-rickspencer3/quidgets/precise |
Merge into: | lp:quidgets |
Diff against target: |
4084 lines (+997/-2064) 14 files modified
quickly/widgets/asynch_task_progressbox.py (+0/-310) quickly/widgets/conventions.py (+0/-2) quickly/widgets/couch_grid.py (+0/-458) quickly/widgets/dictionary_grid.py (+32/-32) quickly/widgets/grid_column.py (+54/-59) quickly/widgets/grid_filter.py (+692/-698) quickly/widgets/media_player_box.py (+61/-58) quickly/widgets/press_and_hold_button.py (+45/-8) quickly/widgets/tests/test_asycnh_task_progress_box.py (+0/-51) quickly/widgets/tests/test_couch_grid.py (+0/-284) quickly/widgets/tests/test_dictionary_grid.py (+7/-8) quickly/widgets/text_editor.py (+34/-31) quickly/widgets/url_fetch_progressbox.py (+39/-35) quickly/widgets/web_cam_box.py (+33/-30) |
To merge this branch: | bzr merge lp:~rick-rickspencer3/quidgets/precise |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Terry (community) | Approve | ||
Review via email: mp+96077@code.launchpad.net |
Commit message
Migrated quickly widgets to GObeject Introspection, so they will work with new Quickly projects.
Description of the change
1. deleted couchgrid, since DesktopCouch is no longer recommended (there is currently no upgrade path for apps that used CouchGrid in the past)
2. update all other quidgets to use GOI.
3. ensured all tests ran and passed.
To post a comment you must log in.
Revision history for this message
Michael Terry (mterry) wrote : | # |
Looked into that warning, and it's harmless. Just comes from CheckColumns using the generic GridColumn intializer.
I pushed after porting the GStreamer code to use python-gi so that the gobject module doesn't get imported that way.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'quickly/widgets/asynch_task_progressbox.py' | |||
2 | --- quickly/widgets/asynch_task_progressbox.py 2010-09-11 19:59:30 +0000 | |||
3 | +++ quickly/widgets/asynch_task_progressbox.py 1970-01-01 00:00:00 +0000 | |||
4 | @@ -1,310 +0,0 @@ | |||
5 | 1 | ### BEGIN LICENSE | ||
6 | 2 | # Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com | ||
7 | 3 | #This program is free software: you can redistribute it and/or modify it | ||
8 | 4 | #under the terms of the GNU General Public License version 3, as published | ||
9 | 5 | #by the Free Software Foundation. | ||
10 | 6 | # | ||
11 | 7 | #This program is distributed in the hope that it will be useful, but | ||
12 | 8 | #WITHOUT ANY WARRANTY; without even the implied warranties of | ||
13 | 9 | #MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
14 | 10 | #PURPOSE. See the GNU General Public License for more details. | ||
15 | 11 | # | ||
16 | 12 | #You should have received a copy of the GNU General Public License along | ||
17 | 13 | #with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | 14 | ### END LICENSE | ||
19 | 15 | |||
20 | 16 | try: | ||
21 | 17 | import pygtk | ||
22 | 18 | pygtk.require("2.0") | ||
23 | 19 | import gtk | ||
24 | 20 | import threading | ||
25 | 21 | import time | ||
26 | 22 | import gobject | ||
27 | 23 | import gettext | ||
28 | 24 | from gettext import gettext as _ | ||
29 | 25 | gettext.textdomain('quickly-widgets') | ||
30 | 26 | |||
31 | 27 | except: | ||
32 | 28 | print "couldn't load depencies" | ||
33 | 29 | |||
34 | 30 | class AsynchTaskProgressBox( gtk.HBox ): | ||
35 | 31 | """AsynchTaskProgressBox: encapsulates a pulstating progressbar, a cancel | ||
36 | 32 | button, and a long running task. Use an AsynchTaskProgressBox when you want | ||
37 | 33 | a window to perform a long running task in the background without freezing | ||
38 | 34 | the UI for the user. | ||
39 | 35 | |||
40 | 36 | """ | ||
41 | 37 | |||
42 | 38 | def __init__(self, run_function, params = None, cancelable = True): | ||
43 | 39 | """Create an AsycnTaskProgressBox | ||
44 | 40 | |||
45 | 41 | Keyword arguments: | ||
46 | 42 | run_function -- the function to run asynchronously | ||
47 | 43 | params -- optional dictionary of parameters to be pass into run_function | ||
48 | 44 | cancelable -- optional value to determine whether to show cancel button. Defaults to True. | ||
49 | 45 | Do not use a value with the key of 'kill' in the params dictionary | ||
50 | 46 | |||
51 | 47 | """ | ||
52 | 48 | |||
53 | 49 | gtk.HBox.__init__( self, False, 2 ) | ||
54 | 50 | |||
55 | 51 | self.progressbar = gtk.ProgressBar() | ||
56 | 52 | self.progressbar.show() | ||
57 | 53 | self.pack_start(self.progressbar, True) | ||
58 | 54 | |||
59 | 55 | self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL) | ||
60 | 56 | if cancelable: | ||
61 | 57 | self.cancel_button.show() | ||
62 | 58 | if params is None: | ||
63 | 59 | params = {} | ||
64 | 60 | params["update_progress_function"] = self.update | ||
65 | 61 | self.cancel_button.set_sensitive(False) | ||
66 | 62 | self.cancel_button.connect("clicked",self.__stop_clicked) | ||
67 | 63 | self.pack_end(self.cancel_button, False) | ||
68 | 64 | |||
69 | 65 | self.run_function = run_function | ||
70 | 66 | self.pulse_thread = None | ||
71 | 67 | self.work_thread = None | ||
72 | 68 | self.params = params | ||
73 | 69 | |||
74 | 70 | self.connect("destroy", self.__destroy) | ||
75 | 71 | |||
76 | 72 | __gsignals__ = {'complete' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | ||
77 | 73 | (gobject.TYPE_PYOBJECT,)), | ||
78 | 74 | 'cancelrequested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | ||
79 | 75 | (gobject.TYPE_PYOBJECT,)) | ||
80 | 76 | } | ||
81 | 77 | |||
82 | 78 | def start(self, caption = "Working"): | ||
83 | 79 | """executes run_function asynchronously and starts pulsating the progressbar | ||
84 | 80 | Keyword arguments: | ||
85 | 81 | caption -- optional text to display in the progressbar | ||
86 | 82 | """ | ||
87 | 83 | #Throw an exception if the user tries to start an operating thread | ||
88 | 84 | if self.pulse_thread != None: | ||
89 | 85 | raise RuntimeError("AsynchTaskProgressBox already started.") | ||
90 | 86 | |||
91 | 87 | #Create and start a thread to run the users task | ||
92 | 88 | #pass in a callback and the user's params | ||
93 | 89 | self.work_thread = KillableThread(self.run_function, self.__on_complete, self.params) | ||
94 | 90 | self.work_thread.start() | ||
95 | 91 | |||
96 | 92 | #create a thread to display the user feedback | ||
97 | 93 | self.pulse_thread = PulseThread(self.progressbar, caption) | ||
98 | 94 | self.pulse_thread.start() | ||
99 | 95 | |||
100 | 96 | #enable the button so the user can try to kill the task | ||
101 | 97 | self.cancel_button.set_sensitive( True ) | ||
102 | 98 | |||
103 | 99 | def update(self, fraction = None, displaytext = "Working"): | ||
104 | 100 | """updates the progress bar with a given percentage and/or changes the | ||
105 | 101 | caption. | ||
106 | 102 | Keyword arguments: | ||
107 | 103 | fraction -- the current percentage complete | ||
108 | 104 | displaytext -- the new caption to display""" | ||
109 | 105 | if self.pulse_thread != None: | ||
110 | 106 | self.pulse_thread.update(fraction, displaytext) | ||
111 | 107 | |||
112 | 108 | #call back function for after run_function returns | ||
113 | 109 | def __on_complete( self, data ): | ||
114 | 110 | self.emit("complete", data) | ||
115 | 111 | self.kill() | ||
116 | 112 | |||
117 | 113 | #call back function for cancel button | ||
118 | 114 | def __stop_clicked( self, widget, data = None ): | ||
119 | 115 | self.cancel() | ||
120 | 116 | |||
121 | 117 | def cancel(self): | ||
122 | 118 | self.kill() | ||
123 | 119 | #emit the cancelrequested event | ||
124 | 120 | #note that this only signals that the kill function was called | ||
125 | 121 | #the thread may or may not have actually stopped | ||
126 | 122 | self.emit("cancelrequested", self) | ||
127 | 123 | |||
128 | 124 | def kill( self ): | ||
129 | 125 | """ | ||
130 | 126 | Stops pulstating the progressbar if the progressbar is working. | ||
131 | 127 | Sets the value of 'kill' to True in the run_function. | ||
132 | 128 | |||
133 | 129 | """ | ||
134 | 130 | |||
135 | 131 | #stop the pulse_thread and remove a reference to it if there is one | ||
136 | 132 | if self.pulse_thread != None: | ||
137 | 133 | self.pulse_thread.kill() | ||
138 | 134 | self.pulse_thread = None | ||
139 | 135 | |||
140 | 136 | #disable the cancel button since the task is about to be told to stop | ||
141 | 137 | gobject.idle_add(self.__disable_cancel_button) | ||
142 | 138 | |||
143 | 139 | #tell the users function tostop if it's thread exists | ||
144 | 140 | if self.work_thread != None: | ||
145 | 141 | self.work_thread.kill() | ||
146 | 142 | |||
147 | 143 | #called when the widget is destroyed, attempts to clean up | ||
148 | 144 | #the work thread and the pulse thread | ||
149 | 145 | def __destroy(self, widget, data = None): | ||
150 | 146 | if self.work_thread != None: | ||
151 | 147 | self.work_thread.kill() | ||
152 | 148 | if self.pulse_thread != None: | ||
153 | 149 | self.pulse_thread.kill() | ||
154 | 150 | |||
155 | 151 | def __disable_cancel_button(self): | ||
156 | 152 | gtk.gdk.threads_enter() | ||
157 | 153 | self.cancel_button.set_sensitive( False ) | ||
158 | 154 | gtk.gdk.threads_leave() | ||
159 | 155 | |||
160 | 156 | class PulseThread ( threading.Thread ): | ||
161 | 157 | """Class for use by AsynchTaskProgressBox. Not for general use. | ||
162 | 158 | |||
163 | 159 | """ | ||
164 | 160 | def __init__(self,progressbar,displaytext = _("Working")): | ||
165 | 161 | threading.Thread.__init__(self) | ||
166 | 162 | self.displaytext = displaytext | ||
167 | 163 | self.setDaemon(False) | ||
168 | 164 | self.progressbar = progressbar | ||
169 | 165 | self.__kill = False | ||
170 | 166 | self.fraction = float(0) | ||
171 | 167 | |||
172 | 168 | def kill(self): | ||
173 | 169 | self.__kill = True | ||
174 | 170 | |||
175 | 171 | def update(self, fraction, displaytext): | ||
176 | 172 | self.displaytext = displaytext | ||
177 | 173 | self.fraction = fraction | ||
178 | 174 | |||
179 | 175 | #As a subclass of Thread, this function runs when start() is called | ||
180 | 176 | #It will cause the progressbar to pulse, showing that a task is running | ||
181 | 177 | def run ( self ): | ||
182 | 178 | self.progressbar.set_text(self.displaytext) | ||
183 | 179 | #keep running until the __kill flag is set to True | ||
184 | 180 | while not self.__kill: | ||
185 | 181 | time.sleep(.1) | ||
186 | 182 | #get ahold of the UI thread and command the progress bar to pulse | ||
187 | 183 | gtk.gdk.threads_enter() | ||
188 | 184 | if self.fraction == 0: | ||
189 | 185 | self.progressbar.pulse() | ||
190 | 186 | else: | ||
191 | 187 | self.progressbar.set_fraction(self.fraction) | ||
192 | 188 | self.progressbar.set_text(self.displaytext) | ||
193 | 189 | gtk.gdk.threads_leave() | ||
194 | 190 | #before exiting, reset the progressbar to show that no task is running | ||
195 | 191 | gtk.gdk.threads_enter() | ||
196 | 192 | self.progressbar.set_fraction(0) | ||
197 | 193 | self.progressbar.set_text("") | ||
198 | 194 | gtk.gdk.threads_leave() | ||
199 | 195 | |||
200 | 196 | class KillableThread( threading.Thread ): | ||
201 | 197 | """Class for use by AsynchTaskProgressBox. Not for general use. | ||
202 | 198 | |||
203 | 199 | """ | ||
204 | 200 | def __init__(self,run_function, on_complete, params = None): | ||
205 | 201 | threading.Thread.__init__(self) | ||
206 | 202 | self.setDaemon(False) | ||
207 | 203 | self.run_function = run_function | ||
208 | 204 | self.params = params | ||
209 | 205 | self.on_complete = on_complete | ||
210 | 206 | |||
211 | 207 | #As a subclass of Thread, this function runs when start() is called | ||
212 | 208 | #It will run the user's function on this thread | ||
213 | 209 | def run( self ): | ||
214 | 210 | #set up params and include the kill flag | ||
215 | 211 | if self.params == None: | ||
216 | 212 | self.params = {} | ||
217 | 213 | self.params["kill"] = False | ||
218 | 214 | #tell the function to run | ||
219 | 215 | data = self.run_function(self.params) | ||
220 | 216 | #return any data from the function so it can be sent in the complete signal | ||
221 | 217 | self.on_complete(data) | ||
222 | 218 | |||
223 | 219 | #Tell the user's function that it should stop | ||
224 | 220 | #Note the user's function may not check this | ||
225 | 221 | def kill( self ): | ||
226 | 222 | self.params["kill"] = True | ||
227 | 223 | |||
228 | 224 | class TestWindow(gtk.Window): | ||
229 | 225 | """For testing and demonstrating AsycnTaskProgressBox. | ||
230 | 226 | |||
231 | 227 | """ | ||
232 | 228 | def __init__(self): | ||
233 | 229 | #create a window a VBox to hold the controls | ||
234 | 230 | gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) | ||
235 | 231 | self.set_title("AsynchTaskProgressBox Test Window") | ||
236 | 232 | windowbox = gtk.VBox(False, 2) | ||
237 | 233 | windowbox.show() | ||
238 | 234 | self.add(windowbox) | ||
239 | 235 | |||
240 | 236 | #create params for use by the function that should run asynchronously | ||
241 | 237 | params = {"start": 100, "stop": 110} | ||
242 | 238 | |||
243 | 239 | #pass in the function and the params, then add to the window | ||
244 | 240 | self.thread_progressbox = AsynchTaskProgressBox(self.asynch_function, params) | ||
245 | 241 | self.thread_progressbox.show() | ||
246 | 242 | windowbox.add(self.thread_progressbox) | ||
247 | 243 | |||
248 | 244 | #start the task, and set the text for the progressbar to "Testing" | ||
249 | 245 | #This will start the function and start the progressbar pulsating | ||
250 | 246 | self.thread_progressbox.start("Testing") | ||
251 | 247 | |||
252 | 248 | #connect to the complete event to respond when the task is complete | ||
253 | 249 | self.thread_progressbox.connect("complete",self.complete_function) | ||
254 | 250 | |||
255 | 251 | #connect to the cancel requested event for demonstration purposes | ||
256 | 252 | self.thread_progressbox.connect("cancelrequested", self.canceled_function) | ||
257 | 253 | |||
258 | 254 | #create a button for starting the task and add it to the window | ||
259 | 255 | start_button = gtk.Button(stock=gtk.STOCK_EXECUTE) | ||
260 | 256 | start_button.show() | ||
261 | 257 | windowbox.add(start_button) | ||
262 | 258 | start_button.connect("clicked",self.start_clicked) | ||
263 | 259 | self.show() | ||
264 | 260 | |||
265 | 261 | #finish wiring up the window | ||
266 | 262 | self.connect("destroy", self.destroy) | ||
267 | 263 | |||
268 | 264 | #start up gtk.main in a threaded environment | ||
269 | 265 | gtk.gdk.threads_init() | ||
270 | 266 | gtk.gdk.threads_enter() | ||
271 | 267 | gtk.main() | ||
272 | 268 | gtk.gdk.threads_leave() | ||
273 | 269 | |||
274 | 270 | #called when the window is destroyed | ||
275 | 271 | def destroy(self, widget, data = None): | ||
276 | 272 | gtk.main_quit() | ||
277 | 273 | |||
278 | 274 | #start the AsynchTaskProgressBox when the button is clicked | ||
279 | 275 | def start_clicked(self, widget, data = None): | ||
280 | 276 | self.thread_progressbox.start("Testing") | ||
281 | 277 | |||
282 | 278 | #The function to run asynchronously | ||
283 | 279 | def asynch_function( self, params ): | ||
284 | 280 | # do something interminate and cpu intensive at startup | ||
285 | 281 | print "starting..." | ||
286 | 282 | time.sleep(2) | ||
287 | 283 | #pull values from the params that were set above | ||
288 | 284 | total = float(abs(params["stop"]-params["start"])) | ||
289 | 285 | for x in range(params["start"],params["stop"]): | ||
290 | 286 | #check if to see if the user has told the task to stop | ||
291 | 287 | if params["kill"] == True: | ||
292 | 288 | #return a string if the user stopped the task | ||
293 | 289 | return "stopped at " + str(x) | ||
294 | 290 | else: | ||
295 | 291 | #if the user did not try to stop the task, go ahead and do something | ||
296 | 292 | print x | ||
297 | 293 | fraction=abs(x-params["start"])/total | ||
298 | 294 | # call the update_progress_function function with the current percentage and caption | ||
299 | 295 | params["update_progress_function"](fraction=fraction, displaytext=str(x)) | ||
300 | 296 | #this is a processor intensive task, so | ||
301 | 297 | #sleep the loop to keep the UI from bogging down | ||
302 | 298 | time.sleep(.5) | ||
303 | 299 | #if the loop completes, return a string | ||
304 | 300 | return "counted all" | ||
305 | 301 | |||
306 | 302 | #called when the task completes | ||
307 | 303 | def complete_function(self, widget, data = None): | ||
308 | 304 | print "returned " + str(data) | ||
309 | 305 | |||
310 | 306 | def canceled_function(self, widget, data=None): | ||
311 | 307 | print "cancel requested" | ||
312 | 308 | if __name__== "__main__": | ||
313 | 309 | test = TestWindow() | ||
314 | 310 | |||
315 | 311 | 0 | ||
316 | === modified file 'quickly/widgets/conventions.py' | |||
317 | --- quickly/widgets/conventions.py 2010-11-21 19:20:40 +0000 | |||
318 | +++ quickly/widgets/conventions.py 2012-03-06 08:52:21 +0000 | |||
319 | @@ -13,8 +13,6 @@ | |||
320 | 13 | #with this program. If not, see <http://www.gnu.org/licenses/>. | 13 | #with this program. If not, see <http://www.gnu.org/licenses/>. |
321 | 14 | ### END LICENSE | 14 | ### END LICENSE |
322 | 15 | 15 | ||
323 | 16 | import gtk | ||
324 | 17 | import gobject | ||
325 | 18 | from grid_column import StringColumn, CurrencyColumn, CheckColumn | 16 | from grid_column import StringColumn, CurrencyColumn, CheckColumn |
326 | 19 | from grid_column import IntegerColumn, TagsColumn, DateColumn | 17 | from grid_column import IntegerColumn, TagsColumn, DateColumn |
327 | 20 | 18 | ||
328 | 21 | 19 | ||
329 | === removed file 'quickly/widgets/couch_grid.py' | |||
330 | --- quickly/widgets/couch_grid.py 2011-08-19 17:22:14 +0000 | |||
331 | +++ quickly/widgets/couch_grid.py 1970-01-01 00:00:00 +0000 | |||
332 | @@ -1,458 +0,0 @@ | |||
333 | 1 | # -*- coding: utf-8 -*- | ||
334 | 2 | ### BEGIN LICENSE | ||
335 | 3 | # Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com | ||
336 | 4 | #This program is free software: you can redistribute it and/or modify it | ||
337 | 5 | #under the terms of the GNU General Public License version 3, as published | ||
338 | 6 | #by the Free Software Foundation. | ||
339 | 7 | # | ||
340 | 8 | #This program is distributed in the hope that it will be useful, but | ||
341 | 9 | #WITHOUT ANY WARRANTY; without even the implied warranties of | ||
342 | 10 | #MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
343 | 11 | #PURPOSE. See the GNU General Public License for more details. | ||
344 | 12 | # | ||
345 | 13 | #You should have received a copy of the GNU General Public License along | ||
346 | 14 | #with this program. If not, see <http://www.gnu.org/licenses/>. | ||
347 | 15 | ### END LICENSE | ||
348 | 16 | |||
349 | 17 | """A Treeview for Desktop CouchDB | ||
350 | 18 | Displays and persists data in desktopcouch, handles presentation | ||
351 | 19 | of the UI in a Gtk.TreeView, as well handles persistence to a backend | ||
352 | 20 | desktopcouch database. | ||
353 | 21 | |||
354 | 22 | Using | ||
355 | 23 | #define the database and record type | ||
356 | 24 | database = "couch_widget_test" | ||
357 | 25 | record_type="test_record_type" | ||
358 | 26 | |||
359 | 27 | #create a dictionary if you don't already have one | ||
360 | 28 | dicts = [{"test?":True,"price":100,"foo count":100,"Key4":"1004"}, | ||
361 | 29 | {"test?":True,"price":100,"foo count":100,"Key4":"1004"}, | ||
362 | 30 | {"test?":True,"price":100,"foo count":100,"Key4":"1004"}] | ||
363 | 31 | |||
364 | 32 | #create the CouchGrid | ||
365 | 33 | cg = CouchGrid(database, record_type=record_type, dictionaries=dicts) | ||
366 | 34 | |||
367 | 35 | Configuring | ||
368 | 36 | #Define columns to display | ||
369 | 37 | keys=["price","test?"] | ||
370 | 38 | cg = CouchGrid(database, record_type=record_type, dictionaries=dicts,keys=keys) | ||
371 | 39 | |||
372 | 40 | #Define column types to use | ||
373 | 41 | hints = {"price": StringColumn} | ||
374 | 42 | cg = CouchGrid(database, record_type=record_type, dictionaries=dicts,keys=keys, type_hints = hints) | ||
375 | 43 | |||
376 | 44 | #A CouchGrid is a Dictionary Grid whcih is a TreeView, | ||
377 | 45 | #so you can use DicationaryGrid and TreeView members | ||
378 | 46 | cg.editable = True | ||
379 | 47 | cg.get_column(0).set_title("Price") | ||
380 | 48 | |||
381 | 49 | Extending | ||
382 | 50 | To change the way CouchGrid persists data, change _refresh_treeview to | ||
383 | 51 | handle persistence of data if needed, calling DictionaryGrid._populate_treeivew | ||
384 | 52 | to handle the UI. You should do the same with append_row. | ||
385 | 53 | |||
386 | 54 | You may also want to override _edited and _edited_toggled to handle persistence | ||
387 | 55 | when the UI is changed. | ||
388 | 56 | |||
389 | 57 | It is only useful to extend CouchGrid if you are using desktopcouch for | ||
390 | 58 | persistence. Otherwise, derive from DictionaryGrid. | ||
391 | 59 | |||
392 | 60 | """ | ||
393 | 61 | |||
394 | 62 | import gtk | ||
395 | 63 | import gobject | ||
396 | 64 | from desktopcouch.records.server import CouchDatabase | ||
397 | 65 | from desktopcouch.records.record import Record | ||
398 | 66 | from quickly.widgets.dictionary_grid import DictionaryGrid | ||
399 | 67 | from quickly.widgets.grid_column import CheckColumn | ||
400 | 68 | |||
401 | 69 | class CouchGrid(DictionaryGrid): | ||
402 | 70 | def __init__( | ||
403 | 71 | self, database_name, record_type=None, dictionaries=None, editable=False, keys=None, type_hints=None, uri=None): | ||
404 | 72 | """Create a new Couchwidget | ||
405 | 73 | arguments: | ||
406 | 74 | database_name - specify the name of the database in the desktop | ||
407 | 75 | couchdb to use. If the specified database does not exist, it | ||
408 | 76 | will be created. | ||
409 | 77 | |||
410 | 78 | optional arguments: | ||
411 | 79 | record_type - a string to specify the record_type to use in | ||
412 | 80 | retrieving and creating records. Note that if no records exist | ||
413 | 81 | in the CouchDB then the keys argument must also be used or | ||
414 | 82 | a RuntimeError will result. | ||
415 | 83 | |||
416 | 84 | dictionaries - a list of dictionaries to initialize in the | ||
417 | 85 | grid. If these haven't been added to desktopcouch, the will | ||
418 | 86 | be automatically persisted and updated using the recored_type | ||
419 | 87 | specified. Any previously saved data of the same record_type will | ||
420 | 88 | also be displayed. | ||
421 | 89 | |||
422 | 90 | keys - a list of strings specifying keys to use in | ||
423 | 91 | the columns of the CouchGrid. The keys will also be used for the | ||
424 | 92 | column titles and keys in desktop couch. | ||
425 | 93 | |||
426 | 94 | If a record does not contain a value for a specified key | ||
427 | 95 | the CouchGrid will simply display an empty cell of the | ||
428 | 96 | appropriate type. If the widget is set to editable, | ||
429 | 97 | the user will be able to add values to the database. | ||
430 | 98 | |||
431 | 99 | The types for the columns will be inferred by the key based on | ||
432 | 100 | some conventions. the key "id" is assumed to be an integer, as | ||
433 | 101 | is any key ending in " count". A key ending in "?" is assumed | ||
434 | 102 | to be a Boolean displayed with a checkbox. The key "price" is | ||
435 | 103 | assumed to be currency, as is any key ending in "count". There | ||
436 | 104 | may be others. Defaults can be overridden using type-hints. All | ||
437 | 105 | other keys will be assumed to be strings. | ||
438 | 106 | |||
439 | 107 | type-hints - a dictionary containing keys specificed for the | ||
440 | 108 | TreeView and GridColumns. Used to override types inferred | ||
441 | 109 | by convention, or for changing the type of a column from | ||
442 | 110 | the default of a string to something else. | ||
443 | 111 | |||
444 | 112 | uri - A uri for the DesktopCouch. This is only used to | ||
445 | 113 | choose a Couch database running remotely. The default is | ||
446 | 114 | to use the local desktopcouch database. | ||
447 | 115 | |||
448 | 116 | """ | ||
449 | 117 | |||
450 | 118 | if type(database_name) is not type(str()): | ||
451 | 119 | raise TypeError("database_name is required and must be a string") | ||
452 | 120 | |||
453 | 121 | #set up the database before trying to use it | ||
454 | 122 | self.uri = uri | ||
455 | 123 | self._record_type = None | ||
456 | 124 | self._db = None | ||
457 | 125 | if record_type is not None: | ||
458 | 126 | self._record_type = record_type | ||
459 | 127 | |||
460 | 128 | #QUESTION: What is the purpose of this? | ||
461 | 129 | #if dictionaries is not None and keys is None: | ||
462 | 130 | # DictionaryGrid.__init__(self, None, editable, keys, type_hints) | ||
463 | 131 | #else: | ||
464 | 132 | # DictionaryGrid.__init__(self, None, editable, keys, type_hints) | ||
465 | 133 | |||
466 | 134 | # Have to leave it in for now. But this is certainly a bug. | ||
467 | 135 | # Note: adding dicts to a CG adds to empty cols in the model between the key values and the couch dict. | ||
468 | 136 | if dictionaries is not None and keys is None: | ||
469 | 137 | DictionaryGrid.__init__(self, None, editable, keys, type_hints) | ||
470 | 138 | else: | ||
471 | 139 | DictionaryGrid.__init__(self, None, editable, keys, type_hints) | ||
472 | 140 | |||
473 | 141 | |||
474 | 142 | """ | ||
475 | 143 | if dictionaries is not None and keys is not None: | ||
476 | 144 | DictionaryGrid.__init__(self, dictionaries, editable, keys, type_hints) | ||
477 | 145 | elif keys is None: | ||
478 | 146 | DictionaryGrid.__init__(self, dictionaries, editable, None, type_hints) | ||
479 | 147 | elif dictionaries is None: | ||
480 | 148 | DictionaryGrid.__init__(self, None, editable, keys, type_hints) | ||
481 | 149 | """ | ||
482 | 150 | |||
483 | 151 | |||
484 | 152 | if self.uri: | ||
485 | 153 | self._db = CouchDatabase(database_name, create=True, uri=self.uri) | ||
486 | 154 | else: | ||
487 | 155 | self._db = CouchDatabase(database_name, create=True) | ||
488 | 156 | |||
489 | 157 | if dictionaries is not None: | ||
490 | 158 | for d in dictionaries: | ||
491 | 159 | self._persist_dict_to_couch(d) | ||
492 | 160 | |||
493 | 161 | self._refresh_treeview() | ||
494 | 162 | |||
495 | 163 | @property | ||
496 | 164 | def database(self): | ||
497 | 165 | """database - gets an instance to the CouchDB. | ||
498 | 166 | Set to a string to change the database. | ||
499 | 167 | |||
500 | 168 | """ | ||
501 | 169 | return self._db | ||
502 | 170 | |||
503 | 171 | @database.setter | ||
504 | 172 | def database(self, db_name): | ||
505 | 173 | if self.uri: | ||
506 | 174 | self._db = CouchDatabase(db_name, create=True, uri=self.uri) | ||
507 | 175 | else: | ||
508 | 176 | self._db = CouchDatabase(db_name, create=True) | ||
509 | 177 | if self.record_type != None: | ||
510 | 178 | self._refresh_treeview()#first time treeview is reset | ||
511 | 179 | |||
512 | 180 | @property | ||
513 | 181 | def record_type(self): | ||
514 | 182 | """record_type - a string specifying the record type of | ||
515 | 183 | the documents to retrieve from the CouchDB. | ||
516 | 184 | |||
517 | 185 | Will cause the TreeView to refresh when set. | ||
518 | 186 | """ | ||
519 | 187 | return self._record_type | ||
520 | 188 | |||
521 | 189 | @record_type.setter | ||
522 | 190 | def record_type(self, record_type): | ||
523 | 191 | |||
524 | 192 | #store the record type string | ||
525 | 193 | self._record_type = record_type | ||
526 | 194 | self._refresh_treeview() | ||
527 | 195 | |||
528 | 196 | @property | ||
529 | 197 | def selected_record_ids(self): | ||
530 | 198 | """ selected_record_ids - a list of document ids that are | ||
531 | 199 | selected in the CouchGrid. Throws an IndexError if | ||
532 | 200 | a specified id is not found in the list when setting | ||
533 | 201 | this property. | ||
534 | 202 | |||
535 | 203 | This property is read/write | ||
536 | 204 | |||
537 | 205 | """ | ||
538 | 206 | ids = [] | ||
539 | 207 | for row in self.selected_rows: | ||
540 | 208 | id_ = None | ||
541 | 209 | |||
542 | 210 | if "__desktopcouch_id" in row: | ||
543 | 211 | id_ = row["__desktopcouch_id"] | ||
544 | 212 | ids.append(id_) | ||
545 | 213 | return ids | ||
546 | 214 | |||
547 | 215 | @selected_record_ids.setter | ||
548 | 216 | def selected_record_ids(self, indexes): | ||
549 | 217 | rows = [] #a list of rows to select | ||
550 | 218 | for id in indexes: | ||
551 | 219 | id_found = False #track if the id was found | ||
552 | 220 | |||
553 | 221 | for i,r in enumerate(self.list_store): | ||
554 | 222 | dictionary = r[len(self.keys)] #this dictionary always last column | ||
555 | 223 | if "__desktopcouch_id" in dictionary: | ||
556 | 224 | if dictionary["__desktopcouch_id"] == id: | ||
557 | 225 | id_found = True #id was good | ||
558 | 226 | if r not in rows: #don't have duplicates to select | ||
559 | 227 | rows.append(i) | ||
560 | 228 | if not id_found: #stop if a requested id was not in the list | ||
561 | 229 | raise IndexError("id %s not found" %id) | ||
562 | 230 | |||
563 | 231 | #select the requested ids | ||
564 | 232 | selection = self.get_selection() | ||
565 | 233 | selection.unselect_all() | ||
566 | 234 | for r in rows: | ||
567 | 235 | selection.select_path(r) | ||
568 | 236 | |||
569 | 237 | def remove_selected_rows(self, delete=False): | ||
570 | 238 | rows_to_delete = self.selected_rows | ||
571 | 239 | if delete: | ||
572 | 240 | for r in rows_to_delete: | ||
573 | 241 | self.database.delete_record(r["__desktopcouch_id"]) | ||
574 | 242 | DictionaryGrid.remove_selected_rows(self) | ||
575 | 243 | |||
576 | 244 | def _refresh_treeview(self): | ||
577 | 245 | """ | ||
578 | 246 | _refresh_treeview: internal function to handle rebuilding | ||
579 | 247 | the gtk.TreeView along with columns and cell renderers. extends | ||
580 | 248 | DictionaryGrid._refresh_treeview by retrieving stored desktopcouch | ||
581 | 249 | records before calling DictionaryGrid._refresh_treeview. | ||
582 | 250 | |||
583 | 251 | _refresh_treeview is not typically called directly, | ||
584 | 252 | but may be useful to override in subclasses. | ||
585 | 253 | |||
586 | 254 | """ | ||
587 | 255 | |||
588 | 256 | #if the database is not set up, just return | ||
589 | 257 | if self._db is None or self._record_type is None: | ||
590 | 258 | return | ||
591 | 259 | |||
592 | 260 | #if keys aren't set, infer them from the collection | ||
593 | 261 | if len(self._dictionaries) > 0 and self.keys is None: | ||
594 | 262 | self._infer_keys_from_dictionaries() | ||
595 | 263 | |||
596 | 264 | #retrieve the docs for the record_type, if any | ||
597 | 265 | results = self._db.get_records( | ||
598 | 266 | record_type=self._record_type,create_view=True) | ||
599 | 267 | |||
600 | 268 | |||
601 | 269 | #if there are no rows and no keys set, there is no | ||
602 | 270 | #way to build the grid, just raise an error | ||
603 | 271 | if len(results) == 0 and self._keys is None: | ||
604 | 272 | raise RuntimeError("Cannot infer columns for CouchGrid") | ||
605 | 273 | |||
606 | 274 | dicts = [] | ||
607 | 275 | for r in results: | ||
608 | 276 | d = r.value | ||
609 | 277 | |||
610 | 278 | #hmmm, maybe make these so they get hidden rather than delete them | ||
611 | 279 | #hide the desktopcouch variabls | ||
612 | 280 | for key in d: | ||
613 | 281 | if key.startswith("_") and not key.startswith("__desktopcouch"): | ||
614 | 282 | d["__desktopcouch" + key] = d[key] | ||
615 | 283 | del(d[key]) | ||
616 | 284 | |||
617 | 285 | d["__record_type"] = d["record_type"] | ||
618 | 286 | del(d["record_type"]) | ||
619 | 287 | dicts.append(d) | ||
620 | 288 | |||
621 | 289 | self._dictionaries = dicts | ||
622 | 290 | DictionaryGrid._refresh_treeview(self) | ||
623 | 291 | |||
624 | 292 | |||
625 | 293 | # CheckColumn is special because it is a one-shot change. A StringColumn | ||
626 | 294 | # should not be saved for each keystroke, but CheckColumn should. | ||
627 | 295 | for c in self.get_columns(): | ||
628 | 296 | if type(c) == CheckColumn: | ||
629 | 297 | c.renderer.connect("toggled",self._edited_toggled, c) | ||
630 | 298 | else: | ||
631 | 299 | c.renderer.connect("edited",self._edited, c) | ||
632 | 300 | |||
633 | 301 | def append_row(self, dictionary): | ||
634 | 302 | """append_row: add a row to the TreeView and to DesktopCouch. | ||
635 | 303 | If keys are already set up only the the keys in the dictionary | ||
636 | 304 | matching the keys used for columns will be displayed, though | ||
637 | 305 | all the key value pairs will be saved to the DesktopCouch. | ||
638 | 306 | If no keys are set up, and this is the first row, keys will be | ||
639 | 307 | inferred from the dictionary keys. | ||
640 | 308 | |||
641 | 309 | arguments: | ||
642 | 310 | dictionary - a dictionary to add to the Treeview and to DesktopCouch | ||
643 | 311 | |||
644 | 312 | """ | ||
645 | 313 | |||
646 | 314 | if dictionary is None: | ||
647 | 315 | dictionary = {} | ||
648 | 316 | |||
649 | 317 | #Here we add rows to desktopcouch if needed | ||
650 | 318 | if "__desktopcouch_id" not in dictionary: | ||
651 | 319 | self._persist_dict_to_couch(dictionary) | ||
652 | 320 | DictionaryGrid.append_row(self,dictionary) | ||
653 | 321 | |||
654 | 322 | def _persist_dict_to_couch(self,dictionary): | ||
655 | 323 | """ _persist_dict_to_couch - internal implementation. may be useful | ||
656 | 324 | a subclass of CouchGrid, but not normally called directly. | ||
657 | 325 | |||
658 | 326 | """ | ||
659 | 327 | |||
660 | 328 | dictionary["record_type"] = self.record_type | ||
661 | 329 | rec = Record(dictionary) | ||
662 | 330 | #meh, best not to save an empty row | ||
663 | 331 | # Perhaps we should raise an exception if not? | ||
664 | 332 | if len(dictionary) > 1: | ||
665 | 333 | doc_id = self._db.put_record(rec) | ||
666 | 334 | dictionary["__desktopcouch_id"] = doc_id | ||
667 | 335 | dictionary["__record_type"] = self.record_type | ||
668 | 336 | del(dictionary["record_type"]) | ||
669 | 337 | |||
670 | 338 | |||
671 | 339 | def _edited_toggled(self, cell, path, col): | ||
672 | 340 | """ _edited_toggled - internal signal handler. | ||
673 | 341 | Updates the database if a cell in the Treeview | ||
674 | 342 | has been edited special cased for CheckColumns. | ||
675 | 343 | |||
676 | 344 | """ | ||
677 | 345 | |||
678 | 346 | iter = self.list_store.get_iter(path) | ||
679 | 347 | key = col.key | ||
680 | 348 | active = not cell.get_active() | ||
681 | 349 | self._edited(cell, path, active, col) | ||
682 | 350 | |||
683 | 351 | def _edited(self, cell, path, new_val, col): | ||
684 | 352 | """ _edited - internal signal handler. | ||
685 | 353 | Updates the database if a cell in the Treeview | ||
686 | 354 | has been edited. | ||
687 | 355 | |||
688 | 356 | """ | ||
689 | 357 | iter = self.list_store.get_iter(path) | ||
690 | 358 | key = col.key | ||
691 | 359 | dictionary = self.list_store.get_value(iter,len(self.keys)) | ||
692 | 360 | |||
693 | 361 | if "__desktopcouch_id" not in dictionary: #the row has not been stored | ||
694 | 362 | #create a document | ||
695 | 363 | dictionary["record_type"] = self.record_type | ||
696 | 364 | rec = Record(dictionary) | ||
697 | 365 | doc_id = self._db.put_record(rec) | ||
698 | 366 | dictionary["__desktopcouch_id"] = doc_id | ||
699 | 367 | self.list_store.set_value(iter, len(self.keys), dictionary) | ||
700 | 368 | |||
701 | 369 | else: #it has been saved | ||
702 | 370 | #get the record id from the dictionary | ||
703 | 371 | #then update the datbase with the change | ||
704 | 372 | id = dictionary["__desktopcouch_id"] | ||
705 | 373 | key = col.key | ||
706 | 374 | self._db.update_fields(id,{key:new_val}) | ||
707 | 375 | |||
708 | 376 | def __show_selected(widget, row, widgets): | ||
709 | 377 | """Test function for selection properties of CouchGrid""" | ||
710 | 378 | tv, cg = widgets | ||
711 | 379 | disp = "Rows:\n" | ||
712 | 380 | for r in cg.selected_rows: | ||
713 | 381 | disp += str(r) + "\n" | ||
714 | 382 | |||
715 | 383 | disp += "\n\n_Ids:\n" | ||
716 | 384 | for r in cg.selected_record_ids: | ||
717 | 385 | disp += str(r) + "\n" | ||
718 | 386 | |||
719 | 387 | tv.get_buffer().set_text(disp) | ||
720 | 388 | |||
721 | 389 | def __select_ids(widget, widgets): | ||
722 | 390 | """Test function for selecting ids.""" | ||
723 | 391 | entry, cg, lbl = widgets | ||
724 | 392 | cg.selected_record_ids = entry.get_text().split(",") | ||
725 | 393 | |||
726 | 394 | if __name__ == "__main__": | ||
727 | 395 | """creates a test CouchGrid if called directly""" | ||
728 | 396 | |||
729 | 397 | from quickly.widgets.grid_column import StringColumn | ||
730 | 398 | |||
731 | 399 | #create and show a test window and container | ||
732 | 400 | win = gtk.Window(gtk.WINDOW_TOPLEVEL) | ||
733 | 401 | win.set_title("CouchGrid Test Window") | ||
734 | 402 | win.connect("destroy",gtk.main_quit) | ||
735 | 403 | win.show() | ||
736 | 404 | vbox = gtk.VBox(False, False) | ||
737 | 405 | vbox.show() | ||
738 | 406 | win.add(vbox) | ||
739 | 407 | |||
740 | 408 | #create a test widget with test database values | ||
741 | 409 | dicts = [{"test?":True,"price":100,"foo count":100,"Key4":"1004"}, | ||
742 | 410 | {"test?":True,"price":100,"foo count":100,"Key4":"1004"}, | ||
743 | 411 | {"test?":True,"price":100,"foo count":100,"Key4":"1004"}] | ||
744 | 412 | |||
745 | 413 | #create some settings | ||
746 | 414 | database = "couch_widget_test" | ||
747 | 415 | record_type="test_record_type" | ||
748 | 416 | keys=["price","test?"] | ||
749 | 417 | hints = {"price": StringColumn} | ||
750 | 418 | |||
751 | 419 | #create it and part a bit | ||
752 | 420 | cg = CouchGrid(database, record_type=record_type, dictionaries=dicts,keys=keys, type_hints = hints) | ||
753 | 421 | cg.editable = True | ||
754 | 422 | cg.get_column(0).set_title("Price") | ||
755 | 423 | |||
756 | 424 | #finish out and run the test UI | ||
757 | 425 | cg.show() | ||
758 | 426 | vbox.pack_start(cg, False, True) | ||
759 | 427 | hbox = gtk.HBox(False, 5) | ||
760 | 428 | hbox.show() | ||
761 | 429 | tv = gtk.TextView() | ||
762 | 430 | tv.show() | ||
763 | 431 | tv.set_wrap_mode(gtk.WRAP_CHAR) | ||
764 | 432 | tv.set_size_request(300,-1) | ||
765 | 433 | cg.connect("selection-changed",__show_selected, (tv,cg)) | ||
766 | 434 | |||
767 | 435 | id_vbox = gtk.VBox(False, 5) | ||
768 | 436 | id_vbox.show() | ||
769 | 437 | |||
770 | 438 | fb_lbl = gtk.Label("paste ids into the edit box to select them") | ||
771 | 439 | fb_lbl.show() | ||
772 | 440 | |||
773 | 441 | entry = gtk.Entry() | ||
774 | 442 | entry.show() | ||
775 | 443 | |||
776 | 444 | btn = gtk.Button("select ids") | ||
777 | 445 | btn.show() | ||
778 | 446 | btn.connect("clicked", __select_ids, (entry,cg, fb_lbl)) | ||
779 | 447 | |||
780 | 448 | id_vbox.pack_start(fb_lbl, False, False) | ||
781 | 449 | id_vbox.pack_start(entry, False, False) | ||
782 | 450 | id_vbox.pack_end(btn, False, False) | ||
783 | 451 | |||
784 | 452 | hbox.pack_start(tv, False, False) | ||
785 | 453 | vbox.pack_end(hbox, False, False) | ||
786 | 454 | hbox.pack_end(id_vbox, False, False) | ||
787 | 455 | |||
788 | 456 | #run the test app | ||
789 | 457 | gtk.main() | ||
790 | 458 | |||
791 | 459 | 0 | ||
792 | === modified file 'quickly/widgets/dictionary_grid.py' | |||
793 | --- quickly/widgets/dictionary_grid.py 2011-09-04 23:58:24 +0000 | |||
794 | +++ quickly/widgets/dictionary_grid.py 2012-03-06 08:52:21 +0000 | |||
795 | @@ -14,10 +14,10 @@ | |||
796 | 14 | #You should have received a copy of the GNU General Public License along | 14 | #You should have received a copy of the GNU General Public License along |
797 | 15 | #with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | #with this program. If not, see <http://www.gnu.org/licenses/>. |
798 | 16 | ### END LICENSE | 16 | ### END LICENSE |
803 | 17 | """A gtk.TreeView for Dictionaries | 17 | """A Gtk.TreeView for Dictionaries |
804 | 18 | Displays and persists data in a gtk.TreeView. Handles the | 18 | Displays and persists data in a Gtk.TreeView. Handles the |
805 | 19 | set up of the gtk.TreeView, gtk.ListModel, gtk.TreeViewColumns, | 19 | set up of the Gtk.TreeView, Gtk.ListModel, Gtk.TreeViewColumns, |
806 | 20 | and gtk.CellRenderers. | 20 | and Gtk.CellRenderers. |
807 | 21 | 21 | ||
808 | 22 | Using | 22 | Using |
809 | 23 | #create a dictionary if you don't already have one | 23 | #create a dictionary if you don't already have one |
810 | @@ -40,7 +40,7 @@ | |||
811 | 40 | hints = {"price": StringColumn} | 40 | hints = {"price": StringColumn} |
812 | 41 | dg = CouchGrid(dictionaries=dicts,keys=keys, type_hints = hints) | 41 | dg = CouchGrid(dictionaries=dicts,keys=keys, type_hints = hints) |
813 | 42 | 42 | ||
815 | 43 | #A CouchGrid is gtk.TreeView, so you can use gtk.TreeView members | 43 | #A CouchGrid is Gtk.TreeView, so you can use Gtk.TreeView members |
816 | 44 | dg.get_column(0).set_title("Price") | 44 | dg.get_column(0).set_title("Price") |
817 | 45 | 45 | ||
818 | 46 | #Use the selection-changed signal and read from the DictionaryGrid | 46 | #Use the selection-changed signal and read from the DictionaryGrid |
819 | @@ -67,13 +67,13 @@ | |||
820 | 67 | 67 | ||
821 | 68 | """ | 68 | """ |
822 | 69 | 69 | ||
825 | 70 | import gtk | 70 | from gi.repository import GObject |
826 | 71 | import gobject | 71 | from gi.repository import Gtk |
827 | 72 | import conventions | 72 | import conventions |
829 | 73 | from quickly.widgets.grid_column import StringColumn | 73 | from grid_column import StringColumn |
830 | 74 | from grid_column import CheckColumn | 74 | from grid_column import CheckColumn |
831 | 75 | 75 | ||
833 | 76 | class DictionaryGrid(gtk.TreeView): | 76 | class DictionaryGrid(Gtk.TreeView): |
834 | 77 | __gtype_name__ = "DictionaryGrid" | 77 | __gtype_name__ = "DictionaryGrid" |
835 | 78 | 78 | ||
836 | 79 | def __init__(self, dictionaries=None, editable = False, keys=None, type_hints=None): | 79 | def __init__(self, dictionaries=None, editable = False, keys=None, type_hints=None): |
837 | @@ -102,7 +102,7 @@ | |||
838 | 102 | 102 | ||
839 | 103 | """ | 103 | """ |
840 | 104 | 104 | ||
842 | 105 | gtk.TreeView.__init__(self) | 105 | Gtk.TreeView.__init__(self) |
843 | 106 | self.list_store = None | 106 | self.list_store = None |
844 | 107 | self.unfiltered_store = None | 107 | self.unfiltered_store = None |
845 | 108 | self._keys = keys | 108 | self._keys = keys |
846 | @@ -115,7 +115,7 @@ | |||
847 | 115 | self._type_hints = {} | 115 | self._type_hints = {} |
848 | 116 | else: | 116 | else: |
849 | 117 | self._type_hints = type_hints | 117 | self._type_hints = type_hints |
851 | 118 | self.get_selection().set_mode(gtk.SELECTION_MULTIPLE) | 118 | self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) |
852 | 119 | self._refresh_treeview() | 119 | self._refresh_treeview() |
853 | 120 | 120 | ||
854 | 121 | #signal handlers to track selection in the treeview | 121 | #signal handlers to track selection in the treeview |
855 | @@ -193,7 +193,7 @@ | |||
856 | 193 | # themselves, but for now this works. | 193 | # themselves, but for now this works. |
857 | 194 | 194 | ||
858 | 195 | for column in self.get_columns(): | 195 | for column in self.get_columns(): |
860 | 196 | column.set_editable(editable) | 196 | column.set_editable(editable) |
861 | 197 | self._editable = editable | 197 | self._editable = editable |
862 | 198 | 198 | ||
863 | 199 | @property | 199 | @property |
864 | @@ -251,7 +251,7 @@ | |||
865 | 251 | def _refresh_treeview(self): | 251 | def _refresh_treeview(self): |
866 | 252 | """ | 252 | """ |
867 | 253 | _refresh_treeview: internal function to handle rebuilding | 253 | _refresh_treeview: internal function to handle rebuilding |
869 | 254 | the gtk.TreeView along with columns and cell renderers.. | 254 | the Gtk.TreeView along with columns and cell renderers.. |
870 | 255 | 255 | ||
871 | 256 | _refresh_treeview is not typically called directly, | 256 | _refresh_treeview is not typically called directly, |
872 | 257 | but may be useful to override in subclasses. | 257 | but may be useful to override in subclasses. |
873 | @@ -350,7 +350,7 @@ | |||
874 | 350 | """ | 350 | """ |
875 | 351 | remove_selected_rows: removes the rows currently selected | 351 | remove_selected_rows: removes the rows currently selected |
876 | 352 | in the TreeView UI from the TreeView as well as the backing | 352 | in the TreeView UI from the TreeView as well as the backing |
878 | 353 | gtk.ListStore. | 353 | Gtk.ListStore. |
879 | 354 | 354 | ||
880 | 355 | """ | 355 | """ |
881 | 356 | 356 | ||
882 | @@ -361,11 +361,11 @@ | |||
883 | 361 | return | 361 | return |
884 | 362 | 362 | ||
885 | 363 | #store the last selected row to reselect after removal | 363 | #store the last selected row to reselect after removal |
887 | 364 | next_to_select = rows[-1][0] + 1 - len(rows) | 364 | next_to_select = rows[-1].get_indices()[0] + 1 - len(rows) |
888 | 365 | 365 | ||
889 | 366 | #loop through and remove | 366 | #loop through and remove |
890 | 367 | 367 | ||
892 | 368 | if type(model) is not gtk.ListStore: | 368 | if type(model) is not Gtk.ListStore: |
893 | 369 | iters = [model.get_model().get_iter(path) for path in rows] | 369 | iters = [model.get_model().get_iter(path) for path in rows] |
894 | 370 | store_iters = [] | 370 | store_iters = [] |
895 | 371 | 371 | ||
896 | @@ -431,8 +431,8 @@ | |||
897 | 431 | 431 | ||
898 | 432 | #create the liststore with the designated types | 432 | #create the liststore with the designated types |
899 | 433 | #the last column is always for storing the backing dict | 433 | #the last column is always for storing the backing dict |
902 | 434 | col_types.append(gobject.TYPE_PYOBJECT) | 434 | col_types.append(GObject.TYPE_PYOBJECT) |
903 | 435 | self.list_store = gtk.ListStore(*col_types) | 435 | self.list_store = Gtk.ListStore(*col_types) |
904 | 436 | 436 | ||
905 | 437 | for c in self.get_columns(): | 437 | for c in self.get_columns(): |
906 | 438 | self.__last_sorted_col = None | 438 | self.__last_sorted_col = None |
907 | @@ -459,11 +459,11 @@ | |||
908 | 459 | self.__last_sorted_col.set_sort_indicator(False) | 459 | self.__last_sorted_col.set_sort_indicator(False) |
909 | 460 | self.__last_sorted_col = column | 460 | self.__last_sorted_col = column |
910 | 461 | 461 | ||
913 | 462 | __gsignals__ = {'cell-edited' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 462 | __gsignals__ = {'cell-edited' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
914 | 463 | (gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT)), | 463 | (GObject.TYPE_PYOBJECT,GObject.TYPE_PYOBJECT,GObject.TYPE_PYOBJECT,GObject.TYPE_PYOBJECT,GObject.TYPE_PYOBJECT)), |
915 | 464 | 464 | ||
918 | 465 | 'selection-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 465 | 'selection-changed' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, |
919 | 466 | (gobject.TYPE_PYOBJECT,)) | 466 | (GObject.TYPE_PYOBJECT,)) |
920 | 467 | } | 467 | } |
921 | 468 | 468 | ||
922 | 469 | def __show_selected(widget, selected_rows, data=None): | 469 | def __show_selected(widget, selected_rows, data=None): |
923 | @@ -488,14 +488,14 @@ | |||
924 | 488 | {"key?": True, "price":33.00,"tags" : "ccc ddd eee","_foo":"bar","bing count":15}, | 488 | {"key?": True, "price":33.00,"tags" : "ccc ddd eee","_foo":"bar","bing count":15}, |
925 | 489 | {"ID": 3, "tags" : "ddd eee fff","_foo":"bar"}, | 489 | {"ID": 3, "tags" : "ddd eee fff","_foo":"bar"}, |
926 | 490 | {"ID": 4, "price":5.00,"_foo":"bar"}] | 490 | {"ID": 4, "price":5.00,"_foo":"bar"}] |
929 | 491 | #create and show a test window | 491 | #create and show a test windowp |
930 | 492 | win = gtk.Window(gtk.WINDOW_TOPLEVEL) | 492 | win = Gtk.Window() |
931 | 493 | win.set_title("DictionaryGrid Test Window") | 493 | win.set_title("DictionaryGrid Test Window") |
933 | 494 | win.connect("destroy",gtk.main_quit) | 494 | win.connect("destroy",Gtk.main_quit) |
934 | 495 | win.show() | 495 | win.show() |
935 | 496 | 496 | ||
936 | 497 | #create a top level container | 497 | #create a top level container |
938 | 498 | vbox = gtk.VBox(False, False) | 498 | vbox = Gtk.VBox(False, False) |
939 | 499 | vbox.show() | 499 | vbox.show() |
940 | 500 | win.add(vbox) | 500 | win.add(vbox) |
941 | 501 | 501 | ||
942 | @@ -507,19 +507,19 @@ | |||
943 | 507 | 507 | ||
944 | 508 | #show the control, add it to the window, and run the main loop | 508 | #show the control, add it to the window, and run the main loop |
945 | 509 | grid.show() | 509 | grid.show() |
947 | 510 | vbox.pack_start(grid, False, True) | 510 | vbox.pack_start(grid, False, False, 0) |
948 | 511 | 511 | ||
949 | 512 | #create a test display area | 512 | #create a test display area |
951 | 513 | hbox = gtk.HBox(False, 5) | 513 | hbox = Gtk.HBox(False, 5) |
952 | 514 | hbox.show() | 514 | hbox.show() |
954 | 515 | tv = gtk.TextView() | 515 | tv = Gtk.TextView() |
955 | 516 | tv.show() | 516 | tv.show() |
956 | 517 | grid.connect("selection-changed",__show_selected, tv) | 517 | grid.connect("selection-changed",__show_selected, tv) |
957 | 518 | grid.connect("cell-edited",__on_edited, tv) | 518 | grid.connect("cell-edited",__on_edited, tv) |
958 | 519 | 519 | ||
961 | 520 | hbox.pack_start(tv, False, False) | 520 | hbox.pack_start(tv, False, False, 0) |
962 | 521 | vbox.pack_end(hbox, False, False) | 521 | vbox.pack_end(hbox, False, False, 0) |
963 | 522 | 522 | ||
964 | 523 | #run the test app | 523 | #run the test app |
966 | 524 | gtk.main() | 524 | Gtk.main() |
967 | 525 | 525 | ||
968 | 526 | 526 | ||
969 | === modified file 'quickly/widgets/grid_column.py' | |||
970 | --- quickly/widgets/grid_column.py 2011-09-04 23:58:24 +0000 | |||
971 | +++ quickly/widgets/grid_column.py 2012-03-06 08:52:21 +0000 | |||
972 | @@ -23,8 +23,8 @@ | |||
973 | 23 | to control the type of column used for a key in a dictionary. | 23 | to control the type of column used for a key in a dictionary. |
974 | 24 | 24 | ||
975 | 25 | Customizing | 25 | Customizing |
978 | 26 | The column types in this module are all descendants of gtk.TreeView, so | 26 | The column types in this module are all descendants of Gtk.TreeView, so |
979 | 27 | you can use all of the gtk.TreeView methods and properties to control | 27 | you can use all of the Gtk.TreeView methods and properties to control |
980 | 28 | how a grid column looks or works. | 28 | how a grid column looks or works. |
981 | 29 | 29 | ||
982 | 30 | #Find a grid column and change the title | 30 | #Find a grid column and change the title |
983 | @@ -34,7 +34,7 @@ | |||
984 | 34 | 34 | ||
985 | 35 | Extending | 35 | Extending |
986 | 36 | To display data in a column with a string, such as to display words and | 36 | To display data in a column with a string, such as to display words and |
988 | 37 | numbers, you should extend StringColumn. Otherwise, extend gtk.TreeView | 37 | numbers, you should extend StringColumn. Otherwise, extend Gtk.TreeView |
989 | 38 | directly. In either case, you will need to implement a set of functions. | 38 | directly. In either case, you will need to implement a set of functions. |
990 | 39 | 39 | ||
991 | 40 | A Grid Column must track two different data, a "real" value, which is tracked | 40 | A Grid Column must track two different data, a "real" value, which is tracked |
992 | @@ -56,7 +56,7 @@ | |||
993 | 56 | a row does not contain a key, value pair for the specified column. For example | 56 | a row does not contain a key, value pair for the specified column. For example |
994 | 57 | StringColumn returns an empty string ("") | 57 | StringColumn returns an empty string ("") |
995 | 58 | 58 | ||
997 | 59 | A new column type will often require a specially configured gtk.CellRenderer. | 59 | A new column type will often require a specially configured Gtk.CellRenderer. |
998 | 60 | If you are deriving from StringColumn, but are using a custom renderer, | 60 | If you are deriving from StringColumn, but are using a custom renderer, |
999 | 61 | you need to override the _initialize_renderer method, and set the | 61 | you need to override the _initialize_renderer method, and set the |
1000 | 62 | columns renderer property to the renderer. You should also connect the | 62 | columns renderer property to the renderer. You should also connect the |
1001 | @@ -68,8 +68,8 @@ | |||
1002 | 68 | _initialize_renderer: | 68 | _initialize_renderer: |
1003 | 69 | 69 | ||
1004 | 70 | def _initialize_renderer( self, editable, index ): | 70 | def _initialize_renderer( self, editable, index ): |
1007 | 71 | self.renderer = gtk.CellRendererSpin() | 71 | self.renderer = Gtk.CellRendererSpin() |
1008 | 72 | adj = gtk.Adjustment(0,-10000000000,10000000000,1) | 72 | adj = Gtk.Adjustment(0,-10000000000,10000000000,1) |
1009 | 73 | self.renderer.set_property("adjustment", adj) | 73 | self.renderer.set_property("adjustment", adj) |
1010 | 74 | self.renderer.set_property("editable", editable) | 74 | self.renderer.set_property("editable", editable) |
1011 | 75 | self.renderer.set_property("digits",2) | 75 | self.renderer.set_property("digits",2) |
1012 | @@ -89,26 +89,20 @@ | |||
1013 | 89 | """ | 89 | """ |
1014 | 90 | 90 | ||
1015 | 91 | 91 | ||
1016 | 92 | |||
1017 | 93 | import sys | 92 | import sys |
1018 | 94 | import datetime | 93 | import datetime |
1019 | 95 | import gettext | 94 | import gettext |
1020 | 96 | from gettext import gettext as _ | 95 | from gettext import gettext as _ |
1021 | 97 | gettext.textdomain('quickly-widgets') | 96 | gettext.textdomain('quickly-widgets') |
1022 | 98 | 97 | ||
1036 | 99 | try: | 98 | import grid_filter |
1037 | 100 | import pygtk | 99 | |
1038 | 101 | pygtk.require("2.0") | 100 | from gi.repository import GObject |
1039 | 102 | import gtk | 101 | from gi.repository import Gtk |
1040 | 103 | import gobject | 102 | from gi.repository import Gdk |
1041 | 104 | import grid_filter | 103 | |
1042 | 105 | 104 | ||
1043 | 106 | 105 | class GridColumn( Gtk.TreeViewColumn ): | |
1031 | 107 | except Exception, inst: | ||
1032 | 108 | print "some dependencies for GridFilter are not available" | ||
1033 | 109 | raise inst | ||
1034 | 110 | |||
1035 | 111 | class GridColumn( gtk.TreeViewColumn ): | ||
1044 | 112 | """GridColumn - Base class that provides features that is important | 106 | """GridColumn - Base class that provides features that is important |
1045 | 113 | to all columns used in a DictionaryGrid or decendants. Currently | 107 | to all columns used in a DictionaryGrid or decendants. Currently |
1046 | 114 | it's useful only to StringColumn and CheckColumn, but should make it | 108 | it's useful only to StringColumn and CheckColumn, but should make it |
1047 | @@ -116,7 +110,7 @@ | |||
1048 | 116 | """ | 110 | """ |
1049 | 117 | 111 | ||
1050 | 118 | def __init__(self, key, index, dictionary_index, renderer, editable=True, format_function = None ): | 112 | def __init__(self, key, index, dictionary_index, renderer, editable=True, format_function = None ): |
1052 | 119 | gtk.TreeViewColumn.__init__(self, key, renderer, text=index) | 113 | Gtk.TreeViewColumn.__init__(self, key, renderer, text=index) |
1053 | 120 | 114 | ||
1054 | 121 | self.set_clickable(True) | 115 | self.set_clickable(True) |
1055 | 122 | self.set_resizable(True) | 116 | self.set_resizable(True) |
1056 | @@ -124,6 +118,7 @@ | |||
1057 | 124 | self.index = index | 118 | self.index = index |
1058 | 125 | self.key = key | 119 | self.key = key |
1059 | 126 | self.dictionary_index = dictionary_index | 120 | self.dictionary_index = dictionary_index |
1060 | 121 | # Why should list_store be set to None? | ||
1061 | 127 | self.list_store = None | 122 | self.list_store = None |
1062 | 128 | 123 | ||
1063 | 129 | 124 | ||
1064 | @@ -135,21 +130,22 @@ | |||
1065 | 135 | 130 | ||
1066 | 136 | rows = [tuple(r) + (i,) for i, r in enumerate(self.list_store)] | 131 | rows = [tuple(r) + (i,) for i, r in enumerate(self.list_store)] |
1067 | 137 | 132 | ||
1071 | 138 | # I NEED TO HAVE A LOOK AT THIS IF-BLOCK. At least, it needs a comment. | 133 | # Sort opposite of last time |
1072 | 139 | if sort_order == gtk.SORT_ASCENDING: | 134 | if sort_order == Gtk.SortType.ASCENDING: |
1073 | 140 | sort_order = gtk.SORT_DESCENDING | 135 | sort_order = Gtk.SortType.DESCENDING |
1074 | 141 | else: | 136 | else: |
1076 | 142 | sort_order = gtk.SORT_ASCENDING | 137 | sort_order = Gtk.SortType.ASCENDING |
1077 | 143 | 138 | ||
1078 | 144 | self.set_sort_order(sort_order) | 139 | self.set_sort_order(sort_order) |
1079 | 145 | self.set_sort_indicator(True) | 140 | self.set_sort_indicator(True) |
1080 | 146 | 141 | ||
1082 | 147 | if sort_order == gtk.SORT_ASCENDING: | 142 | if sort_order == Gtk.SortType.ASCENDING: |
1083 | 148 | rows.sort(self._sort_ascending) | 143 | rows.sort(self._sort_ascending) |
1084 | 149 | else: | 144 | else: |
1085 | 150 | rows.sort(self._sort_descending) | 145 | rows.sort(self._sort_descending) |
1086 | 151 | 146 | ||
1088 | 152 | self.list_store.reorder([r[-1] for r in rows]) | 147 | # Where does self.list_store come from? |
1089 | 148 | self.list_store.set_sort_column_id(self.index, sort_order) | ||
1090 | 153 | 149 | ||
1091 | 154 | def set_editable(self, editable): | 150 | def set_editable(self, editable): |
1092 | 155 | self.renderer.set_property("editable", editable) | 151 | self.renderer.set_property("editable", editable) |
1093 | @@ -164,7 +160,7 @@ | |||
1094 | 164 | 160 | ||
1095 | 165 | """ | 161 | """ |
1096 | 166 | 162 | ||
1098 | 167 | column_type = gobject.TYPE_STRING | 163 | column_type = GObject.TYPE_STRING |
1099 | 168 | __sort_order = None | 164 | __sort_order = None |
1100 | 169 | default_filter = grid_filter.StringFilterBox | 165 | default_filter = grid_filter.StringFilterBox |
1101 | 170 | def __init__(self, key, index, dictionary_index, editable=True, format_function = None ): | 166 | def __init__(self, key, index, dictionary_index, editable=True, format_function = None ): |
1102 | @@ -247,7 +243,7 @@ | |||
1103 | 247 | cell_renderer - a reference to the specific cell_renderer that is | 243 | cell_renderer - a reference to the specific cell_renderer that is |
1104 | 248 | formatting the string | 244 | formatting the string |
1105 | 249 | 245 | ||
1107 | 250 | tree_model - the gtk.ListStore that is the backing data for the | 246 | tree_model - the Gtk.ListStore that is the backing data for the |
1108 | 251 | DictionaryGrid that contains the column. | 247 | DictionaryGrid that contains the column. |
1109 | 252 | 248 | ||
1110 | 253 | iter - an iterator that references the row of the the DictionaryGrid | 249 | iter - an iterator that references the row of the the DictionaryGrid |
1111 | @@ -274,8 +270,7 @@ | |||
1112 | 274 | 270 | ||
1113 | 275 | """ | 271 | """ |
1114 | 276 | 272 | ||
1117 | 277 | self.renderer = gtk.CellRendererText() | 273 | self.renderer = Gtk.CellRendererText() |
1116 | 278 | self.renderer.mode = gtk.CELL_RENDERER_MODE_EDITABLE | ||
1118 | 279 | self.renderer.set_property("editable", editable) | 274 | self.renderer.set_property("editable", editable) |
1119 | 280 | self.renderer.connect("edited", self._cell_edited) | 275 | self.renderer.connect("edited", self._cell_edited) |
1120 | 281 | 276 | ||
1121 | @@ -340,14 +335,14 @@ | |||
1122 | 340 | return "" | 335 | return "" |
1123 | 341 | 336 | ||
1124 | 342 | class CurrencyColumn( StringColumn ): | 337 | class CurrencyColumn( StringColumn ): |
1126 | 343 | """CurrencyColumn - display data in currency format. Uses a gtk.Spinner | 338 | """CurrencyColumn - display data in currency format. Uses a Gtk.Spinner |
1127 | 344 | to display data and support editing if enabled. Store real values as float. | 339 | to display data and support editing if enabled. Store real values as float. |
1128 | 345 | 340 | ||
1129 | 346 | Inherits from StringColumn. | 341 | Inherits from StringColumn. |
1130 | 347 | 342 | ||
1131 | 348 | """ | 343 | """ |
1132 | 349 | 344 | ||
1134 | 350 | column_type = gobject.TYPE_STRING | 345 | column_type = GObject.TYPE_STRING |
1135 | 351 | default_filter = grid_filter.NumericFilterBox | 346 | default_filter = grid_filter.NumericFilterBox |
1136 | 352 | def __init__(self, key, index,dictionary_index, editable=True ): | 347 | def __init__(self, key, index,dictionary_index, editable=True ): |
1137 | 353 | """Creates a CurrencyColumn | 348 | """Creates a CurrencyColumn |
1138 | @@ -380,8 +375,8 @@ | |||
1139 | 380 | 375 | ||
1140 | 381 | """ | 376 | """ |
1141 | 382 | 377 | ||
1144 | 383 | self.renderer = gtk.CellRendererSpin() | 378 | self.renderer = Gtk.CellRendererSpin() |
1145 | 384 | adj = gtk.Adjustment(0,-10000000000,10000000000,1) | 379 | adj = Gtk.Adjustment(0,-10000000000,10000000000,1) |
1146 | 385 | self.renderer.set_property("adjustment", adj) | 380 | self.renderer.set_property("adjustment", adj) |
1147 | 386 | self.renderer.set_property("editable", editable) | 381 | self.renderer.set_property("editable", editable) |
1148 | 387 | self.renderer.set_property("digits",2) | 382 | self.renderer.set_property("digits",2) |
1149 | @@ -494,19 +489,19 @@ | |||
1150 | 494 | 489 | ||
1151 | 495 | """ | 490 | """ |
1152 | 496 | 491 | ||
1154 | 497 | column_type = gobject.TYPE_STRING | 492 | column_type = GObject.TYPE_STRING |
1155 | 498 | default_filter = grid_filter.TagsFilterBox | 493 | default_filter = grid_filter.TagsFilterBox |
1156 | 499 | 494 | ||
1157 | 500 | 495 | ||
1158 | 501 | class IntegerColumn( StringColumn ): | 496 | class IntegerColumn( StringColumn ): |
1160 | 502 | """IntegerColumn - display data in Integer format. Uses a gtk.Spinner | 497 | """IntegerColumn - display data in Integer format. Uses a Gtk.Spinner |
1161 | 503 | to display data and support editing if enabled. Store real values as int. | 498 | to display data and support editing if enabled. Store real values as int. |
1162 | 504 | 499 | ||
1163 | 505 | Inherits from StringColumn. | 500 | Inherits from StringColumn. |
1164 | 506 | 501 | ||
1165 | 507 | """ | 502 | """ |
1166 | 508 | 503 | ||
1168 | 509 | column_type = gobject.TYPE_STRING | 504 | column_type = GObject.TYPE_STRING |
1169 | 510 | default_filter = grid_filter.IntegerFilterBox | 505 | default_filter = grid_filter.IntegerFilterBox |
1170 | 511 | 506 | ||
1171 | 512 | def __init__(self, key, index, dictionary_index, editable=True ): | 507 | def __init__(self, key, index, dictionary_index, editable=True ): |
1172 | @@ -529,8 +524,8 @@ | |||
1173 | 529 | StringColumn.__init__( self, key, index, dictionary_index, editable) | 524 | StringColumn.__init__( self, key, index, dictionary_index, editable) |
1174 | 530 | 525 | ||
1175 | 531 | def _initialize_renderer( self, editable, index ): | 526 | def _initialize_renderer( self, editable, index ): |
1178 | 532 | self.renderer = gtk.CellRendererSpin() | 527 | self.renderer = Gtk.CellRendererSpin() |
1179 | 533 | adj = gtk.Adjustment(0,-10000000000,10000000000,1) | 528 | adj = Gtk.Adjustment(0,-10000000000,10000000000,1) |
1180 | 534 | self.renderer.set_property("adjustment", adj) | 529 | self.renderer.set_property("adjustment", adj) |
1181 | 535 | self.renderer.set_property("editable", editable) | 530 | self.renderer.set_property("editable", editable) |
1182 | 536 | self.renderer.connect("edited", self._cell_edited) | 531 | self.renderer.connect("edited", self._cell_edited) |
1183 | @@ -640,11 +635,11 @@ | |||
1184 | 640 | class CheckColumn( GridColumn ): | 635 | class CheckColumn( GridColumn ): |
1185 | 641 | """CheckColumn - display data as checkboxes. Store real values as bool. | 636 | """CheckColumn - display data as checkboxes. Store real values as bool. |
1186 | 642 | 637 | ||
1188 | 643 | Inherits from gtk.TreeViewColumn. | 638 | Inherits from Gtk.TreeViewColumn. |
1189 | 644 | 639 | ||
1190 | 645 | """ | 640 | """ |
1191 | 646 | 641 | ||
1193 | 647 | column_type = gobject.TYPE_INT | 642 | column_type = GObject.TYPE_INT |
1194 | 648 | default_filter = grid_filter.CheckFilterBox | 643 | default_filter = grid_filter.CheckFilterBox |
1195 | 649 | 644 | ||
1196 | 650 | def __init__(self, key, index, dictionary_index, editable=True, format_function = None ): | 645 | def __init__(self, key, index, dictionary_index, editable=True, format_function = None ): |
1197 | @@ -684,7 +679,7 @@ | |||
1198 | 684 | y = y[self.index] | 679 | y = y[self.index] |
1199 | 685 | return x - y | 680 | return x - y |
1200 | 686 | 681 | ||
1202 | 687 | def _on_format(self,column, cell_renderer, tree_model, iter): | 682 | def _on_format(self,column, cell_renderer, tree_model, iter, format_function): |
1203 | 688 | cell_val = tree_model.get_value(iter, self.index) | 683 | cell_val = tree_model.get_value(iter, self.index) |
1204 | 689 | cell_renderer.set_property('inconsistent', False) | 684 | cell_renderer.set_property('inconsistent', False) |
1205 | 690 | if cell_val == 1: | 685 | if cell_val == 1: |
1206 | @@ -698,9 +693,9 @@ | |||
1207 | 698 | self.extra_format_function() | 693 | self.extra_format_function() |
1208 | 699 | 694 | ||
1209 | 700 | def _initialize_renderer( self, editable, index ): | 695 | def _initialize_renderer( self, editable, index ): |
1211 | 701 | self.renderer = gtk.CellRendererToggle() | 696 | self.renderer = Gtk.CellRendererToggle() |
1212 | 702 | self.renderer.set_property("activatable", editable) | 697 | self.renderer.set_property("activatable", editable) |
1214 | 703 | col = gtk.TreeViewColumn(self.key, self.renderer, active=index) | 698 | col = Gtk.TreeViewColumn(self.key, self.renderer, active=index) |
1215 | 704 | self.renderer.connect("toggled", self.toggled) | 699 | self.renderer.connect("toggled", self.toggled) |
1216 | 705 | 700 | ||
1217 | 706 | def toggled(self, cell, path, data=None): | 701 | def toggled(self, cell, path, data=None): |
1218 | @@ -772,14 +767,14 @@ | |||
1219 | 772 | return bool(val) | 767 | return bool(val) |
1220 | 773 | 768 | ||
1221 | 774 | class DateColumn( StringColumn ): | 769 | class DateColumn( StringColumn ): |
1223 | 775 | """DateColumn - display data in date format. Uses a gtk.Calendar | 770 | """DateColumn - display data in date format. Uses a Gtk.Calendar |
1224 | 776 | to display data and support editing if enabled. Store real values as tuple. | 771 | to display data and support editing if enabled. Store real values as tuple. |
1225 | 777 | 772 | ||
1226 | 778 | Inherits from StringColumn. | 773 | Inherits from StringColumn. |
1227 | 779 | 774 | ||
1228 | 780 | """ | 775 | """ |
1229 | 781 | 776 | ||
1231 | 782 | column_type = gobject.TYPE_STRING | 777 | column_type = GObject.TYPE_STRING |
1232 | 783 | default_filter = grid_filter.DateFilterBox | 778 | default_filter = grid_filter.DateFilterBox |
1233 | 784 | 779 | ||
1234 | 785 | def __init__(self, key, index,dictionary_index, editable=True ): | 780 | def __init__(self, key, index,dictionary_index, editable=True ): |
1235 | @@ -816,27 +811,27 @@ | |||
1236 | 816 | self.renderer.set_property('editable',self._editable) | 811 | self.renderer.set_property('editable',self._editable) |
1237 | 817 | self.renderer.connect("edited", self._cell_edited) | 812 | self.renderer.connect("edited", self._cell_edited) |
1238 | 818 | 813 | ||
1240 | 819 | class CellRendererDate(gtk.CellRendererText): | 814 | class CellRendererDate(Gtk.CellRendererText): |
1241 | 820 | def __init__(self): | 815 | def __init__(self): |
1243 | 821 | gtk.CellRendererText.__init__(self) | 816 | Gtk.CellRendererText.__init__(self) |
1244 | 822 | self.date_format = '%Y-%m-%d' | 817 | self.date_format = '%Y-%m-%d' |
1245 | 823 | 818 | ||
1246 | 824 | self.calendar_window = None | 819 | self.calendar_window = None |
1247 | 825 | self.calendar = None | 820 | self.calendar = None |
1248 | 826 | 821 | ||
1249 | 827 | def _create_calendar(self, treeview): | 822 | def _create_calendar(self, treeview): |
1251 | 828 | self.calendar_window = gtk.Dialog(parent=treeview.get_toplevel()) | 823 | self.calendar_window = Gtk.Dialog(parent=treeview.get_toplevel()) |
1252 | 829 | self.calendar_window.action_area.hide() | 824 | self.calendar_window.action_area.hide() |
1253 | 830 | self.calendar_window.set_decorated(False) | 825 | self.calendar_window.set_decorated(False) |
1254 | 831 | self.calendar_window.set_property('skip-taskbar-hint', True) | 826 | self.calendar_window.set_property('skip-taskbar-hint', True) |
1255 | 832 | 827 | ||
1258 | 833 | self.calendar = gtk.Calendar() | 828 | self.calendar = Gtk.Calendar() |
1259 | 834 | self.calendar.display_options(gtk.CALENDAR_SHOW_DAY_NAMES | gtk.CALENDAR_SHOW_HEADING) | 829 | self.calendar.display_options(Gtk.CalendarDisplayOptions.SHOW_DAY_NAMES | Gtk.CalendarDisplayOptions.SHOW_HEADING) |
1260 | 835 | self.calendar.connect('day-selected-double-click', self._day_selected, None) | 830 | self.calendar.connect('day-selected-double-click', self._day_selected, None) |
1261 | 836 | self.calendar.connect('key-press-event', self._day_selected) | 831 | self.calendar.connect('key-press-event', self._day_selected) |
1262 | 837 | self.calendar.connect('focus-out-event', self._selection_cancelled) | 832 | self.calendar.connect('focus-out-event', self._selection_cancelled) |
1263 | 838 | self.calendar_window.set_transient_for(None) # cancel the modality of dialog | 833 | self.calendar_window.set_transient_for(None) # cancel the modality of dialog |
1265 | 839 | self.calendar_window.vbox.pack_start(self.calendar) | 834 | self.calendar_window.vbox.pack_start(self.calendar, True, True, 0) |
1266 | 840 | 835 | ||
1267 | 841 | # necessary for getting the (width, height) of calendar_window | 836 | # necessary for getting the (width, height) of calendar_window |
1268 | 842 | self.calendar.show() | 837 | self.calendar.show() |
1269 | @@ -871,20 +866,20 @@ | |||
1270 | 871 | 866 | ||
1271 | 872 | response = self.calendar_window.run() | 867 | response = self.calendar_window.run() |
1272 | 873 | self.calendar_window.hide() | 868 | self.calendar_window.hide() |
1274 | 874 | if response == gtk.RESPONSE_OK: | 869 | if response == Gtk.ResponseType.OK: |
1275 | 875 | (year, month, day) = self.calendar.get_date() | 870 | (year, month, day) = self.calendar.get_date() |
1277 | 876 | date = datetime.date(year, month + 1, day).strftime(self.date_format) # gtk.Calendar's month starts from zero | 871 | date = datetime.date(year, month + 1, day).strftime(self.date_format) # Gtk.Calendar's month starts from zero |
1278 | 877 | self.emit('edited', path, date) | 872 | self.emit('edited', path, date) |
1279 | 878 | 873 | ||
1281 | 879 | return None # don't return any editable, our gtk.Dialog did the work already | 874 | return None # don't return any editable, our Gtk.Dialog did the work already |
1282 | 880 | 875 | ||
1283 | 881 | def _day_selected(self, calendar, event): | 876 | def _day_selected(self, calendar, event): |
1284 | 882 | # event == None for day selected via doubleclick | 877 | # event == None for day selected via doubleclick |
1287 | 883 | if not event or event.type == gtk.gdk.KEY_PRESS and gtk.gdk.keyval_name(event.keyval) == 'Return': | 878 | if not event or event.type == Gdk.EventType.KEY_PRESS and Gdk.keyval_name(event.keyval) == 'Return': |
1288 | 884 | self.calendar_window.response(gtk.RESPONSE_OK) | 879 | self.calendar_window.response(Gtk.ResponseType.OK) |
1289 | 885 | return True | 880 | return True |
1290 | 886 | 881 | ||
1291 | 887 | def _selection_cancelled(self, calendar, event): | 882 | def _selection_cancelled(self, calendar, event): |
1293 | 888 | self.calendar_window.response(gtk.RESPONSE_CANCEL) | 883 | self.calendar_window.response(Gtk.ResponseType.CANCEL) |
1294 | 889 | return True | 884 | return True |
1295 | 890 | 885 | ||
1296 | 891 | 886 | ||
1297 | === modified file 'quickly/widgets/grid_filter.py' | |||
1298 | --- quickly/widgets/grid_filter.py 2010-12-18 16:06:52 +0000 | |||
1299 | +++ quickly/widgets/grid_filter.py 2012-03-06 08:52:21 +0000 | |||
1300 | @@ -15,12 +15,12 @@ | |||
1301 | 15 | ### END LICENSE | 15 | ### END LICENSE |
1302 | 16 | 16 | ||
1303 | 17 | """Widgets and Objects for filtering a DictionaryGrid | 17 | """Widgets and Objects for filtering a DictionaryGrid |
1305 | 18 | GridFilter is as gtk.VBox that provides filtering UI for a | 18 | GridFilter is as Gtk.VBox that provides filtering UI for a |
1306 | 19 | DictionaryGrid. Provides multiple filters, and choosing | 19 | DictionaryGrid. Provides multiple filters, and choosing |
1307 | 20 | between "And" and "Or" filtering. Provides default filters appropriate | 20 | between "And" and "Or" filtering. Provides default filters appropriate |
1308 | 21 | for each column. | 21 | for each column. |
1309 | 22 | 22 | ||
1311 | 23 | GridFilter row is a gtk.HBox that is a container for displaying FilterCombos. | 23 | GridFilter row is a Gtk.HBox that is a container for displaying FilterCombos. |
1312 | 24 | 24 | ||
1313 | 25 | FilterCombos display a filter and handle filtering of rows to | 25 | FilterCombos display a filter and handle filtering of rows to |
1314 | 26 | display and hide in the associated DictionaryGrid. The GridColumns | 26 | display and hide in the associated DictionaryGrid. The GridColumns |
1315 | @@ -60,374 +60,370 @@ | |||
1316 | 60 | self.append("starts with",lambda x,y: x.startswith(y)) | 60 | self.append("starts with",lambda x,y: x.startswith(y)) |
1317 | 61 | self.append("ends with",lambda x,y: x.endswith(y)) | 61 | self.append("ends with",lambda x,y: x.endswith(y)) |
1318 | 62 | 62 | ||
1321 | 63 | Filter UI could be created to use widgets other than gtk.Combo so long as | 63 | Filter UI could be created to use widgets other than Gtk.Combo so long as |
1322 | 64 | the widget has a get_model function that returns a gtk.ListStore with | 64 | the widget has a get_model function that returns a Gtk.ListStore with |
1323 | 65 | filtering functions stored as the last value (column) in the liststore. | 65 | filtering functions stored as the last value (column) in the liststore. |
1324 | 66 | 66 | ||
1325 | 67 | """ | 67 | """ |
1326 | 68 | 68 | ||
1327 | 69 | |||
1328 | 69 | import sys | 70 | import sys |
1329 | 70 | import datetime | 71 | import datetime |
1330 | 71 | import gettext | 72 | import gettext |
1331 | 72 | from gettext import gettext as _ | 73 | from gettext import gettext as _ |
1332 | 73 | gettext.textdomain('quickly-widgets') | 74 | gettext.textdomain('quickly-widgets') |
1333 | 74 | 75 | ||
1459 | 75 | try: | 76 | from gi.repository import GObject |
1460 | 76 | import pygtk | 77 | from gi.repository import Gtk |
1461 | 77 | pygtk.require("2.0") | 78 | |
1462 | 78 | import gtk | 79 | class GridFilter( Gtk.VBox ): |
1463 | 79 | import gobject | 80 | """GridFilter: A widget that provides a user interface for filtering a |
1464 | 80 | 81 | treeview. A GridFilter hosts one ore more GridRows, which in turn host | |
1465 | 81 | except Exception, inst: | 82 | an active filter. |
1466 | 82 | print "some dependencies for GridFilter are not available" | 83 | """ |
1467 | 83 | raise inst | 84 | |
1468 | 84 | 85 | def __init__(self, grid, filter_hints={} ): | |
1469 | 85 | class GridFilter( gtk.VBox ): | 86 | """Create a GridFilter for filtering an associated treeview. |
1470 | 86 | """GridFilter: A widget that provides a user interface for filtering a | 87 | This class is used by BugsPane. |
1471 | 87 | treeview. A GridFilter hosts one ore more GridRows, which in turn host | 88 | |
1472 | 88 | an active filter. | 89 | arguments: |
1473 | 89 | 90 | grid - A DictionaryGrid to filter | |
1474 | 90 | """ | 91 | |
1475 | 91 | def __init__(self, grid, filter_hints={} ): | 92 | options arguments: |
1476 | 92 | """Create a GridFilter for filtering an associated treeview. | 93 | filter_hints - a dictionary of column keys to FilterCombo types to |
1477 | 93 | This class is used by BugsPane. | 94 | provide custom filtering. |
1478 | 94 | 95 | ||
1479 | 95 | arguments: | 96 | """ |
1480 | 96 | grid - A DictionaryGrid to filter | 97 | |
1481 | 97 | 98 | Gtk.VBox.__init__( self, False, 10 ) | |
1482 | 98 | options arguments: | 99 | self.grid = grid |
1483 | 99 | filter_hints - a dictionary of column keys to FilterCombo types to | 100 | self.store = grid.get_model() |
1484 | 100 | provide custom filtering. | 101 | self.filter_hints = filter_hints |
1485 | 101 | 102 | ||
1486 | 102 | """ | 103 | #create the and/or radio buttons |
1487 | 103 | 104 | radio_box = Gtk.HBox(False,2) | |
1488 | 104 | gtk.VBox.__init__( self, False, 10 ) | 105 | radio_box.show() |
1489 | 105 | self.grid = grid | 106 | self.pack_start(radio_box, False, False, 0) |
1490 | 106 | self.store = grid.get_model() | 107 | self.and_button = Gtk.RadioButton.new_with_label_from_widget(None,_("M_atch All of the following")) |
1491 | 107 | self.filter_hints = filter_hints | 108 | self.and_button.show() |
1492 | 108 | 109 | self.and_button.connect("toggled",self.__filter_changed) | |
1493 | 109 | #create the and/or radio buttons | 110 | radio_box.pack_start(self.and_button, False, False, 0) |
1494 | 110 | radio_box = gtk.HBox(False,2) | 111 | or_button = Gtk.RadioButton.new_with_label_from_widget(self.and_button,_("Match any _of the following")) |
1495 | 111 | radio_box.show() | 112 | or_button.show() |
1496 | 112 | self.pack_start(radio_box, False, False) | 113 | radio_box.pack_start(or_button, False, False, 0) |
1497 | 113 | self.and_button = gtk.RadioButton(None,_("M_atch All of the following"), True) | 114 | self.rows = [] |
1498 | 114 | self.and_button.show() | 115 | self._add_row(self) |
1499 | 115 | self.and_button.connect("toggled",self.__filter_changed) | 116 | |
1500 | 116 | radio_box.pack_start(self.and_button, False, False) | 117 | def _add_row(self, widget, data=None): |
1501 | 117 | or_button = gtk.RadioButton(self.and_button,_("Match any _of the following"), True) | 118 | """_add_row: internal signal handler that receives a request |
1502 | 118 | or_button.show() | 119 | from a FilterRow to add a new row. Sets up and adds the row to the GridFilter. |
1503 | 119 | radio_box.pack_start(or_button, False, False) | 120 | |
1504 | 120 | self.rows = [] | 121 | Do not call directly |
1505 | 121 | self._add_row(self) | 122 | """ |
1506 | 122 | 123 | ||
1507 | 123 | def _add_row(self, widget, data=None): | 124 | #TODO: I suppose this is leaking references to filter rows |
1508 | 124 | """_add_row: internal signal handler that receives a request | 125 | row = FilterRow(self.grid, len(self.rows) > 0, self.filter_hints ) |
1509 | 125 | from a FilterRow to add a new row. Sets up and adds the row to the GridFilter. | 126 | row.connect('add_row_requested',self._add_row) |
1510 | 126 | 127 | row.connect('remove_row_requested',self._remove_row) | |
1511 | 127 | Do not call directly | 128 | row.connect('refilter_requested',self.__filter_changed) |
1512 | 128 | """ | 129 | row.show() |
1513 | 129 | 130 | self.rows.append(row) | |
1514 | 130 | #TODO: I suppose this is leaking references to filter rows | 131 | self.pack_start(row, False, False, 0) |
1515 | 131 | row = FilterRow(self.grid, len(self.rows) > 0, self.filter_hints ) | 132 | |
1516 | 132 | row.connect('add_row_requested',self._add_row) | 133 | def _remove_row(self, widget, data=None): |
1517 | 133 | row.connect('remove_row_requested',self._remove_row) | 134 | """_remove_row: internal signal handler that receives a |
1518 | 134 | row.connect('refilter_requested',self.__filter_changed) | 135 | request from a FilterRow to remove itself from the GridFilter. |
1519 | 135 | row.show() | 136 | |
1520 | 136 | self.rows.append(row) | 137 | Do not call directly |
1521 | 137 | self.pack_start(row, False, False) | 138 | """ |
1522 | 138 | 139 | ||
1523 | 139 | def _remove_row(self, widget, data=None): | 140 | self.rows.remove(widget) |
1524 | 140 | """_remove_row: internal signal handler that receives a | 141 | self.remove(widget) |
1525 | 141 | request from a FilterRow to remove itself from the GridFilter. | 142 | self.__filter_changed(self) |
1526 | 142 | 143 | ||
1527 | 143 | Do not call directly | 144 | def __filter_changed(self,widget, data=None): |
1528 | 144 | """ | 145 | """__filter_changed: internal signal handler that handles |
1529 | 145 | 146 | requests to reapply the fitlers in the GridFilter's FilterRows. | |
1530 | 146 | self.rows.remove(widget) | 147 | |
1531 | 147 | self.remove(widget) | 148 | """ |
1532 | 148 | self.__filter_changed(self) | 149 | |
1533 | 149 | 150 | filt = self.store.filter_new() | |
1534 | 150 | def __filter_changed(self,widget, data=None): | 151 | sort_mod = Gtk.TreeModelSort(model=filt) |
1535 | 151 | """__filter_changed: internal signal handler that handles | 152 | filt.set_visible_func(self.__filter_func, data ) |
1536 | 152 | requests to reapply the fitlers in the GridFilter's FilterRows. | 153 | filt.refilter() |
1537 | 153 | 154 | self.grid.set_model(sort_mod) | |
1538 | 154 | """ | 155 | |
1539 | 155 | 156 | def __filter_func(self, model, iter, data): | |
1540 | 156 | filt = self.store.filter_new() | 157 | """filter_func: called for each row in the treeview model in response to |
1541 | 157 | sort_mod = gtk.TreeModelSort(filt) | 158 | a __filter_changed signal. Determines for each row whether it should be |
1542 | 158 | filt.set_visible_func(self.__filter_func, data ) | 159 | visible based on the FilterRows in the GridFilter. |
1543 | 159 | filt.refilter() | 160 | |
1544 | 160 | self.grid.set_model(sort_mod) | 161 | |
1545 | 161 | 162 | Do not call directly | |
1546 | 162 | def __filter_func(self, model, iter, data): | 163 | """ |
1547 | 163 | """filter_func: called for each row in the treeview model in response to | 164 | #determine whether this is an "and" or an "or" filter |
1548 | 164 | a __filter_changed signal. Determines for each row whether it should be | 165 | match_all = self.and_button.get_active() |
1549 | 165 | visible based on the FilterRows in the GridFilter. | 166 | |
1550 | 166 | 167 | for r in self.rows: | |
1551 | 167 | 168 | rez = r.is_match(iter.copy(),model) #check the result of each filter | |
1552 | 168 | Do not call directly | 169 | if match_all: #if it's an "and" filter |
1553 | 169 | """ | 170 | if not rez: #and if the filter does not match |
1554 | 170 | #determine whether this is an "and" or an "or" filter | 171 | return False #then the row should not be visible |
1555 | 171 | match_all = self.and_button.get_active() | 172 | else: #but if it's an "or" filter |
1556 | 172 | 173 | if rez: #and it is a match | |
1557 | 173 | for r in self.rows: | 174 | return True #return that the row should be visible |
1558 | 174 | rez = r.is_match(iter.copy(),model) #check the result of each filter | 175 | return match_all #all filters match an "and" or none matched an "or" |
1559 | 175 | if match_all: #if it's an "and" filter | 176 | |
1560 | 176 | if not rez: #and if the filter does not match | 177 | class FilterRow( Gtk.HBox): |
1561 | 177 | return False #then the row should not be visible | 178 | """FilterRow: A widget that displays a single filter in a GridFilter. |
1562 | 178 | else: #but if it's an "or" filter | 179 | Typically, this class will not be used directly, but only via a GridFilter. |
1563 | 179 | if rez: #and it is a match | 180 | |
1564 | 180 | return True #return that the row should be visible | 181 | """ |
1565 | 181 | return match_all #all filters match an "and" or none matched an "or" | 182 | wait_for_input = False |
1566 | 182 | 183 | ||
1567 | 183 | class FilterRow( gtk.HBox): | 184 | def __init__(self, grid, removable=True, filter_hints={}): |
1568 | 184 | """FilterRow: A widget that displays a single filter in a GridFilter. | 185 | """Create a FilterRow to be used in a GridFilter. |
1569 | 185 | Typically, this class will not be used directly, but only via a GridFilter. | 186 | A FitlerRow is comprised of a combo that lists the treeview headings. |
1570 | 186 | 187 | The combo stores the string to display for the heading, as well as | |
1571 | 187 | """ | 188 | the widget that is used to filter each heading. When the user changes |
1572 | 188 | wait_for_input = False | 189 | the value in the dropdown, the FilterRow retrieves the correct filter from |
1573 | 189 | 190 | the combo, and displays that filter to the user. | |
1574 | 190 | def __init__(self, grid, removable=True, filter_hints={}): | 191 | |
1575 | 191 | """Create a FilterRow to be used in a GridFilter. | 192 | The FilterRow also handles offering UI for the user to add and remove |
1576 | 192 | A FitlerRow is comprised of a combo that lists the treeview headings. | 193 | FilterRows for the GridFilter containing it. |
1452 | 193 | The combo stores the string to display for the heading, as well as | ||
1453 | 194 | the widget that is used to filter each heading. When the user changes | ||
1454 | 195 | the value in the dropdown, the FilterRow retrieves the correct filter from | ||
1455 | 196 | the combo, and displays that filter to the user. | ||
1456 | 197 | |||
1457 | 198 | The FilterRow also handles offering UI for the user to add and remove | ||
1458 | 199 | FilterRows for the GridFilter containing it. | ||
1577 | 200 | 194 | ||
1601 | 201 | grid - | 195 | grid - |
1602 | 202 | 196 | ||
1603 | 203 | keyword arguments: | 197 | keyword arguments: |
1604 | 204 | removable - True if the row should be able to be removed from the GridFilter | 198 | removable - True if the row should be able to be removed from the GridFilter |
1605 | 205 | Typicall False for the first row. | 199 | Typicall False for the first row. |
1606 | 206 | 200 | ||
1607 | 207 | filter_hints - a dictionary of keys mapped to custom filters to apply to the | 201 | filter_hints - a dictionary of keys mapped to custom filters to apply to the |
1608 | 208 | column designated by the key | 202 | column designated by the key |
1609 | 209 | 203 | ||
1610 | 210 | """ | 204 | """ |
1611 | 211 | 205 | ||
1612 | 212 | gtk.HBox.__init__( self, False, 10 ) | 206 | Gtk.HBox.__init__( self, False, 10 ) |
1613 | 213 | self.store = grid.get_model() | 207 | self.store = grid.get_model() |
1614 | 214 | self.grid = grid | 208 | self.grid = grid |
1615 | 215 | 209 | ||
1616 | 216 | heading_combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT,gobject.TYPE_INT) | 210 | heading_combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT,GObject.TYPE_INT) |
1617 | 217 | 211 | ||
1618 | 218 | #apply default combos | 212 | |
1619 | 219 | for i, k in enumerate(self.grid.keys): | 213 | #apply default combos |
1620 | 220 | if k in filter_hints: | 214 | for i, k in enumerate(self.grid.keys): |
1621 | 221 | filt_combo = filter_hints[k] | 215 | if k in filter_hints: |
1622 | 222 | else: | 216 | filt_combo = filter_hints[k] |
1623 | 223 | filt_combo = grid.get_columns()[i].default_filter() | 217 | else: |
1624 | 218 | filt_combo = grid.get_columns()[i].default_filter() | ||
1625 | 224 | 219 | ||
1702 | 225 | column_title = grid.get_columns()[i].get_title() | 220 | column_title = grid.get_columns()[i].get_title() |
1703 | 226 | heading_combo_store.append([column_title,filt_combo,i]) | 221 | heading_combo_store.append([column_title,filt_combo,i]) |
1704 | 227 | 222 | ||
1705 | 228 | filt_combo.connect("changed",self.__filter_changed) | 223 | filt_combo.connect("changed",self.__filter_changed) |
1706 | 229 | filt_combo.show() | 224 | filt_combo.show() |
1707 | 230 | 225 | ||
1708 | 231 | self.column_combo = gtk.ComboBox(heading_combo_store) | 226 | self.column_combo = Gtk.ComboBox.new_with_model(heading_combo_store) |
1709 | 232 | cell = gtk.CellRendererText() | 227 | cell = Gtk.CellRendererText() |
1710 | 233 | self.column_combo.pack_start(cell, True) | 228 | self.column_combo.pack_start(cell, True) |
1711 | 234 | self.column_combo.add_attribute(cell, 'text', 0) | 229 | self.column_combo.add_attribute(cell, 'text', 0) |
1712 | 235 | 230 | ||
1713 | 236 | self.filter_space = gtk.HBox(False,1) | 231 | self.filter_space = Gtk.HBox(False,1) |
1714 | 237 | self.filter_space.show() | 232 | self.filter_space.show() |
1715 | 238 | 233 | ||
1716 | 239 | self.column_combo.show() | 234 | self.column_combo.show() |
1717 | 240 | vb = gtk.VBox(False, 5) | 235 | vb = Gtk.VBox(False, 5) |
1718 | 241 | vb.show() | 236 | vb.show() |
1719 | 242 | vb.pack_start(self.column_combo, True, False) | 237 | vb.pack_start(self.column_combo, True, False, 0) |
1720 | 243 | self.pack_start(vb,False, False) | 238 | self.pack_start(vb,False, False, 0) |
1721 | 244 | self.column_combo.connect("changed",self.__column_changed) | 239 | self.column_combo.connect("changed",self.__column_changed) |
1722 | 245 | self.column_combo.set_active(0) | 240 | self.column_combo.set_active(0) |
1723 | 246 | 241 | ||
1724 | 247 | self.pack_start(self.filter_space, False, False) | 242 | self.pack_start(self.filter_space, False, False, 0) |
1725 | 248 | 243 | ||
1726 | 249 | button_box = gtk.HBox(False,2) | 244 | button_box = Gtk.HBox(False,2) |
1727 | 250 | button_box.show() | 245 | button_box.show() |
1728 | 251 | self.pack_start(button_box,False,False) | 246 | self.pack_start(button_box,False,False, 0) |
1729 | 252 | 247 | ||
1730 | 253 | #add a button that can create a new row in the grid filter | 248 | #add a button that can create a new row in the grid filter |
1731 | 254 | add_button = gtk.Button(stock = gtk.STOCK_ADD) | 249 | add_button = Gtk.Button(stock = Gtk.STOCK_ADD) |
1732 | 255 | add_button.show() | 250 | add_button.show() |
1733 | 256 | vb2 = gtk.VBox(False, 5) | 251 | vb2 = Gtk.VBox(False, 5) |
1734 | 257 | vb2.show() | 252 | vb2.show() |
1735 | 258 | vb2.pack_start(add_button, True, False) | 253 | vb2.pack_start(add_button, True, False, 0) |
1736 | 259 | button_box.pack_start(vb2, False, False) | 254 | button_box.pack_start(vb2, False, False, 0) |
1737 | 260 | add_button.connect("clicked",lambda x: self.emit('add_row_requested',self) ) | 255 | add_button.connect("clicked",lambda x: self.emit('add_row_requested',self) ) |
1738 | 261 | 256 | ||
1739 | 262 | #add a button to remove the row if applicable | 257 | #add a button to remove the row if applicable |
1740 | 263 | if removable: | 258 | if removable: |
1741 | 264 | rm_button = gtk.Button(stock = gtk.STOCK_REMOVE) | 259 | rm_button = Gtk.Button(stock = Gtk.STOCK_REMOVE) |
1742 | 265 | rm_button.show() | 260 | rm_button.show() |
1743 | 266 | vb3 = gtk.VBox(False, 5) | 261 | vb3 = Gtk.VBox(False, 5) |
1744 | 267 | vb3.show() | 262 | vb3.show() |
1745 | 268 | vb3.pack_start(rm_button, True, False) | 263 | vb3.pack_start(rm_button, True, False, 0) |
1746 | 269 | rm_button.connect('clicked', lambda x: self.emit("remove_row_requested",self) ) | 264 | rm_button.connect('clicked', lambda x: self.emit("remove_row_requested",self) ) |
1747 | 270 | button_box.pack_start(vb3) | 265 | button_box.pack_start(vb3, True, True, 0) |
1748 | 271 | 266 | ||
1749 | 272 | __gsignals__ = {'add_row_requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 267 | __gsignals__ = {'add_row_requested' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
1750 | 273 | (gobject.TYPE_PYOBJECT,)), | 268 | (GObject.TYPE_PYOBJECT,)), |
1751 | 274 | 'remove_row_requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 269 | 'remove_row_requested' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
1752 | 275 | (gobject.TYPE_PYOBJECT,)), | 270 | (GObject.TYPE_PYOBJECT,)), |
1753 | 276 | 'refilter_requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 271 | 'refilter_requested' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
1754 | 277 | (gobject.TYPE_PYOBJECT,)) | 272 | (GObject.TYPE_PYOBJECT,)) |
1755 | 278 | } | 273 | } |
1756 | 279 | 274 | ||
1757 | 280 | def __column_changed(self, widget, data = None): | 275 | |
1758 | 281 | """column_changed: internal signal handler for the user changing | 276 | def __column_changed(self, widget, data = None): |
1759 | 282 | the combo for the column that they wish to apply the filter to. | 277 | """column_changed: internal signal handler for the user changing |
1760 | 283 | removes the other filter widgets and replaces them widgets stored in | 278 | the combo for the column that they wish to apply the filter to. |
1761 | 284 | the filter widget. | 279 | removes the other filter widgets and replaces them widgets stored in |
1762 | 285 | 280 | the filter widget. | |
1763 | 286 | """ | 281 | |
1764 | 287 | 282 | """ | |
1765 | 288 | if len(self.filter_space.get_children()) > 0: | 283 | |
1766 | 289 | self.filter_space.remove(self.filter_space.get_children()[0]) | 284 | if len(self.filter_space.get_children()) > 0: |
1767 | 290 | iter = widget.get_model().get_iter(widget.get_active()) | 285 | self.filter_space.remove(self.filter_space.get_children()[0]) |
1768 | 291 | filter_box = widget.get_model().get_value(iter,1) | 286 | iter = widget.get_model().get_iter(widget.get_active()) |
1769 | 292 | self.filter_space.pack_start(filter_box, False, False) | 287 | filter_box = widget.get_model().get_value(iter,1) |
1770 | 293 | 288 | self.filter_space.pack_start(filter_box, False, False, 0) | |
1771 | 294 | def __filter_changed(self,widget, data=None): | 289 | |
1772 | 295 | """filter_changed: internal signal handler called when the FilterRow has changed. | 290 | def __filter_changed(self,widget, data=None): |
1773 | 296 | Used to tell the GridFilter to refilter. Only emits if the filter is | 291 | """filter_changed: internal signal handler called when the FilterRow has changed. |
1774 | 297 | active (a heading is selected in the combo and the user has entered | 292 | Used to tell the GridFilter to refilter. Only emits if the filter is |
1775 | 298 | text in the filter. | 293 | active (a heading is selected in the combo and the user has entered |
1776 | 299 | 294 | text in the filter. | |
1777 | 300 | """ | 295 | |
1778 | 296 | """ | ||
1779 | 301 | 297 | ||
1861 | 302 | #if not self.wait_for_input: | 298 | #if not self.wait_for_input: |
1862 | 303 | #if self.__get_current_filter_combo().get_active > -1: | 299 | #if self.__get_current_filter_combo().get_active > -1: |
1863 | 304 | self.emit('refilter_requested',self) | 300 | self.emit('refilter_requested',self) |
1864 | 305 | 301 | ||
1865 | 306 | def __get_current_filter_combo(self): | 302 | def __get_current_filter_combo(self): |
1866 | 307 | """get_current_filter_combo: internal function that retrieves | 303 | """get_current_filter_combo: internal function that retrieves |
1867 | 308 | the combobox stored for the filter for the user selected treeview column. | 304 | the combobox stored for the filter for the user selected treeview column. |
1868 | 309 | 305 | ||
1869 | 310 | """ | 306 | """ |
1870 | 311 | iter = self.column_combo.get_model().get_iter(self.column_combo.get_active()) | 307 | iter = self.column_combo.get_model().get_iter(self.column_combo.get_active()) |
1871 | 312 | return self.column_combo.get_model().get_value(iter,1) | 308 | return self.column_combo.get_model().get_value(iter,1) |
1872 | 313 | 309 | ||
1873 | 314 | def is_match(self, store_iter, model): | 310 | def is_match(self, store_iter, model): |
1874 | 315 | """is_match: returns true if the filter set in the FilterRow matches | 311 | """is_match: returns true if the filter set in the FilterRow matches |
1875 | 316 | the value specified in the column and row. Used to determine whether | 312 | the value specified in the column and row. Used to determine whether |
1876 | 317 | to hide or show a row. | 313 | to hide or show a row. |
1877 | 318 | 314 | ||
1878 | 319 | Typically called for each treeview row and each FilterRow in response | 315 | Typically called for each treeview row and each FilterRow in response |
1879 | 320 | to a change in one of the FilterRows. | 316 | to a change in one of the FilterRows. |
1880 | 321 | 317 | ||
1881 | 322 | arguments: | 318 | arguments: |
1882 | 323 | store_iter: the iter pointing the the row in the treeview to test | 319 | store_iter: the iter pointing the the row in the treeview to test |
1883 | 324 | model: the treeview model containing the rows being tested | 320 | model: the treeview model containing the rows being tested |
1884 | 325 | 321 | ||
1885 | 326 | """ | 322 | """ |
1886 | 327 | col_iter = self.column_combo.get_model().get_iter(self.column_combo.get_active()) | 323 | col_iter = self.column_combo.get_model().get_iter(self.column_combo.get_active()) |
1887 | 328 | filter_widget = self.column_combo.get_model().get_value(col_iter,1) | 324 | filter_widget = self.column_combo.get_model().get_value(col_iter,1) |
1888 | 329 | treeview_col = self.column_combo.get_model().get_value(col_iter,2) | 325 | treeview_col = self.column_combo.get_model().get_value(col_iter,2) |
1889 | 330 | 326 | ||
1890 | 331 | orig_val = model.get_value(store_iter.copy(), treeview_col) | 327 | orig_val = model.get_value(store_iter.copy(), treeview_col) |
1891 | 332 | return filter_widget.filter(orig_val) | 328 | return filter_widget.filter(orig_val) |
1892 | 333 | 329 | ||
1893 | 334 | class BlankFilterBox( gtk.HBox): | 330 | class BlankFilterBox( Gtk.HBox): |
1894 | 335 | """BlankFilterBox provides a base class for FilterCombos, as | 331 | """BlankFilterBox provides a base class for FilterCombos, as |
1895 | 336 | well as an empty combo that can be used without subclassing | 332 | well as an empty combo that can be used without subclassing |
1896 | 337 | by calling BlankFilterBox.append | 333 | by calling BlankFilterBox.append |
1816 | 338 | |||
1817 | 339 | """ | ||
1818 | 340 | __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | ||
1819 | 341 | (gobject.TYPE_PYOBJECT,)), | ||
1820 | 342 | } | ||
1821 | 343 | |||
1822 | 344 | |||
1823 | 345 | def __init__(self): | ||
1824 | 346 | """create a BlankFilterBox | ||
1825 | 347 | |||
1826 | 348 | """ | ||
1827 | 349 | |||
1828 | 350 | gtk.HBox.__init__(self,False) | ||
1829 | 351 | self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT) | ||
1830 | 352 | self.combo = gtk.ComboBox(self.__combo_store) | ||
1831 | 353 | cell = gtk.CellRendererText() | ||
1832 | 354 | self.combo.pack_start(cell, True) | ||
1833 | 355 | self.combo.add_attribute(cell, 'text', 0) | ||
1834 | 356 | self.combo.show() | ||
1835 | 357 | self.combo.connect("changed",self.__changed) | ||
1836 | 358 | self.entry = gtk.Entry() | ||
1837 | 359 | self.entry.show() | ||
1838 | 360 | self.entry.connect("changed",self.__changed) | ||
1839 | 361 | self.pack_start(self.combo, False, False) | ||
1840 | 362 | self.pack_start(self.entry) | ||
1841 | 363 | |||
1842 | 364 | def filter(self, orig_val): | ||
1843 | 365 | if self.combo.get_active() == -1: | ||
1844 | 366 | return True | ||
1845 | 367 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) | ||
1846 | 368 | filt_func = self.combo.get_model().get_value(filt_iter,1) | ||
1847 | 369 | target_val = self.entry.get_text() | ||
1848 | 370 | return filt_func(orig_val, self.entry.get_text()) | ||
1849 | 371 | |||
1850 | 372 | def __changed(self, widget, data=None): | ||
1851 | 373 | self.emit("changed",data) | ||
1852 | 374 | |||
1853 | 375 | def append(self, text, func): | ||
1854 | 376 | """append: adds a row to the FilterCombo that includes a | ||
1855 | 377 | string to display in the combo, and a function to determine | ||
1856 | 378 | if a row should displayed or hidden by the filter. | ||
1857 | 379 | |||
1858 | 380 | func should take a value indicated by text, and a value entered by | ||
1859 | 381 | the user in the supplied gtk.TextEntry, and return True if the | ||
1860 | 382 | row should be displayed or False if it should be hidden. | ||
1897 | 383 | 334 | ||
1898 | 384 | """ | 335 | """ |
1901 | 385 | 336 | __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, | |
1902 | 386 | self.__combo_store.append([text, func]) | 337 | (GObject.TYPE_PYOBJECT,)), |
1903 | 338 | } | ||
1904 | 339 | |||
1905 | 340 | |||
1906 | 341 | def __init__(self): | ||
1907 | 342 | """create a BlankFilterBox | ||
1908 | 343 | |||
1909 | 344 | """ | ||
1910 | 345 | |||
1911 | 346 | Gtk.HBox.__init__(self,False) | ||
1912 | 347 | self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT) | ||
1913 | 348 | self.combo = Gtk.ComboBox.new_with_model(self.__combo_store) | ||
1914 | 349 | cell = Gtk.CellRendererText() | ||
1915 | 350 | self.combo.pack_start(cell, True) | ||
1916 | 351 | self.combo.add_attribute(cell, 'text', 0) | ||
1917 | 352 | self.combo.show() | ||
1918 | 353 | self.combo.connect("changed",self.__changed) | ||
1919 | 354 | self.entry = Gtk.Entry() | ||
1920 | 355 | self.entry.show() | ||
1921 | 356 | self.entry.connect("changed",self.__changed) | ||
1922 | 357 | self.pack_start(self.combo, False, False, 0) | ||
1923 | 358 | self.pack_start(self.entry, True, True, 0) | ||
1924 | 359 | |||
1925 | 360 | def filter(self, orig_val): | ||
1926 | 361 | if self.combo.get_active() == -1: | ||
1927 | 362 | return True | ||
1928 | 363 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) | ||
1929 | 364 | filt_func = self.combo.get_model().get_value(filt_iter,1) | ||
1930 | 365 | target_val = self.entry.get_text() | ||
1931 | 366 | return filt_func(orig_val, self.entry.get_text()) | ||
1932 | 367 | |||
1933 | 368 | def __changed(self, widget, data=None): | ||
1934 | 369 | self.emit("changed",data) | ||
1935 | 370 | |||
1936 | 371 | def append(self, text, func): | ||
1937 | 372 | """append: adds a row to the FilterCombo that includes a | ||
1938 | 373 | string to display in the combo, and a function to determine | ||
1939 | 374 | if a row should displayed or hidden by the filter. | ||
1940 | 375 | |||
1941 | 376 | func should take a value indicated by text, and a value entered by | ||
1942 | 377 | the user in the supplied Gtk.TextEntry, and return True if the | ||
1943 | 378 | row should be displayed or False if it should be hidden. | ||
1944 | 379 | |||
1945 | 380 | """ | ||
1946 | 381 | |||
1947 | 382 | self.__combo_store.append([text, func]) | ||
1948 | 387 | 383 | ||
1949 | 388 | class StringFilterBox( BlankFilterBox ): | 384 | class StringFilterBox( BlankFilterBox ): |
1951 | 389 | """StringFilterBox: A default string filter class for use in a FilterRow. | 385 | """StringFilterBox: A default string filter class for use in a FilterRow. |
1952 | 390 | 386 | ||
1953 | 391 | Lets the user specify if the row should be displayed based on | 387 | Lets the user specify if the row should be displayed based on |
1954 | 392 | containing, not containing, starting with, or ending with a user specified | 388 | containing, not containing, starting with, or ending with a user specified |
1955 | 393 | string. | 389 | string. |
1956 | 394 | 390 | ||
1957 | 395 | |||
1958 | 396 | """ | 391 | """ |
1989 | 397 | def __init__(self): | 392 | |
1990 | 398 | """create a StringFilterBox. | 393 | def __init__(self): |
1991 | 399 | 394 | """create a StringFilterBox. | |
1992 | 400 | """ | 395 | |
1993 | 401 | 396 | """ | |
1994 | 402 | BlankFilterBox.__init__(self) | 397 | |
1995 | 403 | self.append(_("contains"),self.contains) | 398 | BlankFilterBox.__init__(self) |
1996 | 404 | self.append(_("does not contain"),self.not_contains) | 399 | self.append(_("contains"),self.contains) |
1997 | 405 | self.append(_("starts with"),self.starts_with) | 400 | self.append(_("does not contain"),self.not_contains) |
1998 | 406 | self.append(_("ends with"),self.ends_with) | 401 | self.append(_("starts with"),self.starts_with) |
1999 | 407 | 402 | self.append(_("ends with"),self.ends_with) | |
2000 | 408 | def contains(self, orig_val, target_val): | 403 | |
2001 | 409 | if len(self.entry.get_text()) == 0 or orig_val is None: | 404 | def contains(self, orig_val, target_val): |
2002 | 410 | return True | 405 | if len(self.entry.get_text()) == 0 or orig_val is None: |
2003 | 411 | return orig_val.find(target_val) > -1 | 406 | return True |
2004 | 412 | 407 | return orig_val.find(target_val) > -1 | |
2005 | 413 | def not_contains(self, orig_val, target_val): | 408 | |
2006 | 414 | if len(target_val) == 0 or orig_val is None: | 409 | def not_contains(self, orig_val, target_val): |
2007 | 415 | return True | 410 | if len(target_val) == 0 or orig_val is None: |
2008 | 416 | return orig_val.find(target_val) == -1 | 411 | return True |
2009 | 417 | 412 | return orig_val.find(target_val) == -1 | |
2010 | 418 | def starts_with(self, orig_val, target_val): | 413 | |
2011 | 419 | if len(target_val) == 0 or orig_val is None: | 414 | def starts_with(self, orig_val, target_val): |
2012 | 420 | return True | 415 | if len(target_val) == 0 or orig_val is None: |
2013 | 421 | return orig_val.startswith(target_val) | 416 | return True |
2014 | 422 | 417 | return orig_val.startswith(target_val) | |
2015 | 423 | def ends_with(self, orig_val, target_val): | 418 | |
2016 | 424 | if len(target_val) == 0 or orig_val is None: | 419 | def ends_with(self, orig_val, target_val): |
2017 | 425 | return True | 420 | if len(target_val) == 0 or orig_val is None: |
2018 | 426 | return orig_val.endswith(target_val) | 421 | return True |
2019 | 422 | return orig_val.endswith(target_val) | ||
2020 | 427 | 423 | ||
2021 | 428 | 424 | ||
2022 | 429 | class TagsFilterBox( BlankFilterBox ): | 425 | class TagsFilterBox( BlankFilterBox ): |
2024 | 430 | """TagsFilterBox: A default tag filter class for use in a FilterRow. | 426 | """TagsFilterBox: A default tag filter class for use in a FilterRow. |
2025 | 431 | 427 | ||
2026 | 432 | Lets the user specify if the row should be displayed based on | 428 | Lets the user specify if the row should be displayed based on |
2027 | 433 | containing a one tag or all tags. Assumes tags are seperated by | 429 | containing a one tag or all tags. Assumes tags are seperated by |
2028 | @@ -435,382 +431,382 @@ | |||
2029 | 435 | 431 | ||
2030 | 436 | """ | 432 | """ |
2031 | 437 | 433 | ||
2094 | 438 | def __init__(self): | 434 | def __init__(self): |
2095 | 439 | BlankFilterBox.__init__(self) | 435 | BlankFilterBox.__init__(self) |
2096 | 440 | self.append(_("has any of these tags"), self._filter_any) | 436 | self.append(_("has any of these tags"), self._filter_any) |
2097 | 441 | self.append(_("has all of these tags"), self._filter_all) | 437 | self.append(_("has all of these tags"), self._filter_all) |
2098 | 442 | self.append(_("does not have one of these tags"), self._filter_not) | 438 | self.append(_("does not have one of these tags"), self._filter_not) |
2099 | 443 | self.append(_("does not have any of these tags"), self._filter_not_all) | 439 | self.append(_("does not have any of these tags"), self._filter_not_all) |
2100 | 444 | 440 | ||
2101 | 445 | def _filter_any(self, orig_val, target_val): | 441 | def _filter_any(self, orig_val, target_val): |
2102 | 446 | """ | 442 | """ |
2103 | 447 | _filter_any: filter function that hides rows | 443 | _filter_any: filter function that hides rows |
2104 | 448 | if none of the tags supplied in "bugs_tags_s" are found | 444 | if none of the tags supplied in "bugs_tags_s" are found |
2105 | 449 | in the gtk.TextEntry. | 445 | in the Gtk.TextEntry. |
2106 | 450 | 446 | ||
2107 | 451 | Do not call directly | 447 | Do not call directly |
2108 | 452 | 448 | ||
2109 | 453 | """ | 449 | """ |
2110 | 454 | 450 | ||
2111 | 455 | if len(target_val) == 0: | 451 | if len(target_val) == 0: |
2112 | 456 | return True | 452 | return True |
2113 | 457 | 453 | ||
2114 | 458 | tags_on_bug = orig_val.split() | 454 | tags_on_bug = orig_val.split() |
2115 | 459 | tags_in_filter = target_val.split() | 455 | tags_in_filter = target_val.split() |
2116 | 460 | 456 | ||
2117 | 461 | for tag in tags_in_filter: | 457 | for tag in tags_in_filter: |
2118 | 462 | if tag in tags_on_bug: | 458 | if tag in tags_on_bug: |
2119 | 463 | return True | 459 | return True |
2120 | 464 | return False | 460 | return False |
2121 | 465 | 461 | ||
2122 | 466 | def _filter_all(self, orig_val, target_val): | 462 | def _filter_all(self, orig_val, target_val): |
2123 | 467 | """ | 463 | """ |
2124 | 468 | _filter_any: filter function that hides rows | 464 | _filter_any: filter function that hides rows |
2125 | 469 | if not all of the tags supplied in "bugs_tags_s" are found | 465 | if not all of the tags supplied in "bugs_tags_s" are found |
2126 | 470 | in the gtk.TextEntry. | 466 | in the Gtk.TextEntry. |
2127 | 471 | 467 | ||
2128 | 472 | Do not call directly | 468 | Do not call directly |
2129 | 473 | 469 | ||
2130 | 474 | """ | 470 | """ |
2131 | 475 | if len(target_val) == 0: | 471 | if len(target_val) == 0: |
2132 | 476 | return True | 472 | return True |
2133 | 477 | 473 | ||
2134 | 478 | tags_on_bug = orig_val.split() | 474 | tags_on_bug = orig_val.split() |
2135 | 479 | tags_in_filter = self.entry.get_text().split() | 475 | tags_in_filter = self.entry.get_text().split() |
2136 | 480 | 476 | ||
2137 | 481 | for tag in tags_in_filter: | 477 | for tag in tags_in_filter: |
2138 | 482 | if tag not in tags_on_bug: | 478 | if tag not in tags_on_bug: |
2139 | 483 | return False | 479 | return False |
2140 | 484 | return True | 480 | return True |
2141 | 485 | 481 | ||
2142 | 486 | def _filter_not(self, orig_val, target_val): | 482 | def _filter_not(self, orig_val, target_val): |
2143 | 487 | """ | 483 | """ |
2144 | 488 | _filter_not: filter function that hides rows | 484 | _filter_not: filter function that hides rows |
2145 | 489 | if one of the tags supplied in "bugs_tags_s" are found | 485 | if one of the tags supplied in "bugs_tags_s" are found |
2146 | 490 | in the gtk.TextEntry. | 486 | in the Gtk.TextEntry. |
2147 | 491 | 487 | ||
2148 | 492 | Do not call directly | 488 | Do not call directly |
2149 | 493 | 489 | ||
2150 | 494 | """ | 490 | """ |
2151 | 495 | if len(target_val) == 0: | 491 | if len(target_val) == 0: |
2152 | 496 | return True | 492 | return True |
2153 | 497 | 493 | ||
2154 | 498 | tags_on_bug = orig_val.split() | 494 | tags_on_bug = orig_val.split() |
2155 | 499 | tags_in_filter = target_val.split() | 495 | tags_in_filter = target_val.split() |
2156 | 500 | 496 | ||
2263 | 501 | for tag in tags_in_filter: | 497 | for tag in tags_in_filter: |
2264 | 502 | if tag not in tags_on_bug: | 498 | if tag not in tags_on_bug: |
2265 | 503 | return True | 499 | return True |
2266 | 504 | return False | 500 | return False |
2267 | 505 | 501 | ||
2268 | 506 | def _filter_not_all(self, orig_val, target_val): | 502 | def _filter_not_all(self, orig_val, target_val): |
2269 | 507 | """ | 503 | """ |
2270 | 508 | _filter_not all: filter function that hides rows | 504 | _filter_not all: filter function that hides rows |
2271 | 509 | if all of the tags supplied in "bugs_tags_s" are found | 505 | if all of the tags supplied in "bugs_tags_s" are found |
2272 | 510 | in the gtk.TextEntry. | 506 | in the Gtk.TextEntry. |
2273 | 511 | 507 | ||
2274 | 512 | Do not call directly | 508 | Do not call directly |
2275 | 513 | 509 | ||
2276 | 514 | """ | 510 | """ |
2277 | 515 | if len(self.entry.get_text()) == 0: | 511 | if len(self.entry.get_text()) == 0: |
2278 | 516 | return True | 512 | return True |
2279 | 517 | 513 | ||
2280 | 518 | tags_on_bug = orig_val.split() | 514 | tags_on_bug = orig_val.split() |
2281 | 519 | tags_in_filter = target_val.split() | 515 | tags_in_filter = target_val.split() |
2282 | 520 | 516 | ||
2283 | 521 | for tag in tags_in_filter: | 517 | for tag in tags_in_filter: |
2284 | 522 | if tag in tags_on_bug: | 518 | if tag in tags_on_bug: |
2285 | 523 | return False | 519 | return False |
2286 | 524 | return True | 520 | return True |
2287 | 525 | 521 | ||
2288 | 526 | class IntegerFilterBox( gtk.HBox ): | 522 | class IntegerFilterBox( Gtk.HBox ): |
2289 | 527 | """ | 523 | """ |
2290 | 528 | 524 | ||
2291 | 529 | """ | 525 | """ |
2292 | 530 | __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 526 | __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
2293 | 531 | (gobject.TYPE_PYOBJECT,)), | 527 | (GObject.TYPE_PYOBJECT,)), |
2294 | 532 | } | 528 | } |
2295 | 533 | 529 | ||
2296 | 534 | def __init__(self): | 530 | def __init__(self): |
2297 | 535 | gtk.HBox.__init__(self, False, 10) | 531 | Gtk.HBox.__init__(self, False, 10) |
2298 | 536 | self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT) | 532 | self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT) |
2299 | 537 | self.combo = gtk.ComboBox(self.__combo_store) | 533 | self.combo = Gtk.ComboBox.new_with_model(self.__combo_store) |
2300 | 538 | cell = gtk.CellRendererText() | 534 | cell = Gtk.CellRendererText() |
2301 | 539 | self.combo.pack_start(cell, True) | 535 | self.combo.pack_start(cell, True) |
2302 | 540 | self.combo.add_attribute(cell, 'text', 0) | 536 | self.combo.add_attribute(cell, 'text', 0) |
2303 | 541 | self.combo.show() | 537 | self.combo.show() |
2304 | 542 | self.combo.connect("changed",self.__changed) | 538 | self.combo.connect("changed",self.__changed) |
2305 | 543 | adj = gtk.Adjustment(0,-1000000000,1000000000,1) | 539 | adj = Gtk.Adjustment(0,-1000000000,1000000000,1) |
2306 | 544 | 540 | ||
2307 | 545 | self.spinner = gtk.SpinButton(adj,1,0) | 541 | self.spinner = Gtk.SpinButton.new(adj,1,0) |
2308 | 546 | self.spinner.set_activates_default(True) | 542 | self.spinner.set_activates_default(True) |
2309 | 547 | self.spinner.show() | 543 | self.spinner.show() |
2310 | 548 | self.spinner.set_numeric(True) | 544 | self.spinner.set_numeric(True) |
2311 | 549 | 545 | ||
2312 | 550 | self.spinner.connect("value-changed",self.__changed) | 546 | self.spinner.connect("value-changed",self.__changed) |
2313 | 551 | self.pack_start(self.combo, False, False) | 547 | self.pack_start(self.combo, False, False, 0) |
2314 | 552 | self.pack_start(self.spinner) | 548 | self.pack_start(self.spinner, True, True, 0) |
2315 | 553 | 549 | ||
2316 | 554 | self.__combo_store.append(["=",self._equals]) | 550 | self.__combo_store.append(["=",self._equals]) |
2317 | 555 | self.__combo_store.append(["<",self._less_than]) | 551 | self.__combo_store.append(["<",self._less_than]) |
2318 | 556 | self.__combo_store.append([">",self._greater_than]) | 552 | self.__combo_store.append([">",self._greater_than]) |
2319 | 557 | self.__combo_store.append(["<=",self._less_than_equals]) | 553 | self.__combo_store.append(["<=",self._less_than_equals]) |
2320 | 558 | self.__combo_store.append([">=",self._greater_than_equals]) | 554 | self.__combo_store.append([">=",self._greater_than_equals]) |
2321 | 559 | 555 | ||
2322 | 560 | def __changed(self, widget, data=None): | 556 | def __changed(self, widget, data=None): |
2323 | 561 | self.emit("changed",data) | 557 | self.emit("changed",data) |
2324 | 562 | 558 | ||
2325 | 563 | def filter(self, orig_val): | 559 | def filter(self, orig_val): |
2326 | 564 | if self.combo.get_active() == -1: | 560 | if self.combo.get_active() == -1: |
2327 | 565 | return True | 561 | return True |
2328 | 566 | 562 | ||
2329 | 567 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) | 563 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) |
2330 | 568 | filt_func = self.combo.get_model().get_value(filt_iter,1) | 564 | filt_func = self.combo.get_model().get_value(filt_iter,1) |
2331 | 569 | 565 | ||
2332 | 570 | try: | 566 | try: |
2333 | 571 | target_val = int(self.spinner.get_value_as_int()) | 567 | target_val = int(self.spinner.get_value_as_int()) |
2334 | 572 | 568 | except Exception, inst: | |
2335 | 573 | 569 | print inst | |
2336 | 574 | except Exception, inst: | 570 | return False |
2337 | 575 | print inst | 571 | |
2338 | 576 | return False | 572 | return filt_func(orig_val, target_val) |
2339 | 577 | 573 | ||
2340 | 578 | return filt_func(orig_val, target_val) | 574 | def _equals(self, orig_val, target_val): |
2341 | 579 | 575 | if orig_val == "": | |
2342 | 580 | def _equals(self, orig_val, target_val): | 576 | return False |
2343 | 581 | if orig_val == "": | 577 | return int(orig_val) == target_val |
2344 | 582 | return False | 578 | |
2345 | 583 | return int(orig_val) == target_val | 579 | def _less_than(self, orig_val, target_val): |
2346 | 584 | 580 | if orig_val == "": | |
2347 | 585 | def _less_than(self, orig_val, target_val): | 581 | return False |
2348 | 586 | if orig_val == "": | 582 | return int(orig_val) < target_val |
2349 | 587 | return False | 583 | |
2350 | 588 | return int(orig_val) < target_val | 584 | def _greater_than(self, orig_val, target_val): |
2351 | 589 | 585 | if orig_val == "": | |
2352 | 590 | def _greater_than(self, orig_val, target_val): | 586 | return False |
2353 | 591 | if orig_val == "": | 587 | return int(orig_val) > target_val |
2354 | 592 | return False | 588 | |
2355 | 593 | return int(orig_val) > target_val | 589 | def _less_than_equals(self, orig_val, target_val): |
2356 | 594 | 590 | if orig_val == "": | |
2357 | 595 | def _less_than_equals(self, orig_val, target_val): | 591 | return False |
2358 | 596 | if orig_val == "": | 592 | return int(orig_val) <= target_val |
2359 | 597 | return False | 593 | |
2360 | 598 | return int(orig_val) <= target_val | 594 | def _greater_than_equals(self, orig_val, target_val): |
2361 | 599 | 595 | if orig_val == "": | |
2362 | 600 | def _greater_than_equals(self, orig_val, target_val): | 596 | return False |
2363 | 601 | if orig_val == "": | 597 | return int(orig_val) >= target_val |
2364 | 602 | return False | 598 | |
2365 | 603 | return int(orig_val) >= target_val | 599 | class DateFilterBox( Gtk.HBox ): |
2366 | 604 | 600 | """DateFilterCombo: A default date filter class for use in a FilterRow. | |
2261 | 605 | class DateFilterBox( gtk.HBox ): | ||
2262 | 606 | """DateFilterCombo: A default date filter class for use in a FilterRow. | ||
2367 | 607 | 601 | ||
2368 | 608 | Lets the user specify if the row should be displayed based on | 602 | Lets the user specify if the row should be displayed based on |
2369 | 609 | the settings in a date widget. | 603 | the settings in a date widget. |
2370 | 610 | 604 | ||
2375 | 611 | """ | 605 | """ |
2376 | 612 | __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 606 | __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
2377 | 613 | (gobject.TYPE_PYOBJECT,)), | 607 | (GObject.TYPE_PYOBJECT,)), |
2378 | 614 | } | 608 | } |
2379 | 615 | 609 | ||
2382 | 616 | def __init__(self): | 610 | def __init__(self): |
2383 | 617 | """create a CheckFilterCombo | 611 | """create a CheckFilterCombo |
2384 | 618 | 612 | ||
2465 | 619 | """ | 613 | """ |
2466 | 620 | gtk.HBox.__init__(self, False, 10) | 614 | Gtk.HBox.__init__(self, False, 10) |
2467 | 621 | 615 | ||
2468 | 622 | self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT) | 616 | self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT) |
2469 | 623 | self.combo = gtk.ComboBox(self.__combo_store) | 617 | self.combo = Gtk.ComboBox.new_with_model(self.__combo_store) |
2470 | 624 | cell = gtk.CellRendererText() | 618 | cell = Gtk.CellRendererText() |
2471 | 625 | self.combo.pack_start(cell, False) | 619 | self.combo.pack_start(cell, False) |
2472 | 626 | self.combo.add_attribute(cell, 'text', 0) | 620 | self.combo.add_attribute(cell, 'text', 0) |
2473 | 627 | self.combo.show() | 621 | self.combo.show() |
2474 | 628 | self.combo.connect("changed",self.__changed) | 622 | self.combo.connect("changed",self.__changed) |
2475 | 629 | 623 | ||
2476 | 630 | self.__combo_store.append([ _("before"),self.before ]) | 624 | self.__combo_store.append([ _("before"),self.before ]) |
2477 | 631 | self.__combo_store.append([ _("on or before"),self.on_before ]) | 625 | self.__combo_store.append([ _("on or before"),self.on_before ]) |
2478 | 632 | self.__combo_store.append([ _("on"), self.on_date ]) | 626 | self.__combo_store.append([ _("on"), self.on_date ]) |
2479 | 633 | self.__combo_store.append([ _("on or after"),self.on_after ]) | 627 | self.__combo_store.append([ _("on or after"),self.on_after ]) |
2480 | 634 | self.__combo_store.append([ _("after"),self.after ]) | 628 | self.__combo_store.append([ _("after"),self.after ]) |
2481 | 635 | 629 | ||
2482 | 636 | self.calendar = gtk.Calendar() | 630 | self.calendar = Gtk.Calendar() |
2483 | 637 | self.calendar.show() | 631 | self.calendar.show() |
2484 | 638 | self.calendar.connect("day-selected", self.__changed) | 632 | self.calendar.connect("day-selected", self.__changed) |
2485 | 639 | vb = gtk.VBox(False, 5) | 633 | vb = Gtk.VBox(False, 5) |
2486 | 640 | vb.show() | 634 | vb.show() |
2487 | 641 | vb.pack_start(self.combo, True, False) | 635 | vb.pack_start(self.combo, True, False, 0) |
2488 | 642 | self.pack_start(vb, False, False) | 636 | self.pack_start(vb, False, False, 0) |
2489 | 643 | self.pack_start(self.calendar, False, False) | 637 | self.pack_start(self.calendar, False, False, 0) |
2490 | 644 | 638 | ||
2491 | 645 | def before(self, orig_val): | 639 | def before(self, orig_val): |
2492 | 646 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) | 640 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) |
2493 | 647 | if stored_date is None: | 641 | if stored_date is None: |
2494 | 648 | return False | 642 | return False |
2495 | 649 | return stored_date < target_date | 643 | return stored_date < target_date |
2496 | 650 | 644 | ||
2497 | 651 | def on_before(self, orig_val): | 645 | def on_before(self, orig_val): |
2498 | 652 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) | 646 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) |
2499 | 653 | if stored_date is None: | 647 | if stored_date is None: |
2500 | 654 | return False | 648 | return False |
2501 | 655 | return stored_date <= target_date | 649 | return stored_date <= target_date |
2502 | 656 | 650 | ||
2503 | 657 | def on_date(self, orig_val): | 651 | def on_date(self, orig_val): |
2504 | 658 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) | 652 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) |
2505 | 659 | if stored_date is None: | 653 | if stored_date is None: |
2506 | 660 | return False | 654 | return False |
2507 | 661 | return stored_date == target_date | 655 | return stored_date == target_date |
2508 | 662 | 656 | ||
2509 | 663 | def on_after(self, orig_val): | 657 | def on_after(self, orig_val): |
2510 | 664 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) | 658 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) |
2511 | 665 | if stored_date is None: | 659 | if stored_date is None: |
2512 | 666 | return False | 660 | return False |
2513 | 667 | return stored_date >= target_date | 661 | return stored_date >= target_date |
2514 | 668 | 662 | ||
2515 | 669 | def after(self, orig_val): | 663 | def after(self, orig_val): |
2516 | 670 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) | 664 | stored_date, target_date = self.__get_dates(orig_val, self.calendar.get_date()) |
2517 | 671 | if stored_date is None: | 665 | if stored_date is None: |
2518 | 672 | return False | 666 | return False |
2519 | 673 | return stored_date > target_date | 667 | return stored_date > target_date |
2520 | 674 | 668 | ||
2521 | 675 | def __get_dates(self, orig_val, target_date): | 669 | def __get_dates(self, orig_val, target_date): |
2522 | 676 | target_date = self.calendar.get_date() | 670 | target_date = self.calendar.get_date() |
2523 | 677 | target_date = datetime.date(int(target_date[0]),int(target_date[1] + 1),int(target_date[2])) | 671 | target_date = datetime.date(int(target_date[0]),int(target_date[1] + 1),int(target_date[2])) |
2524 | 678 | if orig_val is not None and len(orig_val) > 0: | 672 | if orig_val is not None and len(orig_val) > 0: |
2525 | 679 | p = orig_val.split("-") | 673 | p = orig_val.split("-") |
2526 | 680 | stored_date = datetime.date(int(p[0]),int(p[1]),int(p[2])) | 674 | stored_date = datetime.date(int(p[0]),int(p[1]),int(p[2])) |
2527 | 681 | else: | 675 | else: |
2528 | 682 | stored_date = None | 676 | stored_date = None |
2529 | 683 | return (stored_date, target_date) | 677 | return (stored_date, target_date) |
2530 | 684 | 678 | ||
2531 | 685 | def filter(self, orig_val): | 679 | def filter(self, orig_val): |
2532 | 686 | if self.combo.get_active() == -1: | 680 | if self.combo.get_active() == -1: |
2533 | 687 | return True | 681 | return True |
2534 | 688 | 682 | ||
2535 | 689 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) | 683 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) |
2536 | 690 | filt_func = self.combo.get_model().get_value(filt_iter,1) | 684 | filt_func = self.combo.get_model().get_value(filt_iter,1) |
2537 | 691 | return filt_func(orig_val) | 685 | return filt_func(orig_val) |
2538 | 692 | 686 | ||
2539 | 693 | def __changed(self, widget, data=None): | 687 | def __changed(self, widget, data=None): |
2540 | 694 | self.emit("changed",data) | 688 | self.emit("changed",data) |
2541 | 695 | 689 | ||
2542 | 696 | 690 | class CheckFilterBox( Gtk.HBox ): | |
2543 | 697 | class CheckFilterBox( gtk.HBox ): | 691 | """CheckFilterCombo: A default checkbox filter class for use in a FilterRow. |
2464 | 698 | """CheckFilterCombo: A default checkbox filter class for use in a FilterRow. | ||
2544 | 699 | 692 | ||
2545 | 700 | Lets the user specify if the row should be displayed based on | 693 | Lets the user specify if the row should be displayed based on |
2546 | 701 | whether a Checkbox is active, inactive, or not set. | 694 | whether a Checkbox is active, inactive, or not set. |
2547 | 702 | 695 | ||
2552 | 703 | """ | 696 | """ |
2553 | 704 | __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 697 | __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, |
2554 | 705 | (gobject.TYPE_PYOBJECT,)), | 698 | (GObject.TYPE_PYOBJECT,)), |
2555 | 706 | } | 699 | } |
2556 | 707 | 700 | ||
2559 | 708 | def __init__(self): | 701 | def __init__(self): |
2560 | 709 | """create a CheckFilterCombo | 702 | """create a CheckFilterCombo |
2561 | 710 | 703 | ||
2598 | 711 | """ | 704 | """ |
2599 | 712 | gtk.HBox.__init__(self, False, 10) | 705 | Gtk.HBox.__init__(self, False, 10) |
2600 | 713 | 706 | ||
2601 | 714 | self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT) | 707 | self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT) |
2602 | 715 | self.combo = gtk.ComboBox(self.__combo_store) | 708 | self.combo = Gtk.ComboBox.new_with_model(self.__combo_store) |
2603 | 716 | cell = gtk.CellRendererText() | 709 | cell = Gtk.CellRendererText() |
2604 | 717 | self.combo.pack_start(cell, True) | 710 | self.combo.pack_start(cell, True) |
2605 | 718 | self.combo.add_attribute(cell, 'text', 0) | 711 | self.combo.add_attribute(cell, 'text', 0) |
2606 | 719 | self.combo.show() | 712 | self.combo.show() |
2607 | 720 | self.combo.connect("changed",self.__changed) | 713 | self.combo.connect("changed",self.__changed) |
2608 | 721 | 714 | ||
2609 | 722 | self.__combo_store.append([ _("checked"),self.filter_checked ]) | 715 | self.__combo_store.append([ _("checked"),self.filter_checked ]) |
2610 | 723 | self.__combo_store.append([ _("not Checked"),self.filter_not_checked ]) | 716 | self.__combo_store.append([ _("not Checked"),self.filter_not_checked ]) |
2611 | 724 | self.__combo_store.append([ _("unset"), self.filter_unset ]) | 717 | self.__combo_store.append([ _("unset"), self.filter_unset ]) |
2612 | 725 | 718 | ||
2613 | 726 | self.pack_start(self.combo, False, False) | 719 | self.pack_start(self.combo, False, False, 0) |
2614 | 727 | 720 | ||
2615 | 728 | def filter(self, orig_val): | 721 | def filter(self, orig_val): |
2616 | 729 | if self.combo.get_active() == -1: | 722 | if self.combo.get_active() == -1: |
2617 | 730 | return True | 723 | return True |
2618 | 731 | 724 | ||
2619 | 732 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) | 725 | filt_iter = self.combo.get_model().get_iter(self.combo.get_active()) |
2620 | 733 | filt_func = self.combo.get_model().get_value(filt_iter,1) | 726 | filt_func = self.combo.get_model().get_value(filt_iter,1) |
2621 | 734 | return filt_func(orig_val) | 727 | return filt_func(orig_val) |
2622 | 735 | 728 | ||
2623 | 736 | def filter_checked(self, orig_val): | 729 | def filter_checked(self, orig_val): |
2624 | 737 | return orig_val == 1 | 730 | return orig_val == 1 |
2625 | 738 | 731 | ||
2626 | 739 | def filter_not_checked(self, orig_val): | 732 | def filter_not_checked(self, orig_val): |
2627 | 740 | return orig_val == 0 | 733 | return orig_val == 0 |
2628 | 741 | 734 | ||
2629 | 742 | def filter_unset(self, orig_val): | 735 | def filter_unset(self, orig_val): |
2630 | 743 | return orig_val == -1 | 736 | return orig_val == -1 |
2631 | 744 | 737 | ||
2632 | 745 | def __changed(self, widget, data=None): | 738 | def __changed(self, widget, data=None): |
2633 | 746 | self.emit("changed",data) | 739 | self.emit("changed",data) |
2634 | 747 | 740 | ||
2635 | 748 | 741 | ||
2636 | 749 | class NumericFilterBox( BlankFilterBox ): | 742 | class NumericFilterBox( BlankFilterBox ): |
2638 | 750 | """NumericFilterCombo: A default number filter class for use in a FilterRow. | 743 | """NumericFilterCombo: A default number filter class for use in a FilterRow. |
2639 | 751 | 744 | ||
2640 | 752 | Lets the user specify if the row should be displayed based on numeric | 745 | Lets the user specify if the row should be displayed based on numeric |
2641 | 753 | relationships to a number specified by the user. | 746 | relationships to a number specified by the user. |
2642 | 754 | 747 | ||
2686 | 755 | """ | 748 | """ |
2687 | 756 | 749 | ||
2688 | 757 | 750 | ||
2689 | 758 | def __init__(self): | 751 | def __init__(self): |
2690 | 759 | """create a NumericFilterCombo | 752 | """create a NumericFilterCombo |
2691 | 760 | 753 | ||
2692 | 761 | """ | 754 | """ |
2693 | 762 | BlankFilterBox.__init__( self ) | 755 | BlankFilterBox.__init__( self ) |
2694 | 763 | self.append("=",self._equals ) | 756 | self.append("=",self._equals ) |
2695 | 764 | self.append("<",self._less_than ) | 757 | self.append("<",self._less_than ) |
2696 | 765 | self.append(">",self._greater_than ) | 758 | self.append(">",self._greater_than ) |
2697 | 766 | self.append("<=",self._less_than_equals) | 759 | self.append("<=",self._less_than_equals) |
2698 | 767 | self.append(">=",self._greater_than_equals ) | 760 | self.append(">=",self._greater_than_equals ) |
2699 | 768 | 761 | ||
2700 | 769 | def _equals(self, orig_val): | 762 | def _equals(self, orig_val): |
2701 | 770 | try: | 763 | try: |
2702 | 771 | return float(orig_val) == float(self.entry.get_text()) | 764 | return float(orig_val) == float(self.entry.get_text()) |
2703 | 772 | except: | 765 | except: |
2704 | 773 | return True | 766 | return True |
2705 | 774 | 767 | ||
2706 | 775 | def _less_than(self, orig_val): | 768 | def _less_than(self, orig_val): |
2707 | 776 | try: | 769 | try: |
2708 | 777 | return float(orig_val) < float(self.entry.get_text()) | 770 | return float(orig_val) < float(self.entry.get_text()) |
2709 | 778 | except: | 771 | except: |
2710 | 779 | return True | 772 | return True |
2711 | 780 | 773 | ||
2712 | 781 | def _greater_than(self, orig_val): | 774 | def _greater_than(self, orig_val): |
2713 | 782 | try: | 775 | try: |
2714 | 783 | return float(orig_val) > float(self.entry.get_text()) | 776 | return float(orig_val) > float(self.entry.get_text()) |
2715 | 784 | except: | 777 | except: |
2716 | 785 | return True | 778 | return True |
2717 | 786 | 779 | ||
2718 | 787 | def _less_than_equals(self, orig_val): | 780 | def _less_than_equals(self, orig_val): |
2719 | 788 | try: | 781 | try: |
2720 | 789 | return float(orig_val) <= float(self.entry.get_text()) | 782 | return float(orig_val) <= float(self.entry.get_text()) |
2721 | 790 | except: | 783 | except: |
2722 | 791 | return True | 784 | return True |
2723 | 792 | 785 | ||
2724 | 793 | def _greater_than_equals(self, orig_val): | 786 | def _greater_than_equals(self, orig_val): |
2725 | 794 | try: | 787 | try: |
2726 | 795 | return float(orig_val) >= float(self.entry.get_text()) | 788 | return float(orig_val) >= float(self.entry.get_text()) |
2727 | 796 | except: | 789 | except: |
2728 | 797 | return True | 790 | return True |
2729 | 791 | |||
2730 | 792 | |||
2731 | 793 | # Test case begins here. | ||
2732 | 798 | 794 | ||
2733 | 799 | def __delete_test(button, grid): | 795 | def __delete_test(button, grid): |
2734 | 800 | grid.remove_selected_rows(delete=True) | 796 | grid.remove_selected_rows(delete=True) |
2735 | 801 | 797 | ||
2736 | 802 | if __name__ == "__main__": | 798 | if __name__ == "__main__": |
2739 | 803 | """creates a test CouchGrid if called directly""" | 799 | """creates a test DictionaryGrid and GridFilter if called directly""" |
2740 | 804 | from couch_grid import CouchGrid | 800 | from dictionary_grid import DictionaryGrid |
2741 | 805 | 801 | ||
2742 | 806 | #create and show a test window | 802 | #create and show a test window |
2746 | 807 | win = gtk.Window(gtk.WINDOW_TOPLEVEL) | 803 | win = Gtk.Window.new(Gtk.WindowType.TOPLEVEL) |
2747 | 808 | win.set_title("DictionaryGrid Test Window") | 804 | win.set_title("CouchGrid Test Window") |
2748 | 809 | win.connect("destroy",gtk.main_quit) | 805 | win.connect("destroy",Gtk.main_quit) |
2749 | 810 | win.show() | 806 | win.show() |
2750 | 811 | 807 | ||
2751 | 812 | #create a top level container | 808 | #create a top level container |
2753 | 813 | vbox = gtk.VBox(False, 10) | 809 | vbox = Gtk.VBox(False, 10) |
2754 | 814 | vbox.show() | 810 | vbox.show() |
2755 | 815 | win.add(vbox) | 811 | win.add(vbox) |
2756 | 816 | 812 | ||
2757 | @@ -821,9 +817,7 @@ | |||
2758 | 821 | {"ID": 3, "key?": False, "tags": "ddd eee fff", "string":"dddddddd","date":"2010-10-01"}, | 817 | {"ID": 3, "key?": False, "tags": "ddd eee fff", "string":"dddddddd","date":"2010-10-01"}, |
2759 | 822 | {"ID": 4, "key?": True, "tags": "eee fff ggg", "string":"eeeeeeee","date":"2010-11-01"}] | 818 | {"ID": 4, "key?": True, "tags": "eee fff ggg", "string":"eeeeeeee","date":"2010-11-01"}] |
2760 | 823 | 819 | ||
2764 | 824 | database_name = "couch_widget_test" | 820 | grid = DictionaryGrid(dictionaries=dicts, editable=True) |
2762 | 825 | record_type = "couch_grid_filter_test" | ||
2763 | 826 | grid = CouchGrid(database_name, record_type=record_type, dictionaries=dicts, editable=True) | ||
2765 | 827 | grid.columns["tags"].set_title("modified title") | 821 | grid.columns["tags"].set_title("modified title") |
2766 | 828 | grid.show() | 822 | grid.show() |
2767 | 829 | 823 | ||
2768 | @@ -831,15 +825,15 @@ | |||
2769 | 831 | hints = {} | 825 | hints = {} |
2770 | 832 | filt = GridFilter(grid,hints) | 826 | filt = GridFilter(grid,hints) |
2771 | 833 | filt.show() | 827 | filt.show() |
2774 | 834 | vbox.pack_start(filt, False, False) | 828 | vbox.pack_start(filt, False, False, 0) |
2775 | 835 | vbox.pack_end(grid, True, True) | 829 | vbox.pack_end(grid, True, True, 0) |
2776 | 836 | 830 | ||
2778 | 837 | delete_button = gtk.Button("Delete Selected") | 831 | delete_button = Gtk.Button("Delete Selected") |
2779 | 838 | delete_button.connect("clicked",__delete_test,grid) | 832 | delete_button.connect("clicked",__delete_test,grid) |
2780 | 839 | delete_button.show() | 833 | delete_button.show() |
2781 | 840 | 834 | ||
2782 | 841 | 835 | ||
2785 | 842 | vbox.pack_start(delete_button,False, False) | 836 | vbox.pack_start(delete_button,False, False, 0) |
2786 | 843 | gtk.main() | 837 | Gtk.main() |
2787 | 844 | 838 | ||
2788 | 845 | 839 | ||
2789 | 846 | 840 | ||
2790 | === modified file 'quickly/widgets/media_player_box.py' | |||
2791 | --- quickly/widgets/media_player_box.py 2011-01-17 03:40:02 +0000 | |||
2792 | +++ quickly/widgets/media_player_box.py 2012-03-06 08:52:21 +0000 | |||
2793 | @@ -50,14 +50,14 @@ | |||
2794 | 50 | #You can add Widgets to the MediaPlayerBox simply by packing them in | 50 | #You can add Widgets to the MediaPlayerBox simply by packing them in |
2795 | 51 | player.pack_start(my_widget, False, False) | 51 | player.pack_start(my_widget, False, False) |
2796 | 52 | 52 | ||
2799 | 53 | #You can get a reference to the controls, which are a gtk.Toolbar | 53 | #You can get a reference to the controls, which are a Gtk.Toolbar |
2800 | 54 | mybutton = gtk.ToolButton() | 54 | mybutton = Gtk.ToolButton() |
2801 | 55 | player.controls.insert(mybutton, 0) | 55 | player.controls.insert(mybutton, 0) |
2802 | 56 | 56 | ||
2803 | 57 | #You can access the playbutton, slider, or time label directly as well | 57 | #You can access the playbutton, slider, or time label directly as well |
2807 | 58 | player.play_button.hide()#a gtk.ToggleToolButton | 58 | player.play_button.hide()#a Gtk.ToggleToolButton |
2808 | 59 | player.slider.hide()#a gtk.HScale | 59 | player.slider.hide()#a Gtk.HScale |
2809 | 60 | player.time_label.hide()#a gtk.Label | 60 | player.time_label.hide()#a Gtk.Label |
2810 | 61 | 61 | ||
2811 | 62 | #If you want access to all the gstreamer knobs and dials, you can just | 62 | #If you want access to all the gstreamer knobs and dials, you can just |
2812 | 63 | #get a reference to the playbin (see gstreamer documentation for details. | 63 | #get a reference to the playbin (see gstreamer documentation for details. |
2813 | @@ -67,13 +67,13 @@ | |||
2814 | 67 | player.playbin.emit(signal_name) | 67 | player.playbin.emit(signal_name) |
2815 | 68 | 68 | ||
2816 | 69 | Extending | 69 | Extending |
2819 | 70 | A WebCamBox is gtk.VBox | 70 | A WebCamBox is Gtk.VBox |
2820 | 71 | A WebCamBox is a gtk.VBox that contains a gtk.DrawingArea for displaying | 71 | A WebCamBox is a Gtk.VBox that contains a Gtk.DrawingArea for displaying |
2821 | 72 | video output, and a thin wrapper around a playbin, which is a gstreamer | 72 | video output, and a thin wrapper around a playbin, which is a gstreamer |
2822 | 73 | pipleine sublcass that provides all the media playing functionality. | 73 | pipleine sublcass that provides all the media playing functionality. |
2823 | 74 | 74 | ||
2824 | 75 | To add GUI elements simple, create them and pack them into MediaPlayerBox, since | 75 | To add GUI elements simple, create them and pack them into MediaPlayerBox, since |
2826 | 76 | it's just a gtk.VBox | 76 | it's just a Gtk.VBox |
2827 | 77 | 77 | ||
2828 | 78 | Similarly, to add to or change the media player functionality, modify properties on | 78 | Similarly, to add to or change the media player functionality, modify properties on |
2829 | 79 | the playbin. You may also want to overide _on_message and/or _on_sync_message | 79 | the playbin. You may also want to overide _on_message and/or _on_sync_message |
2830 | @@ -81,18 +81,21 @@ | |||
2831 | 81 | 81 | ||
2832 | 82 | """ | 82 | """ |
2833 | 83 | 83 | ||
2834 | 84 | |||
2835 | 84 | import sys | 85 | import sys |
2836 | 85 | import os | 86 | import os |
2838 | 86 | import gtk | 87 | from gi.repository import Gtk |
2839 | 88 | from gi.repository import Gdk | ||
2840 | 89 | from gi.repository import GdkX11 | ||
2841 | 90 | from gi.repository import GObject | ||
2842 | 87 | import gst | 91 | import gst |
2843 | 88 | import datetime | 92 | import datetime |
2844 | 89 | import gobject | ||
2845 | 90 | 93 | ||
2846 | 91 | import gettext | 94 | import gettext |
2847 | 92 | from gettext import gettext as _ | 95 | from gettext import gettext as _ |
2848 | 93 | gettext.textdomain('quickly-widgets') | 96 | gettext.textdomain('quickly-widgets') |
2849 | 94 | 97 | ||
2851 | 95 | class MediaPlayerBox(gtk.VBox): | 98 | class MediaPlayerBox(Gtk.VBox): |
2852 | 96 | """MediaPlayerBox - A VBox that tries to play the media file as defined by it's URU property. | 99 | """MediaPlayerBox - A VBox that tries to play the media file as defined by it's URU property. |
2853 | 97 | It works for video and sound files. | 100 | It works for video and sound files. |
2854 | 98 | 101 | ||
2855 | @@ -102,13 +105,13 @@ | |||
2856 | 102 | """Creates a MediaPlayerBox, Note that this does not start media. | 105 | """Creates a MediaPlayerBox, Note that this does not start media. |
2857 | 103 | For that, set the uri property and then call play(). | 106 | For that, set the uri property and then call play(). |
2858 | 104 | 107 | ||
2860 | 105 | This function has no arguments | 108 | This function has no argumentsf |
2861 | 106 | 109 | ||
2862 | 107 | """ | 110 | """ |
2865 | 108 | gtk.VBox.__init__(self, False, 5) | 111 | Gtk.VBox.__init__(self, False, 5) |
2866 | 109 | self.video_window = gtk.DrawingArea() | 112 | self.video_window = Gtk.DrawingArea() |
2867 | 110 | self.video_window.connect("realize",self.__on_video_window_realized) | 113 | self.video_window.connect("realize",self.__on_video_window_realized) |
2869 | 111 | # self.pack_start(self.video_window, True, True) | 114 | # self.pack_start(self.video_window, True, True, 0) |
2870 | 112 | self.video_window.show() | 115 | self.video_window.show() |
2871 | 113 | self.connect("destroy", self.on_destroy) | 116 | self.connect("destroy", self.on_destroy) |
2872 | 114 | 117 | ||
2873 | @@ -121,21 +124,21 @@ | |||
2874 | 121 | self.__uri = "" | 124 | self.__uri = "" |
2875 | 122 | self.realized = False | 125 | self.realized = False |
2876 | 123 | 126 | ||
2878 | 124 | self.controls = gtk.Toolbar() | 127 | self.controls = Gtk.Toolbar() |
2879 | 125 | if show_controls: | 128 | if show_controls: |
2880 | 126 | self.controls.show() | 129 | self.controls.show() |
2883 | 127 | self.pack_start(self.controls, False, False) | 130 | self.pack_start(self.controls, False, False, 0) |
2884 | 128 | self.pack_start(self.video_window, True, True) | 131 | self.pack_start(self.video_window, True, True, 0) |
2885 | 129 | 132 | ||
2888 | 130 | self.play_button = gtk.ToggleToolButton() | 133 | self.play_button = Gtk.ToggleToolButton() |
2889 | 131 | self.play_button.set_stock_id(gtk.STOCK_MEDIA_PLAY) | 134 | self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PLAY) |
2890 | 132 | self.play_button.show() | 135 | self.play_button.show() |
2891 | 133 | self._play_button_toggled_handler = self.play_button.connect("toggled",self._play_button_toggled) | 136 | self._play_button_toggled_handler = self.play_button.connect("toggled",self._play_button_toggled) |
2892 | 134 | self.controls.add(self.play_button) | 137 | self.controls.add(self.play_button) |
2893 | 135 | 138 | ||
2895 | 136 | item = gtk.ToolItem() | 139 | item = Gtk.ToolItem() |
2896 | 137 | item.show() | 140 | item.show() |
2898 | 138 | self.slider = gtk.HScale() | 141 | self.slider = Gtk.HScale() |
2899 | 139 | self.slider_changed_handler = None | 142 | self.slider_changed_handler = None |
2900 | 140 | self.slider.set_draw_value(False) | 143 | self.slider.set_draw_value(False) |
2901 | 141 | self.slider.set_increments(10,60) | 144 | self.slider.set_increments(10,60) |
2902 | @@ -145,9 +148,9 @@ | |||
2903 | 145 | item.add(self.slider) | 148 | item.add(self.slider) |
2904 | 146 | self.controls.insert(item, -1) | 149 | self.controls.insert(item, -1) |
2905 | 147 | 150 | ||
2907 | 148 | item2 = gtk.ToolItem() | 151 | item2 = Gtk.ToolItem() |
2908 | 149 | item2.show() | 152 | item2.show() |
2910 | 150 | self.time_label = gtk.Label("") | 153 | self.time_label = Gtk.Label("") |
2911 | 151 | self.time_label.show() | 154 | self.time_label.show() |
2912 | 152 | item2.add(self.time_label) | 155 | item2.add(self.time_label) |
2913 | 153 | self.controls.insert(item2, -1) | 156 | self.controls.insert(item2, -1) |
2914 | @@ -175,7 +178,7 @@ | |||
2915 | 175 | self.slider.set_sensitive(True) | 178 | self.slider.set_sensitive(True) |
2916 | 176 | self._reformat_slider() | 179 | self._reformat_slider() |
2917 | 177 | self._start_slider_updates() | 180 | self._start_slider_updates() |
2919 | 178 | self.play_button.set_stock_id(gtk.STOCK_MEDIA_PAUSE) | 181 | self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PAUSE) |
2920 | 179 | self._set_play_button_active(True) | 182 | self._set_play_button_active(True) |
2921 | 180 | 183 | ||
2922 | 181 | def pause(self): | 184 | def pause(self): |
2923 | @@ -186,11 +189,12 @@ | |||
2924 | 186 | This function has no arguments | 189 | This function has no arguments |
2925 | 187 | 190 | ||
2926 | 188 | """ | 191 | """ |
2927 | 192 | |||
2928 | 189 | self.playbin.set_state(gst.STATE_PAUSED) | 193 | self.playbin.set_state(gst.STATE_PAUSED) |
2929 | 190 | self.slider.set_sensitive(True) | 194 | self.slider.set_sensitive(True) |
2930 | 191 | self._reformat_slider() | 195 | self._reformat_slider() |
2931 | 192 | self.slider.set_sensitive(True) | 196 | self.slider.set_sensitive(True) |
2933 | 193 | self.play_button.set_stock_id(gtk.STOCK_MEDIA_PLAY) | 197 | self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PLAY) |
2934 | 194 | self._set_play_button_active(False) | 198 | self._set_play_button_active(False) |
2935 | 195 | 199 | ||
2936 | 196 | def stop(self): | 200 | def stop(self): |
2937 | @@ -203,7 +207,7 @@ | |||
2938 | 203 | 207 | ||
2939 | 204 | self.playbin.set_state(gst.STATE_NULL) | 208 | self.playbin.set_state(gst.STATE_NULL) |
2940 | 205 | self.slider.set_sensitive(False) | 209 | self.slider.set_sensitive(False) |
2942 | 206 | self.play_button.set_stock_id(gtk.STOCK_MEDIA_PLAY) | 210 | self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PLAY) |
2943 | 207 | self._set_play_button_active(False) | 211 | self._set_play_button_active(False) |
2944 | 208 | self.slider.set_value(0) | 212 | self.slider.set_value(0) |
2945 | 209 | 213 | ||
2946 | @@ -305,7 +309,7 @@ | |||
2947 | 305 | if self.playbin.get_state()[1] == gst.STATE_NULL: | 309 | if self.playbin.get_state()[1] == gst.STATE_NULL: |
2948 | 306 | self.slider.set_range(0, 0) | 310 | self.slider.set_range(0, 0) |
2949 | 307 | else: | 311 | else: |
2951 | 308 | gobject.idle_add(self._set_slider_range) | 312 | GObject.idle_add(self._set_slider_range) |
2952 | 309 | 313 | ||
2953 | 310 | def _set_slider_range(self): | 314 | def _set_slider_range(self): |
2954 | 311 | dur = self.duration | 315 | dur = self.duration |
2955 | @@ -313,16 +317,16 @@ | |||
2956 | 313 | return True | 317 | return True |
2957 | 314 | else: | 318 | else: |
2958 | 315 | self._duration_time_str = self._formatted_time(dur) | 319 | self._duration_time_str = self._formatted_time(dur) |
2960 | 316 | gtk.gdk.threads_enter() | 320 | Gdk.threads_enter() |
2961 | 317 | self.slider.set_range(0, dur) | 321 | self.slider.set_range(0, dur) |
2963 | 318 | gtk.gdk.threads_leave() | 322 | Gdk.threads_leave() |
2964 | 319 | return False | 323 | return False |
2965 | 320 | 324 | ||
2966 | 321 | def _start_slider_updates(self): | 325 | def _start_slider_updates(self): |
2967 | 322 | if self.playbin.get_state()[1] == gst.STATE_NULL: | 326 | if self.playbin.get_state()[1] == gst.STATE_NULL: |
2968 | 323 | self.slide.set_value(0) | 327 | self.slide.set_value(0) |
2969 | 324 | else: | 328 | else: |
2971 | 325 | gobject.timeout_add(1000, self._set_slider_position) | 329 | GObject.timeout_add(1000, self._set_slider_position) |
2972 | 326 | 330 | ||
2973 | 327 | def _set_slider_position(self): | 331 | def _set_slider_position(self): |
2974 | 328 | if self._slider_changed_handler is not None: | 332 | if self._slider_changed_handler is not None: |
2975 | @@ -395,7 +399,7 @@ | |||
2976 | 395 | if message_name == "prepare-xwindow-id": | 399 | if message_name == "prepare-xwindow-id": |
2977 | 396 | imagesink = message.src | 400 | imagesink = message.src |
2978 | 397 | imagesink.set_property("force-aspect-ratio", True) | 401 | imagesink.set_property("force-aspect-ratio", True) |
2980 | 398 | imagesink.set_xwindow_id(self.video_window.window.xid) | 402 | imagesink.set_xwindow_id(self.video_window.get_window().get_xid()) |
2981 | 399 | 403 | ||
2982 | 400 | def __on_video_window_realized(self, widget, data=None): | 404 | def __on_video_window_realized(self, widget, data=None): |
2983 | 401 | """__on_video_window_realized - internal signal handler, used | 405 | """__on_video_window_realized - internal signal handler, used |
2984 | @@ -407,16 +411,16 @@ | |||
2985 | 407 | self._set_video_window_id() | 411 | self._set_video_window_id() |
2986 | 408 | 412 | ||
2987 | 409 | def _set_video_window_id(self): | 413 | def _set_video_window_id(self): |
2990 | 410 | if not self.realized and self.video_window.window is not None: | 414 | if not self.realized and self.video_window.get_window() is not None: |
2991 | 411 | x = self.video_window.window.xid | 415 | x = self.video_window.get_window().get_xid() |
2992 | 412 | self.realized = True | 416 | self.realized = True |
2993 | 413 | 417 | ||
2994 | 414 | def on_destroy(self, widget, data=None): | 418 | def on_destroy(self, widget, data=None): |
2995 | 415 | #clean up the camera before exiting | 419 | #clean up the camera before exiting |
2996 | 416 | self.playbin.set_state(gst.STATE_NULL) | 420 | self.playbin.set_state(gst.STATE_NULL) |
2997 | 417 | 421 | ||
3000 | 418 | __gsignals__ = {'end-of-file' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 422 | __gsignals__ = {'end-of-file' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, |
3001 | 419 | (gobject.TYPE_PYOBJECT,)), | 423 | (GObject.TYPE_PYOBJECT,)), |
3002 | 420 | } | 424 | } |
3003 | 421 | 425 | ||
3004 | 422 | def __seek_func(sender, mb): | 426 | def __seek_func(sender, mb): |
3005 | @@ -445,16 +449,15 @@ | |||
3006 | 445 | 449 | ||
3007 | 446 | if __name__ == "__main__": | 450 | if __name__ == "__main__": |
3008 | 447 | """creates a test WebCamBox""" | 451 | """creates a test WebCamBox""" |
3009 | 448 | import quickly.prompts | ||
3010 | 449 | 452 | ||
3011 | 450 | #create and show a test window | 453 | #create and show a test window |
3013 | 451 | win = gtk.Window(gtk.WINDOW_TOPLEVEL) | 454 | win = Gtk.Window() |
3014 | 452 | win.set_title("WebCam Test Window") | 455 | win.set_title("WebCam Test Window") |
3016 | 453 | win.connect("destroy",gtk.main_quit) | 456 | win.connect("destroy",Gtk.main_quit) |
3017 | 454 | win.show() | 457 | win.show() |
3018 | 455 | 458 | ||
3019 | 456 | #create a top level container | 459 | #create a top level container |
3021 | 457 | vbox = gtk.VBox(False, 10) | 460 | vbox = Gtk.VBox(False, 10) |
3022 | 458 | vbox.show() | 461 | vbox.show() |
3023 | 459 | win.add(vbox) | 462 | win.add(vbox) |
3024 | 460 | 463 | ||
3025 | @@ -464,47 +467,47 @@ | |||
3026 | 464 | vbox.add(mb) | 467 | vbox.add(mb) |
3027 | 465 | mb.show() | 468 | mb.show() |
3028 | 466 | 469 | ||
3030 | 467 | uri_entry = gtk.Entry() | 470 | uri_entry = Gtk.Entry() |
3031 | 468 | 471 | ||
3038 | 469 | play_butt = gtk.Button("Play") | 472 | play_butt = Gtk.Button("Play") |
3039 | 470 | pause_butt = gtk.Button("Pause") | 473 | pause_butt = Gtk.Button("Pause") |
3040 | 471 | stop_butt = gtk.Button("Stop") | 474 | stop_butt = Gtk.Button("Stop") |
3041 | 472 | seek_butt = gtk.Button("Seek") | 475 | seek_butt = Gtk.Button("Seek") |
3042 | 473 | controls_butt = gtk.ToggleButton("Controls") | 476 | controls_butt = Gtk.ToggleButton("Controls") |
3043 | 474 | time_label = gtk.Label("") | 477 | time_label = Gtk.Label("") |
3044 | 475 | 478 | ||
3045 | 476 | play_butt.connect("clicked", lambda x:mb.play()) | 479 | play_butt.connect("clicked", lambda x:mb.play()) |
3046 | 477 | play_butt.show() | 480 | play_butt.show() |
3048 | 478 | mb.pack_end(play_butt, False) | 481 | mb.pack_end(play_butt, False, False, 0) |
3049 | 479 | 482 | ||
3050 | 480 | uri_entry.connect("activate", __set_uri, (mb, uri_entry)) | 483 | uri_entry.connect("activate", __set_uri, (mb, uri_entry)) |
3051 | 481 | uri_entry.set_text("file:///home/rick/Videos/VID00110.AVI") | 484 | uri_entry.set_text("file:///home/rick/Videos/VID00110.AVI") |
3052 | 482 | uri_entry.show() | 485 | uri_entry.show() |
3054 | 483 | mb.pack_end(uri_entry, False) | 486 | mb.pack_end(uri_entry, False, False, 0) |
3055 | 484 | 487 | ||
3056 | 485 | pause_butt.connect("clicked", lambda x:mb.pause()) | 488 | pause_butt.connect("clicked", lambda x:mb.pause()) |
3057 | 486 | pause_butt.show() | 489 | pause_butt.show() |
3059 | 487 | mb.pack_end(pause_butt, False) | 490 | mb.pack_end(pause_butt, False, False, 0) |
3060 | 488 | 491 | ||
3061 | 489 | stop_butt.connect("clicked", lambda x:mb.stop()) | 492 | stop_butt.connect("clicked", lambda x:mb.stop()) |
3062 | 490 | stop_butt.show() | 493 | stop_butt.show() |
3064 | 491 | mb.pack_end(stop_butt, False) | 494 | mb.pack_end(stop_butt, False, False, 0) |
3065 | 492 | 495 | ||
3066 | 493 | seek_butt.connect("clicked", __seek_func, mb) | 496 | seek_butt.connect("clicked", __seek_func, mb) |
3067 | 494 | seek_butt.show() | 497 | seek_butt.show() |
3069 | 495 | mb.pack_end(seek_butt, False) | 498 | mb.pack_end(seek_butt, False, False, 0) |
3070 | 496 | 499 | ||
3071 | 497 | controls_butt.connect("clicked", __controls_func, mb) | 500 | controls_butt.connect("clicked", __controls_func, mb) |
3072 | 498 | controls_butt.show() | 501 | controls_butt.show() |
3074 | 499 | mb.pack_end(controls_butt, False) | 502 | mb.pack_end(controls_butt, False, False, 0) |
3075 | 500 | 503 | ||
3076 | 501 | mb.connect("end-of-file", __on_media_ended) | 504 | mb.connect("end-of-file", __on_media_ended) |
3077 | 502 | 505 | ||
3078 | 503 | time_label.show() | 506 | time_label.show() |
3084 | 504 | mb.pack_end(time_label, False) | 507 | mb.pack_end(time_label, False, False, 0) |
3085 | 505 | 508 | ||
3086 | 506 | gobject.timeout_add(1000, __seek_time, (mb, time_label)) | 509 | GObject.timeout_add(1000, __seek_time, (mb, time_label)) |
3087 | 507 | 510 | ||
3088 | 508 | gtk.main() | 511 | Gtk.main() |
3089 | 509 | 512 | ||
3090 | 510 | 513 | ||
3091 | 511 | 514 | ||
3092 | === modified file 'quickly/widgets/press_and_hold_button.py' | |||
3093 | --- quickly/widgets/press_and_hold_button.py 2011-01-17 20:27:12 +0000 | |||
3094 | +++ quickly/widgets/press_and_hold_button.py 2012-03-06 08:52:21 +0000 | |||
3095 | @@ -34,14 +34,14 @@ | |||
3096 | 34 | pah.set_labe("Press and Hold") | 34 | pah.set_labe("Press and Hold") |
3097 | 35 | 35 | ||
3098 | 36 | Extending | 36 | Extending |
3100 | 37 | A PressAndHoldButton is gtk.Button | 37 | A PressAndHoldButton is Gtk.Button |
3101 | 38 | 38 | ||
3102 | 39 | """ | 39 | """ |
3103 | 40 | 40 | ||
3106 | 41 | import gobject | 41 | from gi.repository import GObject |
3107 | 42 | import gtk | 42 | from gi.repository import Gtk |
3108 | 43 | 43 | ||
3110 | 44 | class PressAndHoldButton(gtk.Button): | 44 | class PressAndHoldButton(Gtk.Button): |
3111 | 45 | def __init__(self): | 45 | def __init__(self): |
3112 | 46 | """Create a PressAndHoldButton | 46 | """Create a PressAndHoldButton |
3113 | 47 | 47 | ||
3114 | @@ -51,7 +51,7 @@ | |||
3115 | 51 | 51 | ||
3116 | 52 | """ | 52 | """ |
3117 | 53 | 53 | ||
3119 | 54 | gtk.Button.__init__(self) | 54 | Gtk.Button.__init__(self) |
3120 | 55 | self.timeout = 250 | 55 | self.timeout = 250 |
3121 | 56 | self.connect("pressed",self.__pressed) | 56 | self.connect("pressed",self.__pressed) |
3122 | 57 | self.connect("released",self.__released) | 57 | self.connect("released",self.__released) |
3123 | @@ -60,7 +60,7 @@ | |||
3124 | 60 | def __pressed(self, widget, data=None): | 60 | def __pressed(self, widget, data=None): |
3125 | 61 | self.__continue_ticking = True | 61 | self.__continue_ticking = True |
3126 | 62 | widget.emit("tick",self) | 62 | widget.emit("tick",self) |
3128 | 63 | gobject.timeout_add(self.timeout, self.__tick) | 63 | GObject.timeout_add(self.timeout, self.__tick) |
3129 | 64 | 64 | ||
3130 | 65 | def __released(self, widget, data=None): | 65 | def __released(self, widget, data=None): |
3131 | 66 | self.__continue_ticking = False | 66 | self.__continue_ticking = False |
3132 | @@ -70,7 +70,44 @@ | |||
3133 | 70 | self.emit("tick",self) | 70 | self.emit("tick",self) |
3134 | 71 | return self.__continue_ticking | 71 | return self.__continue_ticking |
3135 | 72 | 72 | ||
3138 | 73 | __gsignals__ = {'tick' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 73 | __gsignals__ = {'tick' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, |
3139 | 74 | (gobject.TYPE_PYOBJECT,)), | 74 | (GObject.TYPE_PYOBJECT,)), |
3140 | 75 | } | 75 | } |
3141 | 76 | 76 | ||
3142 | 77 | def __test_tick(sender, widget, label): | ||
3143 | 78 | """internal method for testing. | ||
3144 | 79 | Do not use. | ||
3145 | 80 | """ | ||
3146 | 81 | |||
3147 | 82 | label.set_text(str(int(label.get_text()) + 1)) | ||
3148 | 83 | |||
3149 | 84 | |||
3150 | 85 | |||
3151 | 86 | if __name__ == "__main__": | ||
3152 | 87 | """creates a test PressAndHoldButton""" | ||
3153 | 88 | |||
3154 | 89 | #create and show a test window | ||
3155 | 90 | win = Gtk.Window() | ||
3156 | 91 | win.set_title("Press and Hold Test Window") | ||
3157 | 92 | win.connect("destroy",Gtk.main_quit) | ||
3158 | 93 | win.show() | ||
3159 | 94 | |||
3160 | 95 | #create a top level container | ||
3161 | 96 | vbox = Gtk.VBox(False, 10) | ||
3162 | 97 | vbox.show() | ||
3163 | 98 | win.add(vbox) | ||
3164 | 99 | |||
3165 | 100 | button = PressAndHoldButton() | ||
3166 | 101 | button.set_label("Press and hold") | ||
3167 | 102 | button.show() | ||
3168 | 103 | vbox.pack_start(button, False, False, 5) | ||
3169 | 104 | |||
3170 | 105 | label = Gtk.Label("0") | ||
3171 | 106 | label.show() | ||
3172 | 107 | vbox.pack_end(label, False, False, 5) | ||
3173 | 108 | |||
3174 | 109 | button.timeout = 10 | ||
3175 | 110 | |||
3176 | 111 | button.connect("tick",__test_tick, label) | ||
3177 | 112 | |||
3178 | 113 | Gtk.main() | ||
3179 | 77 | 114 | ||
3180 | === removed file 'quickly/widgets/tests/test_asycnh_task_progress_box.py' | |||
3181 | --- quickly/widgets/tests/test_asycnh_task_progress_box.py 2010-03-30 23:38:02 +0000 | |||
3182 | +++ quickly/widgets/tests/test_asycnh_task_progress_box.py 1970-01-01 00:00:00 +0000 | |||
3183 | @@ -1,51 +0,0 @@ | |||
3184 | 1 | ### BEGIN LICENSE | ||
3185 | 2 | # Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com | ||
3186 | 3 | #This program is free software: you can redistribute it and/or modify it | ||
3187 | 4 | #under the terms of the GNU General Public License version 3, as published | ||
3188 | 5 | #by the Free Software Foundation. | ||
3189 | 6 | # | ||
3190 | 7 | #This program is distributed in the hope that it will be useful, but | ||
3191 | 8 | #WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3192 | 9 | #MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3193 | 10 | #PURPOSE. See the GNU General Public License for more details. | ||
3194 | 11 | # | ||
3195 | 12 | #You should have received a copy of the GNU General Public License along | ||
3196 | 13 | #with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3197 | 14 | ### END LICENSE | ||
3198 | 15 | |||
3199 | 16 | """Tests for the AsyncTaskProgressBox""" | ||
3200 | 17 | |||
3201 | 18 | from testtools import TestCase | ||
3202 | 19 | from quickly.widgets.asynch_task_progressbox import AsynchTaskProgressBox | ||
3203 | 20 | |||
3204 | 21 | class TestAsynchTaskProgessBox(TestCase): | ||
3205 | 22 | """Test the CouchGrid functionality""" | ||
3206 | 23 | |||
3207 | 24 | def setUp(self): | ||
3208 | 25 | TestCase.setUp(self) | ||
3209 | 26 | |||
3210 | 27 | def tearDown(self): | ||
3211 | 28 | TestCase.tearDown(self) | ||
3212 | 29 | |||
3213 | 30 | def test_constructions(self): | ||
3214 | 31 | """Test a simple creating An AsynchTaskProgressBox """ | ||
3215 | 32 | box = AsynchTaskProgressBox(self.asynch_function) | ||
3216 | 33 | self.assertEqual((box != None), True) | ||
3217 | 34 | |||
3218 | 35 | #A function to run asynchronously | ||
3219 | 36 | def asynch_function( self, params ): | ||
3220 | 37 | #pull values from the params that were set above | ||
3221 | 38 | for x in range(params["start"],params["stop"]): | ||
3222 | 39 | #check if to see if the user has told the task to stop | ||
3223 | 40 | if params["kill"] == True: | ||
3224 | 41 | #return a string if the user stopped the task | ||
3225 | 42 | return "stopped at " + str(x) | ||
3226 | 43 | else: | ||
3227 | 44 | #if the user did not try to stop the task, go ahead and do something | ||
3228 | 45 | print x | ||
3229 | 46 | #this is a processor intensive task, so | ||
3230 | 47 | #sleep the loop to keep the UI from bogging down | ||
3231 | 48 | time.sleep(.5) | ||
3232 | 49 | #if the loop completes, return a string | ||
3233 | 50 | return "counted all" | ||
3234 | 51 | |||
3235 | 52 | 0 | ||
3236 | === removed file 'quickly/widgets/tests/test_couch_grid.py' | |||
3237 | --- quickly/widgets/tests/test_couch_grid.py 2010-09-02 01:03:54 +0000 | |||
3238 | +++ quickly/widgets/tests/test_couch_grid.py 1970-01-01 00:00:00 +0000 | |||
3239 | @@ -1,284 +0,0 @@ | |||
3240 | 1 | # Copyright 2009 Canonical Ltd. | ||
3241 | 2 | # | ||
3242 | 3 | # This file is part of desktopcouch. | ||
3243 | 4 | # | ||
3244 | 5 | # desktopcouch is free software: you can redistribute it and/or modify | ||
3245 | 6 | # it under the terms of the GNU Lesser General Public License version 3 | ||
3246 | 7 | # as published by the Free Software Foundation. | ||
3247 | 8 | # | ||
3248 | 9 | # desktopcouch is distributed in the hope that it will be useful, | ||
3249 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3250 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3251 | 12 | # GNU Lesser General Public License for more details. | ||
3252 | 13 | # | ||
3253 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
3254 | 15 | # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>. | ||
3255 | 16 | # | ||
3256 | 17 | # Authors: Rick Spencer <rick.spencer@canonical.com> | ||
3257 | 18 | |||
3258 | 19 | """Tests for the CouchGrid object""" | ||
3259 | 20 | |||
3260 | 21 | from testtools import TestCase | ||
3261 | 22 | |||
3262 | 23 | from desktopcouch.records.record import Record | ||
3263 | 24 | from desktopcouch.records.server import CouchDatabase | ||
3264 | 25 | from quickly.widgets.couch_grid import CouchGrid | ||
3265 | 26 | |||
3266 | 27 | |||
3267 | 28 | class TestCouchGrid(TestCase): | ||
3268 | 29 | """Test the CouchGrid functionality""" | ||
3269 | 30 | |||
3270 | 31 | def setUp(self): | ||
3271 | 32 | TestCase.setUp(self) | ||
3272 | 33 | self.dbname = self._testMethodName | ||
3273 | 34 | self.db = CouchDatabase(self.dbname, create=True) | ||
3274 | 35 | self.record_type = "test_record_type" | ||
3275 | 36 | |||
3276 | 37 | def tearDown(self): | ||
3277 | 38 | """tear down each test""" | ||
3278 | 39 | TestCase.tearDown(self) | ||
3279 | 40 | #delete the database | ||
3280 | 41 | del self.db._server[self.dbname] | ||
3281 | 42 | |||
3282 | 43 | def test_constructor_guarded(self): | ||
3283 | 44 | """Ensure that CouchGrid cannot be constructed without a | ||
3284 | 45 | database name. | ||
3285 | 46 | """ | ||
3286 | 47 | try: | ||
3287 | 48 | cw = CouchGrid(None) | ||
3288 | 49 | except TypeError, inst: | ||
3289 | 50 | self.assertEqual( | ||
3290 | 51 | inst.args[0],"database_name is required and must be a string") | ||
3291 | 52 | |||
3292 | 53 | def test_new_rows_with_headings(self): | ||
3293 | 54 | """Test a simple creating a CouchGrid """ | ||
3294 | 55 | |||
3295 | 56 | #create a test widget with test database values | ||
3296 | 57 | cw = CouchGrid(self.dbname) | ||
3297 | 58 | |||
3298 | 59 | #allow editing | ||
3299 | 60 | cw.editable = True | ||
3300 | 61 | |||
3301 | 62 | #create headers/keys | ||
3302 | 63 | cw.keys = ["Key1", "Key2", "Key3", "Key4"] | ||
3303 | 64 | |||
3304 | 65 | #set the record_type for the TreeView | ||
3305 | 66 | #it will not populate without this value being set | ||
3306 | 67 | cw.record_type = self.record_type | ||
3307 | 68 | |||
3308 | 69 | #create a row with all four columns set | ||
3309 | 70 | cw.append_row({"Key1":"val1", "Key2":"val2", "Key2":"val3", "Key4":"val4"}) | ||
3310 | 71 | |||
3311 | 72 | #create a row with only the second column set | ||
3312 | 73 | cw.append_row({"Key1":"", "Key2":"val2"}) | ||
3313 | 74 | |||
3314 | 75 | #create an empty row (which will not be saved until the user edits it) | ||
3315 | 76 | cw.append_row({}) | ||
3316 | 77 | |||
3317 | 78 | #if this all worked, there should be three rows in the model | ||
3318 | 79 | model = cw.get_model() | ||
3319 | 80 | self.assertEqual(len(model), 3) | ||
3320 | 81 | |||
3321 | 82 | def test_headings_no_stored_records(self): | ||
3322 | 83 | record_type = "a_new_record_type" | ||
3323 | 84 | dicts = [{"key1":"val1"},{"key1":"val2"}] | ||
3324 | 85 | cw = CouchGrid(self.dbname, record_type=record_type,dictionaries=dicts) | ||
3325 | 86 | self.assertEqual(len(cw.get_model()),2) | ||
3326 | 87 | self.assertEqual(cw.get_model().get_n_columns(),2) | ||
3327 | 88 | |||
3328 | 89 | def test_no_headings_or_stored_records(self): | ||
3329 | 90 | """test when there is no defined headings and no stored records | ||
3330 | 91 | to infer headings from. Should raise a proper exception. | ||
3331 | 92 | """ | ||
3332 | 93 | |||
3333 | 94 | try: | ||
3334 | 95 | #create a test widget with test database values | ||
3335 | 96 | cw = CouchGrid(self.dbname) | ||
3336 | 97 | |||
3337 | 98 | #set the record_type for the TreeView | ||
3338 | 99 | #it will not populate without this value being set | ||
3339 | 100 | cw.record_type = self.record_type | ||
3340 | 101 | |||
3341 | 102 | #create a row with all four columns set | ||
3342 | 103 | cw.append_row(["val1", "val2", "val3", "val4"]) | ||
3343 | 104 | |||
3344 | 105 | #create a row with only the second column set | ||
3345 | 106 | cw.append_row(["", "val2"]) | ||
3346 | 107 | |||
3347 | 108 | #create an empty row (which will not be saved until the | ||
3348 | 109 | #user edits it) | ||
3349 | 110 | cw.append_row([]) | ||
3350 | 111 | |||
3351 | 112 | #if this all worked, there should be three rows in the model | ||
3352 | 113 | model = cw.get_model() | ||
3353 | 114 | |||
3354 | 115 | #should be catching the following exception | ||
3355 | 116 | except RuntimeError, inst: | ||
3356 | 117 | self.assertEquals( | ||
3357 | 118 | inst.args[0].find("Cannot infer columns for CouchGrid"),0) | ||
3358 | 119 | |||
3359 | 120 | |||
3360 | 121 | def test_all_from_database(self): | ||
3361 | 122 | #create some records | ||
3362 | 123 | db = CouchDatabase(self.dbname, create=True) | ||
3363 | 124 | db.put_record(Record({ | ||
3364 | 125 | "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3", | ||
3365 | 126 | "record_type": self.record_type})) | ||
3366 | 127 | db.put_record(Record({ | ||
3367 | 128 | "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3", | ||
3368 | 129 | "record_type": self.record_type})) | ||
3369 | 130 | |||
3370 | 131 | #build the CouchGrid | ||
3371 | 132 | cw = CouchGrid(self.dbname) | ||
3372 | 133 | cw.record_type = self.record_type | ||
3373 | 134 | #make sure there are three columns and two rows | ||
3374 | 135 | self.assertEqual(cw.get_model().get_n_columns(),4) | ||
3375 | 136 | self.assertEqual(len(cw.get_model()),2) | ||
3376 | 137 | |||
3377 | 138 | def test_delete_selected_rows(self): | ||
3378 | 139 | #create some records | ||
3379 | 140 | db = CouchDatabase(self.dbname, create=True) | ||
3380 | 141 | ids = [] | ||
3381 | 142 | for i in xrange(0,10): | ||
3382 | 143 | ids.append( db.put_record(Record({ | ||
3383 | 144 | "key1_1": "val1_%s" % str(i), "iter_count": i, | ||
3384 | 145 | "record_type": self.record_type}))) | ||
3385 | 146 | |||
3386 | 147 | #build the CouchGrid | ||
3387 | 148 | cw = CouchGrid(self.dbname, record_type = self.record_type) | ||
3388 | 149 | cw.selected_record_ids = [ids[0],ids[5],ids[9]] | ||
3389 | 150 | cw.remove_selected_rows(delete=True) | ||
3390 | 151 | self.assertEqual(self.db.get_record(ids[0]) is None,True) | ||
3391 | 152 | self.assertEqual(self.db.get_record(ids[5]) is None,True) | ||
3392 | 153 | self.assertEqual(self.db.get_record(ids[9]) is None,True) | ||
3393 | 154 | |||
3394 | 155 | self.assertEqual(self.db.get_record(ids[1]) is not None,True) | ||
3395 | 156 | self.assertEqual(self.db.get_record(ids[2]) is not None,True) | ||
3396 | 157 | self.assertEqual(self.db.get_record(ids[3]) is not None,True) | ||
3397 | 158 | self.assertEqual(self.db.get_record(ids[4]) is not None,True) | ||
3398 | 159 | self.assertEqual(self.db.get_record(ids[6]) is not None,True) | ||
3399 | 160 | self.assertEqual(self.db.get_record(ids[7]) is not None,True) | ||
3400 | 161 | self.assertEqual(self.db.get_record(ids[8]) is not None,True) | ||
3401 | 162 | |||
3402 | 163 | def test_dont_delete_selected_rows(self): | ||
3403 | 164 | #create some records | ||
3404 | 165 | db = CouchDatabase(self.dbname, create=True) | ||
3405 | 166 | ids = [] | ||
3406 | 167 | for i in xrange(0,10): | ||
3407 | 168 | ids.append( db.put_record(Record({ | ||
3408 | 169 | "key1_1": "val1_%s" % str(i), "iter_count": i, | ||
3409 | 170 | "record_type": self.record_type}))) | ||
3410 | 171 | |||
3411 | 172 | #build the CouchGrid | ||
3412 | 173 | cw = CouchGrid(self.dbname, record_type = self.record_type) | ||
3413 | 174 | cw.selected_record_ids = [ids[0],ids[5],ids[9]] | ||
3414 | 175 | cw.remove_selected_rows(delete=False) | ||
3415 | 176 | cw.selected_record_ids = [ids[1],ids[4],ids[8]] | ||
3416 | 177 | cw.remove_selected_rows() | ||
3417 | 178 | self.assertEqual(self.db.get_record(ids[0]) is not None,True) | ||
3418 | 179 | self.assertEqual(self.db.get_record(ids[5]) is not None,True) | ||
3419 | 180 | self.assertEqual(self.db.get_record(ids[9]) is not None,True) | ||
3420 | 181 | |||
3421 | 182 | self.assertEqual(self.db.get_record(ids[1]) is not None,True) | ||
3422 | 183 | self.assertEqual(self.db.get_record(ids[2]) is not None,True) | ||
3423 | 184 | self.assertEqual(self.db.get_record(ids[3]) is not None,True) | ||
3424 | 185 | self.assertEqual(self.db.get_record(ids[4]) is not None,True) | ||
3425 | 186 | self.assertEqual(self.db.get_record(ids[6]) is not None,True) | ||
3426 | 187 | self.assertEqual(self.db.get_record(ids[7]) is not None,True) | ||
3427 | 188 | self.assertEqual(self.db.get_record(ids[8]) is not None,True) | ||
3428 | 189 | |||
3429 | 190 | |||
3430 | 191 | |||
3431 | 192 | def test_selected_id_property(self): | ||
3432 | 193 | #create some records | ||
3433 | 194 | db = CouchDatabase(self.dbname, create=True) | ||
3434 | 195 | id1 = db.put_record(Record({ | ||
3435 | 196 | "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3", | ||
3436 | 197 | "record_type": self.record_type})) | ||
3437 | 198 | id2 = db.put_record(Record({ | ||
3438 | 199 | "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3", | ||
3439 | 200 | "record_type": self.record_type})) | ||
3440 | 201 | |||
3441 | 202 | #build the CouchGrid | ||
3442 | 203 | cw = CouchGrid(self.dbname) | ||
3443 | 204 | cw.record_type = self.record_type | ||
3444 | 205 | |||
3445 | 206 | #make sure the record ids are selected properly | ||
3446 | 207 | cw.selected_record_ids = [id1] | ||
3447 | 208 | self.assertEqual(cw.selected_record_ids[0], id1) | ||
3448 | 209 | cw.selected_record_ids = [id2] | ||
3449 | 210 | self.assertEqual(cw.selected_record_ids[0], id2) | ||
3450 | 211 | |||
3451 | 212 | def test_single_col_from_database(self): | ||
3452 | 213 | #create some records | ||
3453 | 214 | self.db.put_record(Record({ | ||
3454 | 215 | "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3", | ||
3455 | 216 | "record_type": self.record_type})) | ||
3456 | 217 | self.db.put_record(Record({ | ||
3457 | 218 | "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3", | ||
3458 | 219 | "record_type": self.record_type})) | ||
3459 | 220 | #build the CouchGrid | ||
3460 | 221 | cw = CouchGrid(self.dbname) | ||
3461 | 222 | cw.keys = ["key1_1"] | ||
3462 | 223 | cw.record_type = self.record_type | ||
3463 | 224 | #make sure there are three columns and two rows | ||
3464 | 225 | self.assertEqual(cw.get_model().get_n_columns(),2) | ||
3465 | 226 | self.assertEqual(len(cw.get_model()),2) | ||
3466 | 227 | |||
3467 | 228 | def test_optional_record_type_arg(self): | ||
3468 | 229 | """Test a simple creating a CouchGrid """ | ||
3469 | 230 | #create some records | ||
3470 | 231 | self.db.put_record(Record({ | ||
3471 | 232 | "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3", | ||
3472 | 233 | "record_type": self.record_type})) | ||
3473 | 234 | self.db.put_record(Record({ | ||
3474 | 235 | "key1_1": "val1_1", "key1_2": "val2_2", "key1_3": "val2_3", | ||
3475 | 236 | "record_type": self.record_type})) | ||
3476 | 237 | |||
3477 | 238 | #create a test widget with test database values | ||
3478 | 239 | cw = CouchGrid(self.dbname, record_type=self.record_type) | ||
3479 | 240 | |||
3480 | 241 | #make sure there are three columns and two rows | ||
3481 | 242 | self.assertEqual(cw.get_model().get_n_columns(),4) | ||
3482 | 243 | self.assertEqual(len(cw.get_model()),2) | ||
3483 | 244 | |||
3484 | 245 | def test_optional_args_no_stored_records(self): | ||
3485 | 246 | """Test a simple creating a CouchGrid """ | ||
3486 | 247 | |||
3487 | 248 | #create a test widget with test database values | ||
3488 | 249 | cw = CouchGrid( | ||
3489 | 250 | self.dbname, record_type=self.record_type, | ||
3490 | 251 | keys=["Key1", "Key2", "Key3", "Key4"]) | ||
3491 | 252 | |||
3492 | 253 | #create a row with all four columns set | ||
3493 | 254 | cw.append_row({"Key1":"val1", "Key2":"val2", "Key2":"val3", "Key4":"val4"}) | ||
3494 | 255 | |||
3495 | 256 | #create a row with only the second column set | ||
3496 | 257 | cw.append_row({"Key1":"", "Key2":"val2"}) | ||
3497 | 258 | |||
3498 | 259 | #create an empty row (which will not be saved until the user edits it) | ||
3499 | 260 | cw.append_row({}) | ||
3500 | 261 | |||
3501 | 262 | #if this all worked, there should be three rows in the model | ||
3502 | 263 | model = cw.get_model() | ||
3503 | 264 | self.assertEqual(len(model), 3) | ||
3504 | 265 | |||
3505 | 266 | def test_programatically_add_row(self): | ||
3506 | 267 | """test appending different sized rows programatically""" | ||
3507 | 268 | #create some records | ||
3508 | 269 | self.db.put_record(Record({ | ||
3509 | 270 | "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3", | ||
3510 | 271 | "record_type": self.record_type})) | ||
3511 | 272 | self.db.put_record(Record({ | ||
3512 | 273 | "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3", | ||
3513 | 274 | "record_type": self.record_type})) | ||
3514 | 275 | |||
3515 | 276 | #create a test widget with test database values | ||
3516 | 277 | cw = CouchGrid(self.dbname, record_type=self.record_type) | ||
3517 | 278 | |||
3518 | 279 | #allow editing | ||
3519 | 280 | cw.append_row({"key1_1":"boo", "key1_2":"ray"}) | ||
3520 | 281 | |||
3521 | 282 | #make sure there are three columns and two rows | ||
3522 | 283 | self.assertEqual(cw.get_model().get_n_columns(),4) | ||
3523 | 284 | self.assertEqual(len(cw.get_model()),3) | ||
3524 | 285 | 0 | ||
3525 | === modified file 'quickly/widgets/tests/test_dictionary_grid.py' | |||
3526 | --- quickly/widgets/tests/test_dictionary_grid.py 2011-09-05 06:23:54 +0000 | |||
3527 | +++ quickly/widgets/tests/test_dictionary_grid.py 2012-03-06 08:52:21 +0000 | |||
3528 | @@ -18,7 +18,7 @@ | |||
3529 | 18 | 18 | ||
3530 | 19 | from testtools import TestCase | 19 | from testtools import TestCase |
3531 | 20 | from quickly.widgets.dictionary_grid import DictionaryGrid | 20 | from quickly.widgets.dictionary_grid import DictionaryGrid |
3533 | 21 | import gobject | 21 | from gi.repository import GObject |
3534 | 22 | from quickly.widgets.grid_column import StringColumn, IntegerColumn, CurrencyColumn,CheckColumn, DateColumn | 22 | from quickly.widgets.grid_column import StringColumn, IntegerColumn, CurrencyColumn,CheckColumn, DateColumn |
3535 | 23 | 23 | ||
3536 | 24 | class TestDictionaryGrid(TestCase): | 24 | class TestDictionaryGrid(TestCase): |
3537 | @@ -42,7 +42,6 @@ | |||
3538 | 42 | grid.append_row({"key1":"val11","key2":"val12"}) | 42 | grid.append_row({"key1":"val11","key2":"val12"}) |
3539 | 43 | self.assertEqual(len(grid.get_model()),1) | 43 | self.assertEqual(len(grid.get_model()),1) |
3540 | 44 | 44 | ||
3541 | 45 | |||
3542 | 46 | def test_constructor_with_dicts(self): | 45 | def test_constructor_with_dicts(self): |
3543 | 47 | """test creating a grid with dictionaries in the contructor""" | 46 | """test creating a grid with dictionaries in the contructor""" |
3544 | 48 | dicts = [{"key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3"}, | 47 | dicts = [{"key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3"}, |
3545 | @@ -184,13 +183,13 @@ | |||
3546 | 184 | key = c.key | 183 | key = c.key |
3547 | 185 | col_type = c.column_type | 184 | col_type = c.column_type |
3548 | 186 | if key == "id": | 185 | if key == "id": |
3550 | 187 | self.assertEqual(col_type,gobject.TYPE_STRING) | 186 | self.assertEqual(col_type,GObject.TYPE_STRING) |
3551 | 188 | elif key == "price": | 187 | elif key == "price": |
3553 | 189 | self.assertEqual(col_type,gobject.TYPE_STRING) | 188 | self.assertEqual(col_type,GObject.TYPE_STRING) |
3554 | 190 | elif key == "bool?": | 189 | elif key == "bool?": |
3556 | 191 | self.assertEqual(col_type,gobject.TYPE_STRING) | 190 | self.assertEqual(col_type,GObject.TYPE_STRING) |
3557 | 192 | elif key == "foo": | 191 | elif key == "foo": |
3559 | 193 | self.assertEqual(col_type,gobject.TYPE_INT) | 192 | self.assertEqual(col_type,GObject.TYPE_INT) |
3560 | 194 | else: | 193 | else: |
3561 | 195 | self.assertEqual("Extra key Found",False) | 194 | self.assertEqual("Extra key Found",False) |
3562 | 196 | 195 | ||
3563 | @@ -253,9 +252,9 @@ | |||
3564 | 253 | dicts = [{"price":"100.00","id":"50","bool?":"Yes"}] | 252 | dicts = [{"price":"100.00","id":"50","bool?":"Yes"}] |
3565 | 254 | grid = DictionaryGrid(dicts, keys) | 253 | grid = DictionaryGrid(dicts, keys) |
3566 | 255 | grid.editable = False | 254 | grid.editable = False |
3568 | 256 | ed1 = grid.columns["price"].get_cell_renderers()[0].get_property("editable") | 255 | ed1 = grid.columns["price"].get_cells()[0].get_property("editable") |
3569 | 257 | self.assertTrue(not ed1) | 256 | self.assertTrue(not ed1) |
3571 | 258 | ed2 = grid.columns["bool?"].get_cell_renderers()[0].get_property("activatable") | 257 | ed2 = grid.columns["bool?"].get_cells()[0].get_property("activatable") |
3572 | 259 | self.assertTrue(not ed2) | 258 | self.assertTrue(not ed2) |
3573 | 260 | 259 | ||
3574 | 261 | def test_set_a_column_title(self): | 260 | def test_set_a_column_title(self): |
3575 | 262 | 261 | ||
3576 | === modified file 'quickly/widgets/text_editor.py' | |||
3577 | --- quickly/widgets/text_editor.py 2011-02-03 16:28:49 +0000 | |||
3578 | +++ quickly/widgets/text_editor.py 2012-03-06 08:52:21 +0000 | |||
3579 | @@ -35,25 +35,26 @@ | |||
3580 | 35 | 35 | ||
3581 | 36 | Configuring | 36 | Configuring |
3582 | 37 | #Configure as a TextView | 37 | #Configure as a TextView |
3584 | 38 | self.editor.set_wrap_mode(gtk.WRAP_CHAR) | 38 | self.editor.set_wrap_mode(Gtk.WRAP_CHAR) |
3585 | 39 | 39 | ||
3587 | 40 | #Access the gtk.TextBuffer if needed | 40 | #Access the Gtk.TextBuffer if needed |
3588 | 41 | buffer = self.editor.get_buffer() | 41 | buffer = self.editor.get_buffer() |
3589 | 42 | 42 | ||
3590 | 43 | Extending | 43 | Extending |
3592 | 44 | A TextEditor is gtk.TextView | 44 | A TextEditor is Gtk.TextView |
3593 | 45 | 45 | ||
3594 | 46 | """ | 46 | """ |
3595 | 47 | 47 | ||
3596 | 48 | |||
3597 | 48 | try: | 49 | try: |
3601 | 49 | import pygtk | 50 | from gi.repository import Gtk |
3602 | 50 | pygtk.require("2.0") | 51 | from gi.repository import Gdk |
3600 | 51 | import gtk | ||
3603 | 52 | import re | 52 | import re |
3604 | 53 | except: | 53 | except: |
3605 | 54 | print "couldn't load depencies" | 54 | print "couldn't load depencies" |
3606 | 55 | 55 | ||
3608 | 56 | class TextEditor( gtk.TextView ): | 56 | |
3609 | 57 | class TextEditor( Gtk.TextView ): | ||
3610 | 57 | """TextEditor encapsulates management of TextBuffer and TextIter for | 58 | """TextEditor encapsulates management of TextBuffer and TextIter for |
3611 | 58 | common functionality, such as cut, copy, paste, undo, redo, and | 59 | common functionality, such as cut, copy, paste, undo, redo, and |
3612 | 59 | highlighting of text. | 60 | highlighting of text. |
3613 | @@ -65,10 +66,10 @@ | |||
3614 | 65 | 66 | ||
3615 | 66 | """ | 67 | """ |
3616 | 67 | 68 | ||
3618 | 68 | gtk.TextView.__init__( self) | 69 | Gtk.TextView.__init__(self) |
3619 | 69 | self.undo_max = None | 70 | self.undo_max = None |
3620 | 70 | self._highlight_strings = [] | 71 | self._highlight_strings = [] |
3622 | 71 | found_tag = gtk.TextTag("highlight") | 72 | found_tag = Gtk.TextTag(name="highlight") |
3623 | 72 | found_tag.set_property("background","yellow") | 73 | found_tag.set_property("background","yellow") |
3624 | 73 | self.get_buffer().get_tag_table().add(found_tag) | 74 | self.get_buffer().get_tag_table().add(found_tag) |
3625 | 74 | 75 | ||
3626 | @@ -77,7 +78,8 @@ | |||
3627 | 77 | self.change_event = self.get_buffer().connect("changed",self._on_text_changed) | 78 | self.change_event = self.get_buffer().connect("changed",self._on_text_changed) |
3628 | 78 | self._auto_bullet = None | 79 | self._auto_bullet = None |
3629 | 79 | self.auto_bullets = False | 80 | self.auto_bullets = False |
3631 | 80 | self.clipboard = gtk.Clipboard() | 81 | display = self.get_display() |
3632 | 82 | self.clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD) | ||
3633 | 81 | 83 | ||
3634 | 82 | self.undos = [] | 84 | self.undos = [] |
3635 | 83 | self.redos = [] | 85 | self.redos = [] |
3636 | @@ -92,7 +94,7 @@ | |||
3637 | 92 | """ | 94 | """ |
3638 | 93 | start_iter = self.get_buffer().get_iter_at_offset(0) | 95 | start_iter = self.get_buffer().get_iter_at_offset(0) |
3639 | 94 | end_iter = self.get_buffer().get_iter_at_offset(-1) | 96 | end_iter = self.get_buffer().get_iter_at_offset(-1) |
3641 | 95 | return self.get_buffer().get_text(start_iter,end_iter) | 97 | return self.get_buffer().get_text(start_iter,end_iter, False) |
3642 | 96 | 98 | ||
3643 | 97 | @text.setter | 99 | @text.setter |
3644 | 98 | def text(self, text): | 100 | def text(self, text): |
3645 | @@ -179,6 +181,7 @@ | |||
3646 | 179 | 181 | ||
3647 | 180 | 182 | ||
3648 | 181 | self._highlight_strings = [] | 183 | self._highlight_strings = [] |
3649 | 184 | |||
3650 | 182 | self._highlight() | 185 | self._highlight() |
3651 | 183 | 186 | ||
3652 | 184 | def _highlight(self): | 187 | def _highlight(self): |
3653 | @@ -189,7 +192,7 @@ | |||
3654 | 189 | 192 | ||
3655 | 190 | start_iter = self.get_buffer().get_iter_at_offset(0) | 193 | start_iter = self.get_buffer().get_iter_at_offset(0) |
3656 | 191 | end_iter = self.get_buffer().get_iter_at_offset(-1) | 194 | end_iter = self.get_buffer().get_iter_at_offset(-1) |
3658 | 192 | text = self.get_buffer().get_text(start_iter,end_iter) | 195 | text = self.get_buffer().get_text(start_iter,end_iter, False) |
3659 | 193 | self.get_buffer().remove_all_tags(start_iter, end_iter) | 196 | self.get_buffer().remove_all_tags(start_iter, end_iter) |
3660 | 194 | for s in self._highlight_strings: | 197 | for s in self._highlight_strings: |
3661 | 195 | hits = [match.start() for match in re.finditer(re.escape(s), text)] | 198 | hits = [match.start() for match in re.finditer(re.escape(s), text)] |
3662 | @@ -213,7 +216,7 @@ | |||
3663 | 213 | handler. | 216 | handler. |
3664 | 214 | 217 | ||
3665 | 215 | """ | 218 | """ |
3667 | 216 | 219 | ||
3668 | 217 | self.get_buffer().copy_clipboard(self.clipboard) | 220 | self.get_buffer().copy_clipboard(self.clipboard) |
3669 | 218 | 221 | ||
3670 | 219 | def paste(self, widget=None, data=None): | 222 | def paste(self, widget=None, data=None): |
3671 | @@ -306,7 +309,7 @@ | |||
3672 | 306 | cur_line = iter.get_line() | 309 | cur_line = iter.get_line() |
3673 | 307 | prev_line_iter = self.get_buffer().get_iter_at_line(cur_line) | 310 | prev_line_iter = self.get_buffer().get_iter_at_line(cur_line) |
3674 | 308 | pl_offset = prev_line_iter.get_offset() | 311 | pl_offset = prev_line_iter.get_offset() |
3676 | 309 | pl_text = self.get_buffer().get_text(prev_line_iter, iter) | 312 | pl_text = self.get_buffer().get_text(prev_line_iter, iter, False) |
3677 | 310 | if pl_text.strip().find("*") == 0: | 313 | if pl_text.strip().find("*") == 0: |
3678 | 311 | ws = "" | 314 | ws = "" |
3679 | 312 | if not pl_text.startswith("*"): | 315 | if not pl_text.startswith("*"): |
3680 | @@ -320,7 +323,7 @@ | |||
3681 | 320 | """ | 323 | """ |
3682 | 321 | 324 | ||
3683 | 322 | self._highlight() | 325 | self._highlight() |
3685 | 323 | text = self.get_buffer().get_text(start_iter,end_iter) | 326 | text = self.get_buffer().get_text(start_iter,end_iter, False) |
3686 | 324 | cmd = {"action":"insert","offset":start_iter.get_offset(),"text":text} | 327 | cmd = {"action":"insert","offset":start_iter.get_offset(),"text":text} |
3687 | 325 | self._add_undo(cmd) | 328 | self._add_undo(cmd) |
3688 | 326 | 329 | ||
3689 | @@ -335,25 +338,25 @@ | |||
3690 | 335 | del(self.undos[0]) | 338 | del(self.undos[0]) |
3691 | 336 | self.undos.append(cmd) | 339 | self.undos.append(cmd) |
3692 | 337 | 340 | ||
3694 | 338 | class TestWindow(gtk.Window): | 341 | class TestWindow(Gtk.Window): |
3695 | 339 | """For testing and demonstrating AsycnTaskProgressBox. | 342 | """For testing and demonstrating AsycnTaskProgressBox. |
3696 | 340 | 343 | ||
3697 | 341 | """ | 344 | """ |
3698 | 342 | def __init__(self): | 345 | def __init__(self): |
3699 | 343 | #create a window a VBox to hold the controls | 346 | #create a window a VBox to hold the controls |
3701 | 344 | gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) | 347 | Gtk.Window.__init__(self) |
3702 | 345 | self.set_title("TextEditor Test Window") | 348 | self.set_title("TextEditor Test Window") |
3704 | 346 | windowbox = gtk.VBox(False, 2) | 349 | windowbox = Gtk.VBox(False, 2) |
3705 | 347 | windowbox.show() | 350 | windowbox.show() |
3706 | 348 | self.add(windowbox) | 351 | self.add(windowbox) |
3707 | 349 | self.editor = TextEditor() | 352 | self.editor = TextEditor() |
3708 | 350 | self.editor.show() | 353 | self.editor.show() |
3710 | 351 | windowbox.pack_end(self.editor) | 354 | windowbox.pack_end(self.editor, True, True, 0) |
3711 | 352 | self.set_size_request(200,200) | 355 | self.set_size_request(200,200) |
3712 | 353 | self.show() | 356 | self.show() |
3713 | 354 | self.maximize() | 357 | self.maximize() |
3714 | 355 | 358 | ||
3716 | 356 | self.connect("destroy", gtk.main_quit) | 359 | self.connect("destroy", Gtk.main_quit) |
3717 | 357 | self.editor.text = "this is some inserted text" | 360 | self.editor.text = "this is some inserted text" |
3718 | 358 | self.editor.append("\nLine 3") | 361 | self.editor.append("\nLine 3") |
3719 | 359 | self.editor.prepend("Line1\n") | 362 | self.editor.prepend("Line1\n") |
3720 | @@ -364,35 +367,35 @@ | |||
3721 | 364 | self.editor.add_highlight("some") | 367 | self.editor.add_highlight("some") |
3722 | 365 | self.editor.undo_max = 100 | 368 | self.editor.undo_max = 100 |
3723 | 366 | self.editor.auto_bullets = True | 369 | self.editor.auto_bullets = True |
3725 | 367 | cut_button = gtk.Button("Cut") | 370 | cut_button = Gtk.Button("Cut") |
3726 | 368 | cut_button.connect("clicked",self.editor.cut) | 371 | cut_button.connect("clicked",self.editor.cut) |
3727 | 369 | cut_button.show() | 372 | cut_button.show() |
3729 | 370 | windowbox.pack_start(cut_button, False) | 373 | windowbox.pack_start(cut_button, False, False, 0) |
3730 | 371 | 374 | ||
3732 | 372 | copy_button = gtk.Button("Copy") | 375 | copy_button = Gtk.Button("Copy") |
3733 | 373 | copy_button.connect("clicked",self.editor.copy) | 376 | copy_button.connect("clicked",self.editor.copy) |
3734 | 374 | copy_button.show() | 377 | copy_button.show() |
3736 | 375 | windowbox.pack_start(copy_button, False) | 378 | windowbox.pack_start(copy_button, False, False, 0) |
3737 | 376 | 379 | ||
3739 | 377 | paste_button = gtk.Button("Paste") | 380 | paste_button = Gtk.Button("Paste") |
3740 | 378 | paste_button.connect("clicked",self.editor.paste) | 381 | paste_button.connect("clicked",self.editor.paste) |
3741 | 379 | paste_button.show() | 382 | paste_button.show() |
3743 | 380 | windowbox.pack_start(paste_button, False) | 383 | windowbox.pack_start(paste_button, False, False, 0) |
3744 | 381 | 384 | ||
3746 | 382 | undo_button = gtk.Button("Undo") | 385 | undo_button = Gtk.Button("Undo") |
3747 | 383 | undo_button.connect("clicked",self.editor.undo) | 386 | undo_button.connect("clicked",self.editor.undo) |
3748 | 384 | undo_button.show() | 387 | undo_button.show() |
3750 | 385 | windowbox.pack_start(undo_button, False) | 388 | windowbox.pack_start(undo_button, False, False, 0) |
3751 | 386 | 389 | ||
3753 | 387 | redo_button = gtk.Button("Redo") | 390 | redo_button = Gtk.Button("Redo") |
3754 | 388 | redo_button.connect("clicked",self.editor.redo) | 391 | redo_button.connect("clicked",self.editor.redo) |
3755 | 389 | redo_button.show() | 392 | redo_button.show() |
3757 | 390 | windowbox.pack_start(redo_button, False) | 393 | windowbox.pack_start(redo_button, False, False, 0) |
3758 | 391 | 394 | ||
3759 | 392 | print self.editor.text | 395 | print self.editor.text |
3760 | 393 | 396 | ||
3761 | 394 | 397 | ||
3762 | 395 | if __name__== "__main__": | 398 | if __name__== "__main__": |
3763 | 396 | test = TestWindow() | 399 | test = TestWindow() |
3765 | 397 | gtk.main() | 400 | Gtk.main() |
3766 | 398 | 401 | ||
3767 | 399 | 402 | ||
3768 | === modified file 'quickly/widgets/url_fetch_progressbox.py' | |||
3769 | --- quickly/widgets/url_fetch_progressbox.py 2011-09-05 06:19:23 +0000 | |||
3770 | +++ quickly/widgets/url_fetch_progressbox.py 2012-03-06 08:52:21 +0000 | |||
3771 | @@ -1,5 +1,6 @@ | |||
3772 | 1 | ### BEGIN LICENSE | 1 | ### BEGIN LICENSE |
3773 | 2 | # Copyright (C) 2010 Stuart Langridge stuart.langridge@canonical.com | 2 | # Copyright (C) 2010 Stuart Langridge stuart.langridge@canonical.com |
3774 | 3 | # Copyright (C) 2012 Rick Spencer rick.spencer@canonical.com | ||
3775 | 3 | #This program is free software: you can redistribute it and/or modify it | 4 | #This program is free software: you can redistribute it and/or modify it |
3776 | 4 | #under the terms of the GNU General Public License version 3, as published | 5 | #under the terms of the GNU General Public License version 3, as published |
3777 | 5 | #by the Free Software Foundation. | 6 | #by the Free Software Foundation. |
3778 | @@ -14,18 +15,18 @@ | |||
3779 | 14 | ### END LICENSE | 15 | ### END LICENSE |
3780 | 15 | 16 | ||
3781 | 16 | try: | 17 | try: |
3785 | 17 | import pygtk | 18 | from gi.repository import GObject |
3786 | 18 | pygtk.require("2.0") | 19 | from gi.repository import Gtk |
3787 | 19 | import gtk, gobject, gio | 20 | from gi.repository import Gio |
3788 | 21 | from gi.repository import GLib | ||
3789 | 20 | import gettext | 22 | import gettext |
3790 | 21 | from gettext import gettext as _ | 23 | from gettext import gettext as _ |
3791 | 22 | gettext.textdomain('quickly-widgets') | 24 | gettext.textdomain('quickly-widgets') |
3792 | 23 | import glib | ||
3793 | 24 | except: | 25 | except: |
3794 | 25 | print "couldn't load dependencies" | 26 | print "couldn't load dependencies" |
3795 | 26 | 27 | ||
3796 | 27 | 28 | ||
3798 | 28 | class UrlFetchProgressBox(gtk.HBox): | 29 | class UrlFetchProgressBox(Gtk.HBox): |
3799 | 29 | """UrlFetchProgressBox: encapsulates a pulsating progressbar, a cancel | 30 | """UrlFetchProgressBox: encapsulates a pulsating progressbar, a cancel |
3800 | 30 | button, and a URL that needs fetching. Use a UrlFetchProgressBox when you | 31 | button, and a URL that needs fetching. Use a UrlFetchProgressBox when you |
3801 | 31 | need to fetch a URL; the box will show while the URL is being fetched | 32 | need to fetch a URL; the box will show while the URL is being fetched |
3802 | @@ -37,10 +38,10 @@ | |||
3803 | 37 | Cancelling fires the "downloaded" signal with a value of None. | 38 | Cancelling fires the "downloaded" signal with a value of None. |
3804 | 38 | """ | 39 | """ |
3805 | 39 | 40 | ||
3810 | 40 | __gsignals__ = {'downloaded' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 41 | __gsignals__ = {'downloaded' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, |
3811 | 41 | (gobject.TYPE_PYOBJECT,)), | 42 | (GObject.TYPE_PYOBJECT,)), |
3812 | 42 | 'download-error' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 43 | 'download-error' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, |
3813 | 43 | (gobject.TYPE_PYOBJECT,))} | 44 | (GObject.TYPE_PYOBJECT,))} |
3814 | 44 | 45 | ||
3815 | 45 | def __init__(self, url, destroy_after_fetching=True, cancelable=True): | 46 | def __init__(self, url, destroy_after_fetching=True, cancelable=True): |
3816 | 46 | """Create an UrlFetchProgressBox | 47 | """Create an UrlFetchProgressBox |
3817 | @@ -51,39 +52,37 @@ | |||
3818 | 51 | is fetched? Defaults to True. | 52 | is fetched? Defaults to True. |
3819 | 52 | cancelable -- whether to show cancel button. Defaults to True. | 53 | cancelable -- whether to show cancel button. Defaults to True. |
3820 | 53 | """ | 54 | """ |
3824 | 54 | gtk.HBox.__init__( self, False, 2) | 55 | Gtk.HBox.__init__( self, False, 2) |
3825 | 55 | self.progressbar = gtk.ProgressBar() | 56 | self.progressbar = Gtk.ProgressBar() |
3826 | 56 | gobject.timeout_add(10, self.__tick) | 57 | GObject.timeout_add(10, self.__tick) |
3827 | 57 | self.running = True | 58 | self.running = True |
3828 | 58 | parts = [x for x in url.split("/") if x] | 59 | parts = [x for x in url.split("/") if x] |
3829 | 59 | self.progressbar.set_text(_("Downloading %s") % parts[-1]) | 60 | self.progressbar.set_text(_("Downloading %s") % parts[-1]) |
3830 | 60 | self.progressbar.show() | 61 | self.progressbar.show() |
3832 | 61 | self.pack_start(self.progressbar, True) | 62 | self.pack_start(self.progressbar, True, True, 0) |
3833 | 62 | self.destroy_after_fetching = destroy_after_fetching | 63 | self.destroy_after_fetching = destroy_after_fetching |
3835 | 63 | self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL) | 64 | self.cancel_button = Gtk.Button(stock=Gtk.STOCK_CANCEL) |
3836 | 64 | if cancelable: | 65 | if cancelable: |
3837 | 65 | self.cancel_button.show() | 66 | self.cancel_button.show() |
3838 | 66 | self.cancel_button.set_sensitive(False) | 67 | self.cancel_button.set_sensitive(False) |
3839 | 67 | self.cancel_button.connect("clicked",self.__cancel) | 68 | self.cancel_button.connect("clicked",self.__cancel) |
3841 | 68 | self.pack_end(self.cancel_button, False) | 69 | self.pack_end(self.cancel_button, False, False, 0) |
3842 | 69 | self.cancel_button.set_sensitive(True) | 70 | self.cancel_button.set_sensitive(True) |
3846 | 70 | self.__canceller = gio.Cancellable() | 71 | self.__canceller = Gio.Cancellable() |
3847 | 71 | self.stream = gio.File(url) | 72 | self.stream = Gio.file_new_for_uri(url) |
3848 | 72 | self.stream.load_contents_async(self.__download_finished, cancellable=self.__canceller) | 73 | self.stream.load_contents_async(self.__canceller, self.__download_finished, None) |
3849 | 73 | 74 | ||
3850 | 75 | |||
3851 | 74 | def __tick(self): | 76 | def __tick(self): |
3852 | 75 | self.progressbar.pulse() | 77 | self.progressbar.pulse() |
3853 | 76 | return self.running | 78 | return self.running |
3854 | 77 | 79 | ||
3856 | 78 | def __download_finished(self, gdaemonfile, result): | 80 | def __download_finished(self, gdaemonfile, result, data=None): |
3857 | 79 | try: | 81 | try: |
3865 | 80 | content = self.stream.load_contents_finish(result)[0] | 82 | #GIO documentation says that the file is [0] in the tuple |
3866 | 81 | except gio.Error, e: | 83 | #but it is realy [1] |
3867 | 82 | if e.code == 19: | 84 | content = self.stream.load_contents_finish(result)[1] |
3868 | 83 | self.emit("downloaded", None) | 85 | except Exception, e: |
3862 | 84 | else: | ||
3863 | 85 | self.emit("download-error",e) | ||
3864 | 86 | except glib.GError, e: | ||
3869 | 87 | self.emit("download-error",e) | 86 | self.emit("download-error",e) |
3870 | 88 | else: | 87 | else: |
3871 | 89 | self.emit("downloaded", content) | 88 | self.emit("downloaded", content) |
3872 | @@ -97,29 +96,34 @@ | |||
3873 | 97 | self.running = False | 96 | self.running = False |
3874 | 98 | if self.destroy_after_fetching: self.destroy() | 97 | if self.destroy_after_fetching: self.destroy() |
3875 | 99 | 98 | ||
3877 | 100 | class TestWindow(gtk.Window): | 99 | class TestWindow(Gtk.Window): |
3878 | 101 | def __init__(self): | 100 | def __init__(self): |
3880 | 102 | gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) | 101 | Gtk.Window.__init__(self) |
3881 | 103 | self.set_title("UrlFetchProgressBox test") | 102 | self.set_title("UrlFetchProgressBox test") |
3884 | 104 | self.vbox = gtk.VBox() | 103 | self.vbox = Gtk.VBox() |
3885 | 105 | btn = gtk.Button(stock=gtk.STOCK_EXECUTE) | 104 | btn = Gtk.Button(stock=Gtk.STOCK_EXECUTE) |
3886 | 106 | btn.connect("clicked", self.start_download) | 105 | btn.connect("clicked", self.start_download) |
3888 | 107 | self.vbox.pack_end(btn) | 106 | self.vbox.pack_end(btn, True, True, 0) |
3889 | 108 | self.add(self.vbox) | 107 | self.add(self.vbox) |
3890 | 109 | self.set_size_request(300,200) | 108 | self.set_size_request(300,200) |
3892 | 110 | self.connect("destroy", gtk.main_quit) | 109 | self.connect("destroy", Gtk.main_quit) |
3893 | 111 | 110 | ||
3894 | 112 | def start_download(self, btn): | 111 | def start_download(self, btn): |
3895 | 113 | prog = UrlFetchProgressBox("http://www.ubuntu.com/desktop/get-ubuntu/download") | 112 | prog = UrlFetchProgressBox("http://www.ubuntu.com/desktop/get-ubuntu/download") |
3896 | 113 | |||
3897 | 114 | prog.connect("downloaded", self.downloaded) | 114 | prog.connect("downloaded", self.downloaded) |
3899 | 115 | self.vbox.pack_start(prog, expand=False) | 115 | prog.connect("download-error", self.errored) |
3900 | 116 | self.vbox.pack_start(prog, False, False, 0) | ||
3901 | 116 | prog.show() | 117 | prog.show() |
3903 | 117 | 118 | ||
3904 | 119 | def errored(self, widget, e): | ||
3905 | 120 | print "encountered error: %s " % e.message | ||
3906 | 121 | |||
3907 | 118 | def downloaded(self, widget, content): | 122 | def downloaded(self, widget, content): |
3908 | 119 | print "downloaded %s bytes of content" % len(content) | 123 | print "downloaded %s bytes of content" % len(content) |
3909 | 120 | 124 | ||
3910 | 121 | if __name__ == "__main__": | 125 | if __name__ == "__main__": |
3911 | 122 | w = TestWindow() | 126 | w = TestWindow() |
3912 | 123 | w.show_all() | 127 | w.show_all() |
3914 | 124 | gtk.main() | 128 | Gtk.main() |
3915 | 125 | 129 | ||
3916 | 126 | 130 | ||
3917 | === modified file 'quickly/widgets/web_cam_box.py' | |||
3918 | --- quickly/widgets/web_cam_box.py 2011-01-04 03:19:55 +0000 | |||
3919 | +++ quickly/widgets/web_cam_box.py 2012-03-06 08:52:21 +0000 | |||
3920 | @@ -53,13 +53,13 @@ | |||
3921 | 53 | cam.pack_start(my_widget, False, False) | 53 | cam.pack_start(my_widget, False, False) |
3922 | 54 | 54 | ||
3923 | 55 | Extending | 55 | Extending |
3926 | 56 | A WebCamBox is gtk.VBox | 56 | A WebCamBox is Gtk.VBox |
3927 | 57 | A WebCamBox is a gtk.VBox that contains a gtk.DrawingArea for displaying | 57 | A WebCamBox is a Gtk.VBox that contains a Gtk.DrawingArea for displaying |
3928 | 58 | webcam output, and a thin wrapper around a camerabin, which is a gstreamer | 58 | webcam output, and a thin wrapper around a camerabin, which is a gstreamer |
3929 | 59 | pipleine sublcass that provides all the camera functionality. | 59 | pipleine sublcass that provides all the camera functionality. |
3930 | 60 | 60 | ||
3931 | 61 | To add GUI elements simple, create them and pack them into WebCamBox, since | 61 | To add GUI elements simple, create them and pack them into WebCamBox, since |
3933 | 62 | it's just a gtk.VBox | 62 | it's just a Gtk.VBox |
3934 | 63 | 63 | ||
3935 | 64 | Similarly, to add to or change the web cam functionality, modify properties on | 64 | Similarly, to add to or change the web cam functionality, modify properties on |
3936 | 65 | the camerabin. You may also want to overide _on_message and/or _on_sync_message | 65 | the camerabin. You may also want to overide _on_message and/or _on_sync_message |
3937 | @@ -67,18 +67,20 @@ | |||
3938 | 67 | 67 | ||
3939 | 68 | """ | 68 | """ |
3940 | 69 | 69 | ||
3941 | 70 | from gi.repository import Gtk | ||
3942 | 71 | from gi.repository import GdkX11 | ||
3943 | 72 | from gi.repository import GObject | ||
3944 | 73 | |||
3945 | 70 | import sys | 74 | import sys |
3946 | 71 | import os | 75 | import os |
3947 | 72 | import gtk | ||
3948 | 73 | import gst | 76 | import gst |
3949 | 74 | import datetime | 77 | import datetime |
3950 | 75 | import gobject | ||
3951 | 76 | 78 | ||
3952 | 77 | import gettext | 79 | import gettext |
3953 | 78 | from gettext import gettext as _ | 80 | from gettext import gettext as _ |
3954 | 79 | gettext.textdomain('quickly-widgets') | 81 | gettext.textdomain('quickly-widgets') |
3955 | 80 | 82 | ||
3957 | 81 | class WebCamBox(gtk.VBox): | 83 | class WebCamBox(Gtk.VBox): |
3958 | 82 | """WebCamBox - A VBox that tries to turn on and display the default webcam for the | 84 | """WebCamBox - A VBox that tries to turn on and display the default webcam for the |
3959 | 83 | computer on which it is running. It is also capable of saving an image | 85 | computer on which it is running. It is also capable of saving an image |
3960 | 84 | from the webcam to the user's Picture directory. | 86 | from the webcam to the user's Picture directory. |
3961 | @@ -92,10 +94,10 @@ | |||
3962 | 92 | This function has no arguments | 94 | This function has no arguments |
3963 | 93 | 95 | ||
3964 | 94 | """ | 96 | """ |
3967 | 95 | gtk.VBox.__init__(self, False, 5) | 97 | Gtk.VBox.__init__(self, False, 5) |
3968 | 96 | self.video_window = gtk.DrawingArea() | 98 | self.video_window = Gtk.DrawingArea() |
3969 | 97 | self.video_window.connect("realize",self.__on_video_window_realized) | 99 | self.video_window.connect("realize",self.__on_video_window_realized) |
3971 | 98 | self.pack_start(self.video_window, True, True) | 100 | self.pack_start(self.video_window, True, True, 0) |
3972 | 99 | self.video_window.show() | 101 | self.video_window.show() |
3973 | 100 | self.connect("destroy", self.on_destroy) | 102 | self.connect("destroy", self.on_destroy) |
3974 | 101 | 103 | ||
3975 | @@ -105,7 +107,7 @@ | |||
3976 | 105 | bus.enable_sync_message_emission() | 107 | bus.enable_sync_message_emission() |
3977 | 106 | bus.connect("message", self._on_message) | 108 | bus.connect("message", self._on_message) |
3978 | 107 | bus.connect("sync-message::element", self._on_sync_message) | 109 | bus.connect("sync-message::element", self._on_sync_message) |
3980 | 108 | self.camerabin.set_property("image-encoder",gst.element_factory_make("pngenc", "png_encoder")) | 110 | #self.camerabin.set_property("image-encoder",gst.element_factory_make("pngenc", "png_encoder")) |
3981 | 109 | self.filename_prefix = "" | 111 | self.filename_prefix = "" |
3982 | 110 | self.realized = False | 112 | self.realized = False |
3983 | 111 | 113 | ||
3984 | @@ -155,7 +157,7 @@ | |||
3985 | 155 | """ | 157 | """ |
3986 | 156 | 158 | ||
3987 | 157 | stamp = str(datetime.datetime.now()) | 159 | stamp = str(datetime.datetime.now()) |
3989 | 158 | extension = ".png" | 160 | extension = ".jpg" |
3990 | 159 | directory = os.environ["HOME"] + _("/Pictures/") | 161 | directory = os.environ["HOME"] + _("/Pictures/") |
3991 | 160 | self.filename = directory + self.filename_prefix + stamp + extension | 162 | self.filename = directory + self.filename_prefix + stamp + extension |
3992 | 161 | self.camerabin.set_property("filename", self.filename) | 163 | self.camerabin.set_property("filename", self.filename) |
3993 | @@ -217,7 +219,7 @@ | |||
3994 | 217 | if message_name == "prepare-xwindow-id": | 219 | if message_name == "prepare-xwindow-id": |
3995 | 218 | imagesink = message.src | 220 | imagesink = message.src |
3996 | 219 | imagesink.set_property("force-aspect-ratio", True) | 221 | imagesink.set_property("force-aspect-ratio", True) |
3998 | 220 | imagesink.set_xwindow_id(self.video_window.window.xid) | 222 | imagesink.set_xwindow_id(self.video_window.get_window().get_xid()) |
3999 | 221 | 223 | ||
4000 | 222 | def __on_video_window_realized(self, widget, data=None): | 224 | def __on_video_window_realized(self, widget, data=None): |
4001 | 223 | """__on_video_window_realized - internal signal handler, used | 225 | """__on_video_window_realized - internal signal handler, used |
4002 | @@ -229,16 +231,16 @@ | |||
4003 | 229 | self._set_video_window_id() | 231 | self._set_video_window_id() |
4004 | 230 | 232 | ||
4005 | 231 | def _set_video_window_id(self): | 233 | def _set_video_window_id(self): |
4008 | 232 | if not self.realized and self.video_window.window is not None: | 234 | if not self.realized and self.video_window.get_window() is not None: |
4009 | 233 | x = self.video_window.window.xid | 235 | x = self.video_window.get_window().get_xid() |
4010 | 234 | self.realized = True | 236 | self.realized = True |
4011 | 235 | 237 | ||
4012 | 236 | def on_destroy(self, widget, data=None): | 238 | def on_destroy(self, widget, data=None): |
4013 | 237 | #clean up the camera before exiting | 239 | #clean up the camera before exiting |
4014 | 238 | self.camerabin.set_state(gst.STATE_NULL) | 240 | self.camerabin.set_state(gst.STATE_NULL) |
4015 | 239 | 241 | ||
4018 | 240 | __gsignals__ = {'image-captured' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, | 242 | __gsignals__ = {'image-captured' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, |
4019 | 241 | (gobject.TYPE_PYOBJECT,)), | 243 | (GObject.TYPE_PYOBJECT,)), |
4020 | 242 | } | 244 | } |
4021 | 243 | 245 | ||
4022 | 244 | def __image_captured(widget, data=None): | 246 | def __image_captured(widget, data=None): |
4023 | @@ -247,20 +249,21 @@ | |||
4024 | 247 | 249 | ||
4025 | 248 | """ | 250 | """ |
4026 | 249 | 251 | ||
4028 | 250 | quickly.prompts.info("WebCam Test",data) | 252 | #quickly.prompts.info("WebCam Test",data) |
4029 | 253 | print data | ||
4030 | 251 | 254 | ||
4031 | 252 | if __name__ == "__main__": | 255 | if __name__ == "__main__": |
4032 | 253 | """creates a test WebCamBox""" | 256 | """creates a test WebCamBox""" |
4034 | 254 | import quickly.prompts | 257 | #import quickly.prompts |
4035 | 255 | 258 | ||
4036 | 256 | #create and show a test window | 259 | #create and show a test window |
4038 | 257 | win = gtk.Window(gtk.WINDOW_TOPLEVEL) | 260 | win = Gtk.Window() |
4039 | 258 | win.set_title("WebCam Test Window") | 261 | win.set_title("WebCam Test Window") |
4041 | 259 | win.connect("destroy",gtk.main_quit) | 262 | win.connect("destroy",Gtk.main_quit) |
4042 | 260 | win.show() | 263 | win.show() |
4043 | 261 | 264 | ||
4044 | 262 | #create a top level container | 265 | #create a top level container |
4046 | 263 | vbox = gtk.VBox(False, 10) | 266 | vbox = Gtk.VBox(False, 10) |
4047 | 264 | vbox.show() | 267 | vbox.show() |
4048 | 265 | win.add(vbox) | 268 | win.add(vbox) |
4049 | 266 | 269 | ||
4050 | @@ -271,27 +274,27 @@ | |||
4051 | 271 | mb.play() | 274 | mb.play() |
4052 | 272 | 275 | ||
4053 | 273 | mb.connect("image-captured", __image_captured) | 276 | mb.connect("image-captured", __image_captured) |
4058 | 274 | play_butt = gtk.Button("Play") | 277 | play_butt = Gtk.Button("Play") |
4059 | 275 | pause_butt = gtk.Button("Pause") | 278 | pause_butt = Gtk.Button("Pause") |
4060 | 276 | stop_butt = gtk.Button("Stop") | 279 | stop_butt = Gtk.Button("Stop") |
4061 | 277 | pic_butt = gtk.Button("Picture") | 280 | pic_butt = Gtk.Button("Picture") |
4062 | 278 | 281 | ||
4063 | 279 | play_butt.connect("clicked", lambda x:mb.play()) | 282 | play_butt.connect("clicked", lambda x:mb.play()) |
4064 | 280 | play_butt.show() | 283 | play_butt.show() |
4066 | 281 | mb.pack_end(play_butt, False) | 284 | mb.pack_end(play_butt, False, False, 0) |
4067 | 282 | 285 | ||
4068 | 283 | pause_butt.connect("clicked", lambda x:mb.pause()) | 286 | pause_butt.connect("clicked", lambda x:mb.pause()) |
4069 | 284 | pause_butt.show() | 287 | pause_butt.show() |
4071 | 285 | mb.pack_end(pause_butt, False) | 288 | mb.pack_end(pause_butt, False, False, 0) |
4072 | 286 | 289 | ||
4073 | 287 | stop_butt.connect("clicked", lambda x:mb.stop()) | 290 | stop_butt.connect("clicked", lambda x:mb.stop()) |
4074 | 288 | stop_butt.show() | 291 | stop_butt.show() |
4076 | 289 | mb.pack_end(stop_butt, False) | 292 | mb.pack_end(stop_butt, False, False, 0) |
4077 | 290 | 293 | ||
4078 | 291 | pic_butt.connect("clicked", lambda x:mb.take_picture()) | 294 | pic_butt.connect("clicked", lambda x:mb.take_picture()) |
4079 | 292 | pic_butt.show() | 295 | pic_butt.show() |
4081 | 293 | mb.pack_end(pic_butt, False) | 296 | mb.pack_end(pic_butt, False, False, 0) |
4082 | 294 | 297 | ||
4084 | 295 | gtk.main() | 298 | Gtk.main() |
4085 | 296 | 299 | ||
4086 | 297 | 300 |
Seems fine, but running the tests on the new code gives me several errors like:
Gtk-WARNING **: Cannot connect attribute `text' for cell renderer class `GtkCellRendere rToggle' since attribute does not exist
Is that expected? Aside from that, this is approved. So if you fix that or it's innocuous, push away.