Merge lp:~rick-rickspencer3/quidgets/precise into lp:quidgets

Proposed by Rick Spencer on 2012-03-06
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
Reviewer Review Type Date Requested Status
Michael Terry (community) 2012-03-06 Approve on 2012-03-13
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.
Michael Terry (mterry) wrote :

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 `GtkCellRendererToggle' 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.

review: Needs Fixing
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
=== removed file 'quickly/widgets/asynch_task_progressbox.py'
--- quickly/widgets/asynch_task_progressbox.py 2010-09-11 19:59:30 +0000
+++ quickly/widgets/asynch_task_progressbox.py 1970-01-01 00:00:00 +0000
@@ -1,310 +0,0 @@
1### BEGIN LICENSE
2# Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com
3#This program is free software: you can redistribute it and/or modify it
4#under the terms of the GNU General Public License version 3, as published
5#by the Free Software Foundation.
6#
7#This program is distributed in the hope that it will be useful, but
8#WITHOUT ANY WARRANTY; without even the implied warranties of
9#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
10#PURPOSE. See the GNU General Public License for more details.
11#
12#You should have received a copy of the GNU General Public License along
13#with this program. If not, see <http://www.gnu.org/licenses/>.
14### END LICENSE
15
16try:
17 import pygtk
18 pygtk.require("2.0")
19 import gtk
20 import threading
21 import time
22 import gobject
23 import gettext
24 from gettext import gettext as _
25 gettext.textdomain('quickly-widgets')
26
27except:
28 print "couldn't load depencies"
29
30class AsynchTaskProgressBox( gtk.HBox ):
31 """AsynchTaskProgressBox: encapsulates a pulstating progressbar, a cancel
32 button, and a long running task. Use an AsynchTaskProgressBox when you want
33 a window to perform a long running task in the background without freezing
34 the UI for the user.
35
36 """
37
38 def __init__(self, run_function, params = None, cancelable = True):
39 """Create an AsycnTaskProgressBox
40
41 Keyword arguments:
42 run_function -- the function to run asynchronously
43 params -- optional dictionary of parameters to be pass into run_function
44 cancelable -- optional value to determine whether to show cancel button. Defaults to True.
45 Do not use a value with the key of 'kill' in the params dictionary
46
47 """
48
49 gtk.HBox.__init__( self, False, 2 )
50
51 self.progressbar = gtk.ProgressBar()
52 self.progressbar.show()
53 self.pack_start(self.progressbar, True)
54
55 self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
56 if cancelable:
57 self.cancel_button.show()
58 if params is None:
59 params = {}
60 params["update_progress_function"] = self.update
61 self.cancel_button.set_sensitive(False)
62 self.cancel_button.connect("clicked",self.__stop_clicked)
63 self.pack_end(self.cancel_button, False)
64
65 self.run_function = run_function
66 self.pulse_thread = None
67 self.work_thread = None
68 self.params = params
69
70 self.connect("destroy", self.__destroy)
71
72 __gsignals__ = {'complete' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
73 (gobject.TYPE_PYOBJECT,)),
74 'cancelrequested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
75 (gobject.TYPE_PYOBJECT,))
76 }
77
78 def start(self, caption = "Working"):
79 """executes run_function asynchronously and starts pulsating the progressbar
80 Keyword arguments:
81 caption -- optional text to display in the progressbar
82 """
83 #Throw an exception if the user tries to start an operating thread
84 if self.pulse_thread != None:
85 raise RuntimeError("AsynchTaskProgressBox already started.")
86
87 #Create and start a thread to run the users task
88 #pass in a callback and the user's params
89 self.work_thread = KillableThread(self.run_function, self.__on_complete, self.params)
90 self.work_thread.start()
91
92 #create a thread to display the user feedback
93 self.pulse_thread = PulseThread(self.progressbar, caption)
94 self.pulse_thread.start()
95
96 #enable the button so the user can try to kill the task
97 self.cancel_button.set_sensitive( True )
98
99 def update(self, fraction = None, displaytext = "Working"):
100 """updates the progress bar with a given percentage and/or changes the
101 caption.
102 Keyword arguments:
103 fraction -- the current percentage complete
104 displaytext -- the new caption to display"""
105 if self.pulse_thread != None:
106 self.pulse_thread.update(fraction, displaytext)
107
108 #call back function for after run_function returns
109 def __on_complete( self, data ):
110 self.emit("complete", data)
111 self.kill()
112
113 #call back function for cancel button
114 def __stop_clicked( self, widget, data = None ):
115 self.cancel()
116
117 def cancel(self):
118 self.kill()
119 #emit the cancelrequested event
120 #note that this only signals that the kill function was called
121 #the thread may or may not have actually stopped
122 self.emit("cancelrequested", self)
123
124 def kill( self ):
125 """
126 Stops pulstating the progressbar if the progressbar is working.
127 Sets the value of 'kill' to True in the run_function.
128
129 """
130
131 #stop the pulse_thread and remove a reference to it if there is one
132 if self.pulse_thread != None:
133 self.pulse_thread.kill()
134 self.pulse_thread = None
135
136 #disable the cancel button since the task is about to be told to stop
137 gobject.idle_add(self.__disable_cancel_button)
138
139 #tell the users function tostop if it's thread exists
140 if self.work_thread != None:
141 self.work_thread.kill()
142
143 #called when the widget is destroyed, attempts to clean up
144 #the work thread and the pulse thread
145 def __destroy(self, widget, data = None):
146 if self.work_thread != None:
147 self.work_thread.kill()
148 if self.pulse_thread != None:
149 self.pulse_thread.kill()
150
151 def __disable_cancel_button(self):
152 gtk.gdk.threads_enter()
153 self.cancel_button.set_sensitive( False )
154 gtk.gdk.threads_leave()
155
156class PulseThread ( threading.Thread ):
157 """Class for use by AsynchTaskProgressBox. Not for general use.
158
159 """
160 def __init__(self,progressbar,displaytext = _("Working")):
161 threading.Thread.__init__(self)
162 self.displaytext = displaytext
163 self.setDaemon(False)
164 self.progressbar = progressbar
165 self.__kill = False
166 self.fraction = float(0)
167
168 def kill(self):
169 self.__kill = True
170
171 def update(self, fraction, displaytext):
172 self.displaytext = displaytext
173 self.fraction = fraction
174
175 #As a subclass of Thread, this function runs when start() is called
176 #It will cause the progressbar to pulse, showing that a task is running
177 def run ( self ):
178 self.progressbar.set_text(self.displaytext)
179 #keep running until the __kill flag is set to True
180 while not self.__kill:
181 time.sleep(.1)
182 #get ahold of the UI thread and command the progress bar to pulse
183 gtk.gdk.threads_enter()
184 if self.fraction == 0:
185 self.progressbar.pulse()
186 else:
187 self.progressbar.set_fraction(self.fraction)
188 self.progressbar.set_text(self.displaytext)
189 gtk.gdk.threads_leave()
190 #before exiting, reset the progressbar to show that no task is running
191 gtk.gdk.threads_enter()
192 self.progressbar.set_fraction(0)
193 self.progressbar.set_text("")
194 gtk.gdk.threads_leave()
195
196class KillableThread( threading.Thread ):
197 """Class for use by AsynchTaskProgressBox. Not for general use.
198
199 """
200 def __init__(self,run_function, on_complete, params = None):
201 threading.Thread.__init__(self)
202 self.setDaemon(False)
203 self.run_function = run_function
204 self.params = params
205 self.on_complete = on_complete
206
207 #As a subclass of Thread, this function runs when start() is called
208 #It will run the user's function on this thread
209 def run( self ):
210 #set up params and include the kill flag
211 if self.params == None:
212 self.params = {}
213 self.params["kill"] = False
214 #tell the function to run
215 data = self.run_function(self.params)
216 #return any data from the function so it can be sent in the complete signal
217 self.on_complete(data)
218
219 #Tell the user's function that it should stop
220 #Note the user's function may not check this
221 def kill( self ):
222 self.params["kill"] = True
223
224class TestWindow(gtk.Window):
225 """For testing and demonstrating AsycnTaskProgressBox.
226
227 """
228 def __init__(self):
229 #create a window a VBox to hold the controls
230 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
231 self.set_title("AsynchTaskProgressBox Test Window")
232 windowbox = gtk.VBox(False, 2)
233 windowbox.show()
234 self.add(windowbox)
235
236 #create params for use by the function that should run asynchronously
237 params = {"start": 100, "stop": 110}
238
239 #pass in the function and the params, then add to the window
240 self.thread_progressbox = AsynchTaskProgressBox(self.asynch_function, params)
241 self.thread_progressbox.show()
242 windowbox.add(self.thread_progressbox)
243
244 #start the task, and set the text for the progressbar to "Testing"
245 #This will start the function and start the progressbar pulsating
246 self.thread_progressbox.start("Testing")
247
248 #connect to the complete event to respond when the task is complete
249 self.thread_progressbox.connect("complete",self.complete_function)
250
251 #connect to the cancel requested event for demonstration purposes
252 self.thread_progressbox.connect("cancelrequested", self.canceled_function)
253
254 #create a button for starting the task and add it to the window
255 start_button = gtk.Button(stock=gtk.STOCK_EXECUTE)
256 start_button.show()
257 windowbox.add(start_button)
258 start_button.connect("clicked",self.start_clicked)
259 self.show()
260
261 #finish wiring up the window
262 self.connect("destroy", self.destroy)
263
264 #start up gtk.main in a threaded environment
265 gtk.gdk.threads_init()
266 gtk.gdk.threads_enter()
267 gtk.main()
268 gtk.gdk.threads_leave()
269
270 #called when the window is destroyed
271 def destroy(self, widget, data = None):
272 gtk.main_quit()
273
274 #start the AsynchTaskProgressBox when the button is clicked
275 def start_clicked(self, widget, data = None):
276 self.thread_progressbox.start("Testing")
277
278 #The function to run asynchronously
279 def asynch_function( self, params ):
280 # do something interminate and cpu intensive at startup
281 print "starting..."
282 time.sleep(2)
283 #pull values from the params that were set above
284 total = float(abs(params["stop"]-params["start"]))
285 for x in range(params["start"],params["stop"]):
286 #check if to see if the user has told the task to stop
287 if params["kill"] == True:
288 #return a string if the user stopped the task
289 return "stopped at " + str(x)
290 else:
291 #if the user did not try to stop the task, go ahead and do something
292 print x
293 fraction=abs(x-params["start"])/total
294 # call the update_progress_function function with the current percentage and caption
295 params["update_progress_function"](fraction=fraction, displaytext=str(x))
296 #this is a processor intensive task, so
297 #sleep the loop to keep the UI from bogging down
298 time.sleep(.5)
299 #if the loop completes, return a string
300 return "counted all"
301
302 #called when the task completes
303 def complete_function(self, widget, data = None):
304 print "returned " + str(data)
305
306 def canceled_function(self, widget, data=None):
307 print "cancel requested"
308if __name__== "__main__":
309 test = TestWindow()
310
3110
=== modified file 'quickly/widgets/conventions.py'
--- quickly/widgets/conventions.py 2010-11-21 19:20:40 +0000
+++ quickly/widgets/conventions.py 2012-03-06 08:52:21 +0000
@@ -13,8 +13,6 @@
13#with this program. If not, see <http://www.gnu.org/licenses/>.13#with this program. If not, see <http://www.gnu.org/licenses/>.
14### END LICENSE14### END LICENSE
1515
16import gtk
17import gobject
18from grid_column import StringColumn, CurrencyColumn, CheckColumn16from grid_column import StringColumn, CurrencyColumn, CheckColumn
19from grid_column import IntegerColumn, TagsColumn, DateColumn17from grid_column import IntegerColumn, TagsColumn, DateColumn
2018
2119
=== removed file 'quickly/widgets/couch_grid.py'
--- quickly/widgets/couch_grid.py 2011-08-19 17:22:14 +0000
+++ quickly/widgets/couch_grid.py 1970-01-01 00:00:00 +0000
@@ -1,458 +0,0 @@
1# -*- coding: utf-8 -*-
2### BEGIN LICENSE
3# Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com
4#This program is free software: you can redistribute it and/or modify it
5#under the terms of the GNU General Public License version 3, as published
6#by the Free Software Foundation.
7#
8#This program is distributed in the hope that it will be useful, but
9#WITHOUT ANY WARRANTY; without even the implied warranties of
10#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11#PURPOSE. See the GNU General Public License for more details.
12#
13#You should have received a copy of the GNU General Public License along
14#with this program. If not, see <http://www.gnu.org/licenses/>.
15### END LICENSE
16
17"""A Treeview for Desktop CouchDB
18Displays and persists data in desktopcouch, handles presentation
19of the UI in a Gtk.TreeView, as well handles persistence to a backend
20desktopcouch database.
21
22Using
23#define the database and record type
24database = "couch_widget_test"
25record_type="test_record_type"
26
27#create a dictionary if you don't already have one
28dicts = [{"test?":True,"price":100,"foo count":100,"Key4":"1004"},
29 {"test?":True,"price":100,"foo count":100,"Key4":"1004"},
30 {"test?":True,"price":100,"foo count":100,"Key4":"1004"}]
31
32#create the CouchGrid
33cg = CouchGrid(database, record_type=record_type, dictionaries=dicts)
34
35Configuring
36#Define columns to display
37keys=["price","test?"]
38cg = CouchGrid(database, record_type=record_type, dictionaries=dicts,keys=keys)
39
40#Define column types to use
41hints = {"price": StringColumn}
42cg = CouchGrid(database, record_type=record_type, dictionaries=dicts,keys=keys, type_hints = hints)
43
44#A CouchGrid is a Dictionary Grid whcih is a TreeView,
45#so you can use DicationaryGrid and TreeView members
46cg.editable = True
47cg.get_column(0).set_title("Price")
48
49Extending
50To change the way CouchGrid persists data, change _refresh_treeview to
51handle persistence of data if needed, calling DictionaryGrid._populate_treeivew
52to handle the UI. You should do the same with append_row.
53
54You may also want to override _edited and _edited_toggled to handle persistence
55when the UI is changed.
56
57It is only useful to extend CouchGrid if you are using desktopcouch for
58persistence. Otherwise, derive from DictionaryGrid.
59
60"""
61
62import gtk
63import gobject
64from desktopcouch.records.server import CouchDatabase
65from desktopcouch.records.record import Record
66from quickly.widgets.dictionary_grid import DictionaryGrid
67from quickly.widgets.grid_column import CheckColumn
68
69class CouchGrid(DictionaryGrid):
70 def __init__(
71 self, database_name, record_type=None, dictionaries=None, editable=False, keys=None, type_hints=None, uri=None):
72 """Create a new Couchwidget
73 arguments:
74 database_name - specify the name of the database in the desktop
75 couchdb to use. If the specified database does not exist, it
76 will be created.
77
78 optional arguments:
79 record_type - a string to specify the record_type to use in
80 retrieving and creating records. Note that if no records exist
81 in the CouchDB then the keys argument must also be used or
82 a RuntimeError will result.
83
84 dictionaries - a list of dictionaries to initialize in the
85 grid. If these haven't been added to desktopcouch, the will
86 be automatically persisted and updated using the recored_type
87 specified. Any previously saved data of the same record_type will
88 also be displayed.
89
90 keys - a list of strings specifying keys to use in
91 the columns of the CouchGrid. The keys will also be used for the
92 column titles and keys in desktop couch.
93
94 If a record does not contain a value for a specified key
95 the CouchGrid will simply display an empty cell of the
96 appropriate type. If the widget is set to editable,
97 the user will be able to add values to the database.
98
99 The types for the columns will be inferred by the key based on
100 some conventions. the key "id" is assumed to be an integer, as
101 is any key ending in " count". A key ending in "?" is assumed
102 to be a Boolean displayed with a checkbox. The key "price" is
103 assumed to be currency, as is any key ending in "count". There
104 may be others. Defaults can be overridden using type-hints. All
105 other keys will be assumed to be strings.
106
107 type-hints - a dictionary containing keys specificed for the
108 TreeView and GridColumns. Used to override types inferred
109 by convention, or for changing the type of a column from
110 the default of a string to something else.
111
112 uri - A uri for the DesktopCouch. This is only used to
113 choose a Couch database running remotely. The default is
114 to use the local desktopcouch database.
115
116 """
117
118 if type(database_name) is not type(str()):
119 raise TypeError("database_name is required and must be a string")
120
121 #set up the database before trying to use it
122 self.uri = uri
123 self._record_type = None
124 self._db = None
125 if record_type is not None:
126 self._record_type = record_type
127
128 #QUESTION: What is the purpose of this?
129 #if dictionaries is not None and keys is None:
130 # DictionaryGrid.__init__(self, None, editable, keys, type_hints)
131 #else:
132 # DictionaryGrid.__init__(self, None, editable, keys, type_hints)
133
134 # Have to leave it in for now. But this is certainly a bug.
135 # Note: adding dicts to a CG adds to empty cols in the model between the key values and the couch dict.
136 if dictionaries is not None and keys is None:
137 DictionaryGrid.__init__(self, None, editable, keys, type_hints)
138 else:
139 DictionaryGrid.__init__(self, None, editable, keys, type_hints)
140
141
142 """
143 if dictionaries is not None and keys is not None:
144 DictionaryGrid.__init__(self, dictionaries, editable, keys, type_hints)
145 elif keys is None:
146 DictionaryGrid.__init__(self, dictionaries, editable, None, type_hints)
147 elif dictionaries is None:
148 DictionaryGrid.__init__(self, None, editable, keys, type_hints)
149 """
150
151
152 if self.uri:
153 self._db = CouchDatabase(database_name, create=True, uri=self.uri)
154 else:
155 self._db = CouchDatabase(database_name, create=True)
156
157 if dictionaries is not None:
158 for d in dictionaries:
159 self._persist_dict_to_couch(d)
160
161 self._refresh_treeview()
162
163 @property
164 def database(self):
165 """database - gets an instance to the CouchDB.
166 Set to a string to change the database.
167
168 """
169 return self._db
170
171 @database.setter
172 def database(self, db_name):
173 if self.uri:
174 self._db = CouchDatabase(db_name, create=True, uri=self.uri)
175 else:
176 self._db = CouchDatabase(db_name, create=True)
177 if self.record_type != None:
178 self._refresh_treeview()#first time treeview is reset
179
180 @property
181 def record_type(self):
182 """record_type - a string specifying the record type of
183 the documents to retrieve from the CouchDB.
184
185 Will cause the TreeView to refresh when set.
186 """
187 return self._record_type
188
189 @record_type.setter
190 def record_type(self, record_type):
191
192 #store the record type string
193 self._record_type = record_type
194 self._refresh_treeview()
195
196 @property
197 def selected_record_ids(self):
198 """ selected_record_ids - a list of document ids that are
199 selected in the CouchGrid. Throws an IndexError if
200 a specified id is not found in the list when setting
201 this property.
202
203 This property is read/write
204
205 """
206 ids = []
207 for row in self.selected_rows:
208 id_ = None
209
210 if "__desktopcouch_id" in row:
211 id_ = row["__desktopcouch_id"]
212 ids.append(id_)
213 return ids
214
215 @selected_record_ids.setter
216 def selected_record_ids(self, indexes):
217 rows = [] #a list of rows to select
218 for id in indexes:
219 id_found = False #track if the id was found
220
221 for i,r in enumerate(self.list_store):
222 dictionary = r[len(self.keys)] #this dictionary always last column
223 if "__desktopcouch_id" in dictionary:
224 if dictionary["__desktopcouch_id"] == id:
225 id_found = True #id was good
226 if r not in rows: #don't have duplicates to select
227 rows.append(i)
228 if not id_found: #stop if a requested id was not in the list
229 raise IndexError("id %s not found" %id)
230
231 #select the requested ids
232 selection = self.get_selection()
233 selection.unselect_all()
234 for r in rows:
235 selection.select_path(r)
236
237 def remove_selected_rows(self, delete=False):
238 rows_to_delete = self.selected_rows
239 if delete:
240 for r in rows_to_delete:
241 self.database.delete_record(r["__desktopcouch_id"])
242 DictionaryGrid.remove_selected_rows(self)
243
244 def _refresh_treeview(self):
245 """
246 _refresh_treeview: internal function to handle rebuilding
247 the gtk.TreeView along with columns and cell renderers. extends
248 DictionaryGrid._refresh_treeview by retrieving stored desktopcouch
249 records before calling DictionaryGrid._refresh_treeview.
250
251 _refresh_treeview is not typically called directly,
252 but may be useful to override in subclasses.
253
254 """
255
256 #if the database is not set up, just return
257 if self._db is None or self._record_type is None:
258 return
259
260 #if keys aren't set, infer them from the collection
261 if len(self._dictionaries) > 0 and self.keys is None:
262 self._infer_keys_from_dictionaries()
263
264 #retrieve the docs for the record_type, if any
265 results = self._db.get_records(
266 record_type=self._record_type,create_view=True)
267
268
269 #if there are no rows and no keys set, there is no
270 #way to build the grid, just raise an error
271 if len(results) == 0 and self._keys is None:
272 raise RuntimeError("Cannot infer columns for CouchGrid")
273
274 dicts = []
275 for r in results:
276 d = r.value
277
278 #hmmm, maybe make these so they get hidden rather than delete them
279 #hide the desktopcouch variabls
280 for key in d:
281 if key.startswith("_") and not key.startswith("__desktopcouch"):
282 d["__desktopcouch" + key] = d[key]
283 del(d[key])
284
285 d["__record_type"] = d["record_type"]
286 del(d["record_type"])
287 dicts.append(d)
288
289 self._dictionaries = dicts
290 DictionaryGrid._refresh_treeview(self)
291
292
293 # CheckColumn is special because it is a one-shot change. A StringColumn
294 # should not be saved for each keystroke, but CheckColumn should.
295 for c in self.get_columns():
296 if type(c) == CheckColumn:
297 c.renderer.connect("toggled",self._edited_toggled, c)
298 else:
299 c.renderer.connect("edited",self._edited, c)
300
301 def append_row(self, dictionary):
302 """append_row: add a row to the TreeView and to DesktopCouch.
303 If keys are already set up only the the keys in the dictionary
304 matching the keys used for columns will be displayed, though
305 all the key value pairs will be saved to the DesktopCouch.
306 If no keys are set up, and this is the first row, keys will be
307 inferred from the dictionary keys.
308
309 arguments:
310 dictionary - a dictionary to add to the Treeview and to DesktopCouch
311
312 """
313
314 if dictionary is None:
315 dictionary = {}
316
317 #Here we add rows to desktopcouch if needed
318 if "__desktopcouch_id" not in dictionary:
319 self._persist_dict_to_couch(dictionary)
320 DictionaryGrid.append_row(self,dictionary)
321
322 def _persist_dict_to_couch(self,dictionary):
323 """ _persist_dict_to_couch - internal implementation. may be useful
324 a subclass of CouchGrid, but not normally called directly.
325
326 """
327
328 dictionary["record_type"] = self.record_type
329 rec = Record(dictionary)
330 #meh, best not to save an empty row
331 # Perhaps we should raise an exception if not?
332 if len(dictionary) > 1:
333 doc_id = self._db.put_record(rec)
334 dictionary["__desktopcouch_id"] = doc_id
335 dictionary["__record_type"] = self.record_type
336 del(dictionary["record_type"])
337
338
339 def _edited_toggled(self, cell, path, col):
340 """ _edited_toggled - internal signal handler.
341 Updates the database if a cell in the Treeview
342 has been edited special cased for CheckColumns.
343
344 """
345
346 iter = self.list_store.get_iter(path)
347 key = col.key
348 active = not cell.get_active()
349 self._edited(cell, path, active, col)
350
351 def _edited(self, cell, path, new_val, col):
352 """ _edited - internal signal handler.
353 Updates the database if a cell in the Treeview
354 has been edited.
355
356 """
357 iter = self.list_store.get_iter(path)
358 key = col.key
359 dictionary = self.list_store.get_value(iter,len(self.keys))
360
361 if "__desktopcouch_id" not in dictionary: #the row has not been stored
362 #create a document
363 dictionary["record_type"] = self.record_type
364 rec = Record(dictionary)
365 doc_id = self._db.put_record(rec)
366 dictionary["__desktopcouch_id"] = doc_id
367 self.list_store.set_value(iter, len(self.keys), dictionary)
368
369 else: #it has been saved
370 #get the record id from the dictionary
371 #then update the datbase with the change
372 id = dictionary["__desktopcouch_id"]
373 key = col.key
374 self._db.update_fields(id,{key:new_val})
375
376def __show_selected(widget, row, widgets):
377 """Test function for selection properties of CouchGrid"""
378 tv, cg = widgets
379 disp = "Rows:\n"
380 for r in cg.selected_rows:
381 disp += str(r) + "\n"
382
383 disp += "\n\n_Ids:\n"
384 for r in cg.selected_record_ids:
385 disp += str(r) + "\n"
386
387 tv.get_buffer().set_text(disp)
388
389def __select_ids(widget, widgets):
390 """Test function for selecting ids."""
391 entry, cg, lbl = widgets
392 cg.selected_record_ids = entry.get_text().split(",")
393
394if __name__ == "__main__":
395 """creates a test CouchGrid if called directly"""
396
397 from quickly.widgets.grid_column import StringColumn
398
399 #create and show a test window and container
400 win = gtk.Window(gtk.WINDOW_TOPLEVEL)
401 win.set_title("CouchGrid Test Window")
402 win.connect("destroy",gtk.main_quit)
403 win.show()
404 vbox = gtk.VBox(False, False)
405 vbox.show()
406 win.add(vbox)
407
408 #create a test widget with test database values
409 dicts = [{"test?":True,"price":100,"foo count":100,"Key4":"1004"},
410 {"test?":True,"price":100,"foo count":100,"Key4":"1004"},
411 {"test?":True,"price":100,"foo count":100,"Key4":"1004"}]
412
413 #create some settings
414 database = "couch_widget_test"
415 record_type="test_record_type"
416 keys=["price","test?"]
417 hints = {"price": StringColumn}
418
419 #create it and part a bit
420 cg = CouchGrid(database, record_type=record_type, dictionaries=dicts,keys=keys, type_hints = hints)
421 cg.editable = True
422 cg.get_column(0).set_title("Price")
423
424 #finish out and run the test UI
425 cg.show()
426 vbox.pack_start(cg, False, True)
427 hbox = gtk.HBox(False, 5)
428 hbox.show()
429 tv = gtk.TextView()
430 tv.show()
431 tv.set_wrap_mode(gtk.WRAP_CHAR)
432 tv.set_size_request(300,-1)
433 cg.connect("selection-changed",__show_selected, (tv,cg))
434
435 id_vbox = gtk.VBox(False, 5)
436 id_vbox.show()
437
438 fb_lbl = gtk.Label("paste ids into the edit box to select them")
439 fb_lbl.show()
440
441 entry = gtk.Entry()
442 entry.show()
443
444 btn = gtk.Button("select ids")
445 btn.show()
446 btn.connect("clicked", __select_ids, (entry,cg, fb_lbl))
447
448 id_vbox.pack_start(fb_lbl, False, False)
449 id_vbox.pack_start(entry, False, False)
450 id_vbox.pack_end(btn, False, False)
451
452 hbox.pack_start(tv, False, False)
453 vbox.pack_end(hbox, False, False)
454 hbox.pack_end(id_vbox, False, False)
455
456 #run the test app
457 gtk.main()
458
4590
=== modified file 'quickly/widgets/dictionary_grid.py'
--- quickly/widgets/dictionary_grid.py 2011-09-04 23:58:24 +0000
+++ quickly/widgets/dictionary_grid.py 2012-03-06 08:52:21 +0000
@@ -14,10 +14,10 @@
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
15#with this program. If not, see <http://www.gnu.org/licenses/>.15#with this program. If not, see <http://www.gnu.org/licenses/>.
16### END LICENSE16### END LICENSE
17"""A gtk.TreeView for Dictionaries17"""A Gtk.TreeView for Dictionaries
18Displays and persists data in a gtk.TreeView. Handles the18Displays and persists data in a Gtk.TreeView. Handles the
19set up of the gtk.TreeView, gtk.ListModel, gtk.TreeViewColumns,19set up of the Gtk.TreeView, Gtk.ListModel, Gtk.TreeViewColumns,
20and gtk.CellRenderers.20and Gtk.CellRenderers.
2121
22Using22Using
23#create a dictionary if you don't already have one23#create a dictionary if you don't already have one
@@ -40,7 +40,7 @@
40hints = {"price": StringColumn}40hints = {"price": StringColumn}
41dg = CouchGrid(dictionaries=dicts,keys=keys, type_hints = hints)41dg = CouchGrid(dictionaries=dicts,keys=keys, type_hints = hints)
4242
43#A CouchGrid is gtk.TreeView, so you can use gtk.TreeView members43#A CouchGrid is Gtk.TreeView, so you can use Gtk.TreeView members
44dg.get_column(0).set_title("Price")44dg.get_column(0).set_title("Price")
4545
46#Use the selection-changed signal and read from the DictionaryGrid46#Use the selection-changed signal and read from the DictionaryGrid
@@ -67,13 +67,13 @@
6767
68"""68"""
6969
70import gtk70from gi.repository import GObject
71import gobject71from gi.repository import Gtk
72import conventions72import conventions
73from quickly.widgets.grid_column import StringColumn73from grid_column import StringColumn
74from grid_column import CheckColumn74from grid_column import CheckColumn
7575
76class DictionaryGrid(gtk.TreeView):76class DictionaryGrid(Gtk.TreeView):
77 __gtype_name__ = "DictionaryGrid"77 __gtype_name__ = "DictionaryGrid"
78 78
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):
@@ -102,7 +102,7 @@
102102
103 """103 """
104104
105 gtk.TreeView.__init__(self)105 Gtk.TreeView.__init__(self)
106 self.list_store = None106 self.list_store = None
107 self.unfiltered_store = None107 self.unfiltered_store = None
108 self._keys = keys108 self._keys = keys
@@ -115,7 +115,7 @@
115 self._type_hints = {}115 self._type_hints = {}
116 else:116 else:
117 self._type_hints = type_hints117 self._type_hints = type_hints
118 self.get_selection().set_mode(gtk.SELECTION_MULTIPLE)118 self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
119 self._refresh_treeview()119 self._refresh_treeview()
120120
121 #signal handlers to track selection in the treeview121 #signal handlers to track selection in the treeview
@@ -193,7 +193,7 @@
193 # themselves, but for now this works. 193 # themselves, but for now this works.
194194
195 for column in self.get_columns():195 for column in self.get_columns():
196 column.set_editable(editable)196 column.set_editable(editable)
197 self._editable = editable197 self._editable = editable
198198
199 @property199 @property
@@ -251,7 +251,7 @@
251 def _refresh_treeview(self):251 def _refresh_treeview(self):
252 """252 """
253 _refresh_treeview: internal function to handle rebuilding253 _refresh_treeview: internal function to handle rebuilding
254 the gtk.TreeView along with columns and cell renderers.. 254 the Gtk.TreeView along with columns and cell renderers..
255255
256 _refresh_treeview is not typically called directly,256 _refresh_treeview is not typically called directly,
257 but may be useful to override in subclasses.257 but may be useful to override in subclasses.
@@ -350,7 +350,7 @@
350 """350 """
351 remove_selected_rows: removes the rows currently selected351 remove_selected_rows: removes the rows currently selected
352 in the TreeView UI from the TreeView as well as the backing352 in the TreeView UI from the TreeView as well as the backing
353 gtk.ListStore.353 Gtk.ListStore.
354354
355 """355 """
356356
@@ -361,11 +361,11 @@
361 return361 return
362362
363 #store the last selected row to reselect after removal363 #store the last selected row to reselect after removal
364 next_to_select = rows[-1][0] + 1 - len(rows)364 next_to_select = rows[-1].get_indices()[0] + 1 - len(rows)
365 365
366 #loop through and remove366 #loop through and remove
367367
368 if type(model) is not gtk.ListStore:368 if type(model) is not Gtk.ListStore:
369 iters = [model.get_model().get_iter(path) for path in rows]369 iters = [model.get_model().get_iter(path) for path in rows]
370 store_iters = []370 store_iters = []
371371
@@ -431,8 +431,8 @@
431 431
432 #create the liststore with the designated types432 #create the liststore with the designated types
433 #the last column is always for storing the backing dict433 #the last column is always for storing the backing dict
434 col_types.append(gobject.TYPE_PYOBJECT)434 col_types.append(GObject.TYPE_PYOBJECT)
435 self.list_store = gtk.ListStore(*col_types)435 self.list_store = Gtk.ListStore(*col_types)
436436
437 for c in self.get_columns():437 for c in self.get_columns():
438 self.__last_sorted_col = None438 self.__last_sorted_col = None
@@ -459,11 +459,11 @@
459 self.__last_sorted_col.set_sort_indicator(False)459 self.__last_sorted_col.set_sort_indicator(False)
460 self.__last_sorted_col = column460 self.__last_sorted_col = column
461461
462 __gsignals__ = {'cell-edited' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,462 __gsignals__ = {'cell-edited' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
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)),
464464
465 'selection-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,465 'selection-changed' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
466 (gobject.TYPE_PYOBJECT,))466 (GObject.TYPE_PYOBJECT,))
467 }467 }
468468
469def __show_selected(widget, selected_rows, data=None):469def __show_selected(widget, selected_rows, data=None):
@@ -488,14 +488,14 @@
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},
489 {"ID": 3, "tags" : "ddd eee fff","_foo":"bar"},489 {"ID": 3, "tags" : "ddd eee fff","_foo":"bar"},
490 {"ID": 4, "price":5.00,"_foo":"bar"}]490 {"ID": 4, "price":5.00,"_foo":"bar"}]
491 #create and show a test window491 #create and show a test windowp
492 win = gtk.Window(gtk.WINDOW_TOPLEVEL)492 win = Gtk.Window()
493 win.set_title("DictionaryGrid Test Window")493 win.set_title("DictionaryGrid Test Window")
494 win.connect("destroy",gtk.main_quit)494 win.connect("destroy",Gtk.main_quit)
495 win.show()495 win.show()
496496
497 #create a top level container497 #create a top level container
498 vbox = gtk.VBox(False, False)498 vbox = Gtk.VBox(False, False)
499 vbox.show()499 vbox.show()
500 win.add(vbox)500 win.add(vbox)
501501
@@ -507,19 +507,19 @@
507507
508 #show the control, add it to the window, and run the main loop508 #show the control, add it to the window, and run the main loop
509 grid.show()509 grid.show()
510 vbox.pack_start(grid, False, True)510 vbox.pack_start(grid, False, False, 0)
511511
512 #create a test display area512 #create a test display area
513 hbox = gtk.HBox(False, 5)513 hbox = Gtk.HBox(False, 5)
514 hbox.show()514 hbox.show()
515 tv = gtk.TextView()515 tv = Gtk.TextView()
516 tv.show()516 tv.show()
517 grid.connect("selection-changed",__show_selected, tv)517 grid.connect("selection-changed",__show_selected, tv)
518 grid.connect("cell-edited",__on_edited, tv)518 grid.connect("cell-edited",__on_edited, tv)
519519
520 hbox.pack_start(tv, False, False)520 hbox.pack_start(tv, False, False, 0)
521 vbox.pack_end(hbox, False, False)521 vbox.pack_end(hbox, False, False, 0)
522522
523 #run the test app523 #run the test app
524 gtk.main()524 Gtk.main()
525525
526526
=== modified file 'quickly/widgets/grid_column.py'
--- quickly/widgets/grid_column.py 2011-09-04 23:58:24 +0000
+++ quickly/widgets/grid_column.py 2012-03-06 08:52:21 +0000
@@ -23,8 +23,8 @@
23to control the type of column used for a key in a dictionary.23to control the type of column used for a key in a dictionary.
2424
25Customizing25Customizing
26The column types in this module are all descendants of gtk.TreeView, so26The column types in this module are all descendants of Gtk.TreeView, so
27you can use all of the gtk.TreeView methods and properties to control27you can use all of the Gtk.TreeView methods and properties to control
28how a grid column looks or works.28how a grid column looks or works.
2929
30#Find a grid column and change the title30#Find a grid column and change the title
@@ -34,7 +34,7 @@
3434
35Extending35Extending
36To display data in a column with a string, such as to display words and36To display data in a column with a string, such as to display words and
37numbers, you should extend StringColumn. Otherwise, extend gtk.TreeView37numbers, you should extend StringColumn. Otherwise, extend Gtk.TreeView
38directly. In either case, you will need to implement a set of functions.38directly. In either case, you will need to implement a set of functions.
3939
40A Grid Column must track two different data, a "real" value, which is tracked40A Grid Column must track two different data, a "real" value, which is tracked
@@ -56,7 +56,7 @@
56a row does not contain a key, value pair for the specified column. For example56a row does not contain a key, value pair for the specified column. For example
57StringColumn returns an empty string ("")57StringColumn returns an empty string ("")
5858
59A new column type will often require a specially configured gtk.CellRenderer.59A new column type will often require a specially configured Gtk.CellRenderer.
60If you are deriving from StringColumn, but are using a custom renderer,60If you are deriving from StringColumn, but are using a custom renderer,
61you need to override the _initialize_renderer method, and set the 61you need to override the _initialize_renderer method, and set the
62columns renderer property to the renderer. You should also connect the 62columns renderer property to the renderer. You should also connect the
@@ -68,8 +68,8 @@
68_initialize_renderer:68_initialize_renderer:
6969
70 def _initialize_renderer( self, editable, index ):70 def _initialize_renderer( self, editable, index ):
71 self.renderer = gtk.CellRendererSpin()71 self.renderer = Gtk.CellRendererSpin()
72 adj = gtk.Adjustment(0,-10000000000,10000000000,1)72 adj = Gtk.Adjustment(0,-10000000000,10000000000,1)
73 self.renderer.set_property("adjustment", adj)73 self.renderer.set_property("adjustment", adj)
74 self.renderer.set_property("editable", editable)74 self.renderer.set_property("editable", editable)
75 self.renderer.set_property("digits",2)75 self.renderer.set_property("digits",2)
@@ -89,26 +89,20 @@
89"""89"""
9090
9191
92
93import sys92import sys
94import datetime93import datetime
95import gettext94import gettext
96from gettext import gettext as _95from gettext import gettext as _
97gettext.textdomain('quickly-widgets')96gettext.textdomain('quickly-widgets')
9897
99try:98import grid_filter
100 import pygtk99
101 pygtk.require("2.0")100from gi.repository import GObject
102 import gtk101from gi.repository import Gtk
103 import gobject102from gi.repository import Gdk
104 import grid_filter103
105104
106105class GridColumn( Gtk.TreeViewColumn ):
107except Exception, inst:
108 print "some dependencies for GridFilter are not available"
109 raise inst
110
111class GridColumn( gtk.TreeViewColumn ):
112 """GridColumn - Base class that provides features that is important106 """GridColumn - Base class that provides features that is important
113 to all columns used in a DictionaryGrid or decendants. Currently107 to all columns used in a DictionaryGrid or decendants. Currently
114 it's useful only to StringColumn and CheckColumn, but should make it108 it's useful only to StringColumn and CheckColumn, but should make it
@@ -116,7 +110,7 @@
116 """110 """
117111
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 ):
119 gtk.TreeViewColumn.__init__(self, key, renderer, text=index)113 Gtk.TreeViewColumn.__init__(self, key, renderer, text=index)
120114
121 self.set_clickable(True)115 self.set_clickable(True)
122 self.set_resizable(True)116 self.set_resizable(True)
@@ -124,6 +118,7 @@
124 self.index = index118 self.index = index
125 self.key = key119 self.key = key
126 self.dictionary_index = dictionary_index120 self.dictionary_index = dictionary_index
121 # Why should list_store be set to None?
127 self.list_store = None122 self.list_store = None
128123
129124
@@ -135,21 +130,22 @@
135 130
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)]
137132
138 # I NEED TO HAVE A LOOK AT THIS IF-BLOCK. At least, it needs a comment. 133 # Sort opposite of last time
139 if sort_order == gtk.SORT_ASCENDING:134 if sort_order == Gtk.SortType.ASCENDING:
140 sort_order = gtk.SORT_DESCENDING135 sort_order = Gtk.SortType.DESCENDING
141 else:136 else:
142 sort_order = gtk.SORT_ASCENDING137 sort_order = Gtk.SortType.ASCENDING
143138
144 self.set_sort_order(sort_order)139 self.set_sort_order(sort_order)
145 self.set_sort_indicator(True)140 self.set_sort_indicator(True)
146 141
147 if sort_order == gtk.SORT_ASCENDING:142 if sort_order == Gtk.SortType.ASCENDING:
148 rows.sort(self._sort_ascending)143 rows.sort(self._sort_ascending)
149 else:144 else:
150 rows.sort(self._sort_descending)145 rows.sort(self._sort_descending)
151146
152 self.list_store.reorder([r[-1] for r in rows])147 # Where does self.list_store come from?
148 self.list_store.set_sort_column_id(self.index, sort_order)
153149
154 def set_editable(self, editable):150 def set_editable(self, editable):
155 self.renderer.set_property("editable", editable)151 self.renderer.set_property("editable", editable)
@@ -164,7 +160,7 @@
164160
165 """161 """
166162
167 column_type = gobject.TYPE_STRING163 column_type = GObject.TYPE_STRING
168 __sort_order = None164 __sort_order = None
169 default_filter = grid_filter.StringFilterBox165 default_filter = grid_filter.StringFilterBox
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 ):
@@ -247,7 +243,7 @@
247 cell_renderer - a reference to the specific cell_renderer that is243 cell_renderer - a reference to the specific cell_renderer that is
248 formatting the string244 formatting the string
249245
250 tree_model - the gtk.ListStore that is the backing data for the246 tree_model - the Gtk.ListStore that is the backing data for the
251 DictionaryGrid that contains the column.247 DictionaryGrid that contains the column.
252248
253 iter - an iterator that references the row of the the DictionaryGrid249 iter - an iterator that references the row of the the DictionaryGrid
@@ -274,8 +270,7 @@
274270
275 """271 """
276272
277 self.renderer = gtk.CellRendererText()273 self.renderer = Gtk.CellRendererText()
278 self.renderer.mode = gtk.CELL_RENDERER_MODE_EDITABLE
279 self.renderer.set_property("editable", editable)274 self.renderer.set_property("editable", editable)
280 self.renderer.connect("edited", self._cell_edited)275 self.renderer.connect("edited", self._cell_edited)
281 276
@@ -340,14 +335,14 @@
340 return ""335 return ""
341336
342class CurrencyColumn( StringColumn ):337class CurrencyColumn( StringColumn ):
343 """CurrencyColumn - display data in currency format. Uses a gtk.Spinner338 """CurrencyColumn - display data in currency format. Uses a Gtk.Spinner
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.
345340
346 Inherits from StringColumn.341 Inherits from StringColumn.
347342
348 """343 """
349344
350 column_type = gobject.TYPE_STRING345 column_type = GObject.TYPE_STRING
351 default_filter = grid_filter.NumericFilterBox346 default_filter = grid_filter.NumericFilterBox
352 def __init__(self, key, index,dictionary_index, editable=True ):347 def __init__(self, key, index,dictionary_index, editable=True ):
353 """Creates a CurrencyColumn348 """Creates a CurrencyColumn
@@ -380,8 +375,8 @@
380375
381 """376 """
382377
383 self.renderer = gtk.CellRendererSpin()378 self.renderer = Gtk.CellRendererSpin()
384 adj = gtk.Adjustment(0,-10000000000,10000000000,1)379 adj = Gtk.Adjustment(0,-10000000000,10000000000,1)
385 self.renderer.set_property("adjustment", adj)380 self.renderer.set_property("adjustment", adj)
386 self.renderer.set_property("editable", editable)381 self.renderer.set_property("editable", editable)
387 self.renderer.set_property("digits",2)382 self.renderer.set_property("digits",2)
@@ -494,19 +489,19 @@
494489
495 """490 """
496491
497 column_type = gobject.TYPE_STRING492 column_type = GObject.TYPE_STRING
498 default_filter = grid_filter.TagsFilterBox493 default_filter = grid_filter.TagsFilterBox
499494
500495
501class IntegerColumn( StringColumn ):496class IntegerColumn( StringColumn ):
502 """IntegerColumn - display data in Integer format. Uses a gtk.Spinner497 """IntegerColumn - display data in Integer format. Uses a Gtk.Spinner
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.
504499
505 Inherits from StringColumn.500 Inherits from StringColumn.
506501
507 """502 """
508503
509 column_type = gobject.TYPE_STRING504 column_type = GObject.TYPE_STRING
510 default_filter = grid_filter.IntegerFilterBox505 default_filter = grid_filter.IntegerFilterBox
511506
512 def __init__(self, key, index, dictionary_index, editable=True ):507 def __init__(self, key, index, dictionary_index, editable=True ):
@@ -529,8 +524,8 @@
529 StringColumn.__init__( self, key, index, dictionary_index, editable)524 StringColumn.__init__( self, key, index, dictionary_index, editable)
530525
531 def _initialize_renderer( self, editable, index ):526 def _initialize_renderer( self, editable, index ):
532 self.renderer = gtk.CellRendererSpin()527 self.renderer = Gtk.CellRendererSpin()
533 adj = gtk.Adjustment(0,-10000000000,10000000000,1)528 adj = Gtk.Adjustment(0,-10000000000,10000000000,1)
534 self.renderer.set_property("adjustment", adj)529 self.renderer.set_property("adjustment", adj)
535 self.renderer.set_property("editable", editable)530 self.renderer.set_property("editable", editable)
536 self.renderer.connect("edited", self._cell_edited)531 self.renderer.connect("edited", self._cell_edited)
@@ -640,11 +635,11 @@
640class CheckColumn( GridColumn ):635class CheckColumn( GridColumn ):
641 """CheckColumn - display data as checkboxes. Store real values as bool.636 """CheckColumn - display data as checkboxes. Store real values as bool.
642637
643 Inherits from gtk.TreeViewColumn.638 Inherits from Gtk.TreeViewColumn.
644639
645 """640 """
646641
647 column_type = gobject.TYPE_INT642 column_type = GObject.TYPE_INT
648 default_filter = grid_filter.CheckFilterBox643 default_filter = grid_filter.CheckFilterBox
649644
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 ):
@@ -684,7 +679,7 @@
684 y = y[self.index]679 y = y[self.index]
685 return x - y680 return x - y
686681
687 def _on_format(self,column, cell_renderer, tree_model, iter):682 def _on_format(self,column, cell_renderer, tree_model, iter, format_function):
688 cell_val = tree_model.get_value(iter, self.index)683 cell_val = tree_model.get_value(iter, self.index)
689 cell_renderer.set_property('inconsistent', False)684 cell_renderer.set_property('inconsistent', False)
690 if cell_val == 1: 685 if cell_val == 1:
@@ -698,9 +693,9 @@
698 self.extra_format_function()693 self.extra_format_function()
699 694
700 def _initialize_renderer( self, editable, index ):695 def _initialize_renderer( self, editable, index ):
701 self.renderer = gtk.CellRendererToggle()696 self.renderer = Gtk.CellRendererToggle()
702 self.renderer.set_property("activatable", editable)697 self.renderer.set_property("activatable", editable)
703 col = gtk.TreeViewColumn(self.key, self.renderer, active=index)698 col = Gtk.TreeViewColumn(self.key, self.renderer, active=index)
704 self.renderer.connect("toggled", self.toggled)699 self.renderer.connect("toggled", self.toggled)
705700
706 def toggled(self, cell, path, data=None):701 def toggled(self, cell, path, data=None):
@@ -772,14 +767,14 @@
772 return bool(val)767 return bool(val)
773768
774class DateColumn( StringColumn ):769class DateColumn( StringColumn ):
775 """DateColumn - display data in date format. Uses a gtk.Calendar770 """DateColumn - display data in date format. Uses a Gtk.Calendar
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.
777772
778 Inherits from StringColumn.773 Inherits from StringColumn.
779774
780 """775 """
781776
782 column_type = gobject.TYPE_STRING777 column_type = GObject.TYPE_STRING
783 default_filter = grid_filter.DateFilterBox778 default_filter = grid_filter.DateFilterBox
784779
785 def __init__(self, key, index,dictionary_index, editable=True ):780 def __init__(self, key, index,dictionary_index, editable=True ):
@@ -816,27 +811,27 @@
816 self.renderer.set_property('editable',self._editable)811 self.renderer.set_property('editable',self._editable)
817 self.renderer.connect("edited", self._cell_edited)812 self.renderer.connect("edited", self._cell_edited)
818 813
819class CellRendererDate(gtk.CellRendererText):814class CellRendererDate(Gtk.CellRendererText):
820 def __init__(self):815 def __init__(self):
821 gtk.CellRendererText.__init__(self)816 Gtk.CellRendererText.__init__(self)
822 self.date_format = '%Y-%m-%d'817 self.date_format = '%Y-%m-%d'
823818
824 self.calendar_window = None819 self.calendar_window = None
825 self.calendar = None820 self.calendar = None
826821
827 def _create_calendar(self, treeview):822 def _create_calendar(self, treeview):
828 self.calendar_window = gtk.Dialog(parent=treeview.get_toplevel())823 self.calendar_window = Gtk.Dialog(parent=treeview.get_toplevel())
829 self.calendar_window.action_area.hide()824 self.calendar_window.action_area.hide()
830 self.calendar_window.set_decorated(False)825 self.calendar_window.set_decorated(False)
831 self.calendar_window.set_property('skip-taskbar-hint', True)826 self.calendar_window.set_property('skip-taskbar-hint', True)
832827
833 self.calendar = gtk.Calendar()828 self.calendar = Gtk.Calendar()
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)
835 self.calendar.connect('day-selected-double-click', self._day_selected, None)830 self.calendar.connect('day-selected-double-click', self._day_selected, None)
836 self.calendar.connect('key-press-event', self._day_selected)831 self.calendar.connect('key-press-event', self._day_selected)
837 self.calendar.connect('focus-out-event', self._selection_cancelled)832 self.calendar.connect('focus-out-event', self._selection_cancelled)
838 self.calendar_window.set_transient_for(None) # cancel the modality of dialog833 self.calendar_window.set_transient_for(None) # cancel the modality of dialog
839 self.calendar_window.vbox.pack_start(self.calendar)834 self.calendar_window.vbox.pack_start(self.calendar, True, True, 0)
840835
841 # necessary for getting the (width, height) of calendar_window836 # necessary for getting the (width, height) of calendar_window
842 self.calendar.show()837 self.calendar.show()
@@ -871,20 +866,20 @@
871 866
872 response = self.calendar_window.run()867 response = self.calendar_window.run()
873 self.calendar_window.hide()868 self.calendar_window.hide()
874 if response == gtk.RESPONSE_OK:869 if response == Gtk.ResponseType.OK:
875 (year, month, day) = self.calendar.get_date()870 (year, month, day) = self.calendar.get_date()
876 date = datetime.date(year, month + 1, day).strftime(self.date_format) # gtk.Calendar's month starts from zero871 date = datetime.date(year, month + 1, day).strftime(self.date_format) # Gtk.Calendar's month starts from zero
877 self.emit('edited', path, date)872 self.emit('edited', path, date)
878 873
879 return None # don't return any editable, our gtk.Dialog did the work already874 return None # don't return any editable, our Gtk.Dialog did the work already
880875
881 def _day_selected(self, calendar, event):876 def _day_selected(self, calendar, event):
882 # event == None for day selected via doubleclick877 # event == None for day selected via doubleclick
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':
884 self.calendar_window.response(gtk.RESPONSE_OK)879 self.calendar_window.response(Gtk.ResponseType.OK)
885 return True880 return True
886881
887 def _selection_cancelled(self, calendar, event):882 def _selection_cancelled(self, calendar, event):
888 self.calendar_window.response(gtk.RESPONSE_CANCEL)883 self.calendar_window.response(Gtk.ResponseType.CANCEL)
889 return True884 return True
890885
891886
=== modified file 'quickly/widgets/grid_filter.py'
--- quickly/widgets/grid_filter.py 2010-12-18 16:06:52 +0000
+++ quickly/widgets/grid_filter.py 2012-03-06 08:52:21 +0000
@@ -15,12 +15,12 @@
15### END LICENSE15### END LICENSE
1616
17"""Widgets and Objects for filtering a DictionaryGrid17"""Widgets and Objects for filtering a DictionaryGrid
18GridFilter is as gtk.VBox that provides filtering UI for a 18GridFilter is as Gtk.VBox that provides filtering UI for a
19DictionaryGrid. Provides multiple filters, and choosing 19DictionaryGrid. Provides multiple filters, and choosing
20between "And" and "Or" filtering. Provides default filters appropriate20between "And" and "Or" filtering. Provides default filters appropriate
21for each column.21for each column.
2222
23GridFilter row is a gtk.HBox that is a container for displaying FilterCombos.23GridFilter row is a Gtk.HBox that is a container for displaying FilterCombos.
2424
25FilterCombos display a filter and handle filtering of rows to25FilterCombos display a filter and handle filtering of rows to
26display and hide in the associated DictionaryGrid. The GridColumns26display and hide in the associated DictionaryGrid. The GridColumns
@@ -60,374 +60,370 @@
60 self.append("starts with",lambda x,y: x.startswith(y))60 self.append("starts with",lambda x,y: x.startswith(y))
61 self.append("ends with",lambda x,y: x.endswith(y))61 self.append("ends with",lambda x,y: x.endswith(y))
6262
63Filter UI could be created to use widgets other than gtk.Combo so long as63Filter UI could be created to use widgets other than Gtk.Combo so long as
64the widget has a get_model function that returns a gtk.ListStore with64the widget has a get_model function that returns a Gtk.ListStore with
65filtering functions stored as the last value (column) in the liststore.65filtering functions stored as the last value (column) in the liststore.
6666
67"""67"""
6868
69
69import sys70import sys
70import datetime71import datetime
71import gettext72import gettext
72from gettext import gettext as _73from gettext import gettext as _
73gettext.textdomain('quickly-widgets')74gettext.textdomain('quickly-widgets')
7475
75try:76from gi.repository import GObject
76 import pygtk77from gi.repository import Gtk
77 pygtk.require("2.0")78
78 import gtk79class GridFilter( Gtk.VBox ):
79 import gobject80 """GridFilter: A widget that provides a user interface for filtering a
8081 treeview. A GridFilter hosts one ore more GridRows, which in turn host
81except Exception, inst:82 an active filter.
82 print "some dependencies for GridFilter are not available"83 """
83 raise inst84
8485 def __init__(self, grid, filter_hints={} ):
85class GridFilter( gtk.VBox ):86 """Create a GridFilter for filtering an associated treeview.
86 """GridFilter: A widget that provides a user interface for filtering a87 This class is used by BugsPane.
87 treeview. A GridFilter hosts one ore more GridRows, which in turn host88
88 an active filter.89 arguments:
8990 grid - A DictionaryGrid to filter
90 """91
91 def __init__(self, grid, filter_hints={} ):92 options arguments:
92 """Create a GridFilter for filtering an associated treeview.93 filter_hints - a dictionary of column keys to FilterCombo types to
93 This class is used by BugsPane.94 provide custom filtering.
9495
95 arguments:96 """
96 grid - A DictionaryGrid to filter97
9798 Gtk.VBox.__init__( self, False, 10 )
98 options arguments:99 self.grid = grid
99 filter_hints - a dictionary of column keys to FilterCombo types to100 self.store = grid.get_model()
100 provide custom filtering. 101 self.filter_hints = filter_hints
101 102
102 """103 #create the and/or radio buttons
103104 radio_box = Gtk.HBox(False,2)
104 gtk.VBox.__init__( self, False, 10 )105 radio_box.show()
105 self.grid = grid 106 self.pack_start(radio_box, False, False, 0)
106 self.store = grid.get_model()107 self.and_button = Gtk.RadioButton.new_with_label_from_widget(None,_("M_atch All of the following"))
107 self.filter_hints = filter_hints108 self.and_button.show()
108109 self.and_button.connect("toggled",self.__filter_changed)
109 #create the and/or radio buttons110 radio_box.pack_start(self.and_button, False, False, 0)
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"))
111 radio_box.show()112 or_button.show()
112 self.pack_start(radio_box, False, False)113 radio_box.pack_start(or_button, False, False, 0)
113 self.and_button = gtk.RadioButton(None,_("M_atch All of the following"), True)114 self.rows = []
114 self.and_button.show()115 self._add_row(self)
115 self.and_button.connect("toggled",self.__filter_changed)116
116 radio_box.pack_start(self.and_button, False, False)117 def _add_row(self, widget, data=None):
117 or_button = gtk.RadioButton(self.and_button,_("Match any _of the following"), True)118 """_add_row: internal signal handler that receives a request
118 or_button.show()119 from a FilterRow to add a new row. Sets up and adds the row to the GridFilter.
119 radio_box.pack_start(or_button, False, False)120
120 self.rows = []121 Do not call directly
121 self._add_row(self)122 """
122123
123 def _add_row(self, widget, data=None):124 #TODO: I suppose this is leaking references to filter rows
124 """_add_row: internal signal handler that receives a request 125 row = FilterRow(self.grid, len(self.rows) > 0, self.filter_hints )
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)
126127 row.connect('remove_row_requested',self._remove_row)
127 Do not call directly128 row.connect('refilter_requested',self.__filter_changed)
128 """129 row.show()
129130 self.rows.append(row)
130 #TODO: I suppose this is leaking references to filter rows131 self.pack_start(row, False, False, 0)
131 row = FilterRow(self.grid, len(self.rows) > 0, self.filter_hints )132
132 row.connect('add_row_requested',self._add_row)133 def _remove_row(self, widget, data=None):
133 row.connect('remove_row_requested',self._remove_row)134 """_remove_row: internal signal handler that receives a
134 row.connect('refilter_requested',self.__filter_changed)135 request from a FilterRow to remove itself from the GridFilter.
135 row.show()136
136 self.rows.append(row)137 Do not call directly
137 self.pack_start(row, False, False)138 """
138 139
139 def _remove_row(self, widget, data=None):140 self.rows.remove(widget)
140 """_remove_row: internal signal handler that receives a 141 self.remove(widget)
141 request from a FilterRow to remove itself from the GridFilter.142 self.__filter_changed(self)
142143
143 Do not call directly144 def __filter_changed(self,widget, data=None):
144 """145 """__filter_changed: internal signal handler that handles
145146 requests to reapply the fitlers in the GridFilter's FilterRows.
146 self.rows.remove(widget)147
147 self.remove(widget)148 """
148 self.__filter_changed(self)149
149150 filt = self.store.filter_new()
150 def __filter_changed(self,widget, data=None):151 sort_mod = Gtk.TreeModelSort(model=filt)
151 """__filter_changed: internal signal handler that handles 152 filt.set_visible_func(self.__filter_func, data )
152 requests to reapply the fitlers in the GridFilter's FilterRows.153 filt.refilter()
153154 self.grid.set_model(sort_mod)
154 """155
155156 def __filter_func(self, model, iter, data):
156 filt = self.store.filter_new()157 """filter_func: called for each row in the treeview model in response to
157 sort_mod = gtk.TreeModelSort(filt)158 a __filter_changed signal. Determines for each row whether it should be
158 filt.set_visible_func(self.__filter_func, data )159 visible based on the FilterRows in the GridFilter.
159 filt.refilter()160
160 self.grid.set_model(sort_mod)161
161 162 Do not call directly
162 def __filter_func(self, model, iter, data):163 """
163 """filter_func: called for each row in the treeview model in response to164 #determine whether this is an "and" or an "or" filter
164 a __filter_changed signal. Determines for each row whether it should be165 match_all = self.and_button.get_active()
165 visible based on the FilterRows in the GridFilter.166
166167 for r in self.rows:
167168 rez = r.is_match(iter.copy(),model) #check the result of each filter
168 Do not call directly169 if match_all: #if it's an "and" filter
169 """170 if not rez: #and if the filter does not match
170 #determine whether this is an "and" or an "or" filter171 return False #then the row should not be visible
171 match_all = self.and_button.get_active()172 else: #but if it's an "or" filter
172173 if rez: #and it is a match
173 for r in self.rows:174 return True #return that the row should be visible
174 rez = r.is_match(iter.copy(),model) #check the result of each filter175 return match_all #all filters match an "and" or none matched an "or"
175 if match_all: #if it's an "and" filter176
176 if not rez: #and if the filter does not match177class FilterRow( Gtk.HBox):
177 return False #then the row should not be visible178 """FilterRow: A widget that displays a single filter in a GridFilter.
178 else: #but if it's an "or" filter179 Typically, this class will not be used directly, but only via a GridFilter.
179 if rez: #and it is a match180
180 return True #return that the row should be visible181 """
181 return match_all #all filters match an "and" or none matched an "or" 182 wait_for_input = False
182 183
183class FilterRow( gtk.HBox):184 def __init__(self, grid, removable=True, filter_hints={}):
184 """FilterRow: A widget that displays a single filter in a GridFilter.185 """Create a FilterRow to be used in a GridFilter.
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.
186 187 The combo stores the string to display for the heading, as well as
187 """188 the widget that is used to filter each heading. When the user changes
188 wait_for_input = False189 the value in the dropdown, the FilterRow retrieves the correct filter from
189190 the combo, and displays that filter to the user.
190 def __init__(self, grid, removable=True, filter_hints={}):191
191 """Create a FilterRow to be used in a GridFilter.192 The FilterRow also handles offering UI for the user to add and remove
192 A FitlerRow is comprised of a combo that lists the treeview headings.193 FilterRows for the GridFilter containing it.
193 The combo stores the string to display for the heading, as well as
194 the widget that is used to filter each heading. When the user changes
195 the value in the dropdown, the FilterRow retrieves the correct filter from
196 the combo, and displays that filter to the user.
197
198 The FilterRow also handles offering UI for the user to add and remove
199 FilterRows for the GridFilter containing it.
200 194
201 grid - 195 grid -
202196
203 keyword arguments:197 keyword arguments:
204 removable - True if the row should be able to be removed from the GridFilter198 removable - True if the row should be able to be removed from the GridFilter
205 Typicall False for the first row.199 Typicall False for the first row.
206200
207 filter_hints - a dictionary of keys mapped to custom filters to apply to the201 filter_hints - a dictionary of keys mapped to custom filters to apply to the
208 column designated by the key202 column designated by the key
209203
210 """204 """
211205
212 gtk.HBox.__init__( self, False, 10 )206 Gtk.HBox.__init__( self, False, 10 )
213 self.store = grid.get_model()207 self.store = grid.get_model()
214 self.grid = grid208 self.grid = grid
215209
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)
217211
218 #apply default combos212
219 for i, k in enumerate(self.grid.keys):213 #apply default combos
220 if k in filter_hints:214 for i, k in enumerate(self.grid.keys):
221 filt_combo = filter_hints[k]215 if k in filter_hints:
222 else:216 filt_combo = filter_hints[k]
223 filt_combo = grid.get_columns()[i].default_filter()217 else:
218 filt_combo = grid.get_columns()[i].default_filter()
224 219
225 column_title = grid.get_columns()[i].get_title()220 column_title = grid.get_columns()[i].get_title()
226 heading_combo_store.append([column_title,filt_combo,i])221 heading_combo_store.append([column_title,filt_combo,i])
227222
228 filt_combo.connect("changed",self.__filter_changed)223 filt_combo.connect("changed",self.__filter_changed)
229 filt_combo.show()224 filt_combo.show()
230 225
231 self.column_combo = gtk.ComboBox(heading_combo_store)226 self.column_combo = Gtk.ComboBox.new_with_model(heading_combo_store)
232 cell = gtk.CellRendererText()227 cell = Gtk.CellRendererText()
233 self.column_combo.pack_start(cell, True)228 self.column_combo.pack_start(cell, True)
234 self.column_combo.add_attribute(cell, 'text', 0)229 self.column_combo.add_attribute(cell, 'text', 0)
235230
236 self.filter_space = gtk.HBox(False,1)231 self.filter_space = Gtk.HBox(False,1)
237 self.filter_space.show()232 self.filter_space.show()
238233
239 self.column_combo.show()234 self.column_combo.show()
240 vb = gtk.VBox(False, 5)235 vb = Gtk.VBox(False, 5)
241 vb.show()236 vb.show()
242 vb.pack_start(self.column_combo, True, False)237 vb.pack_start(self.column_combo, True, False, 0)
243 self.pack_start(vb,False, False)238 self.pack_start(vb,False, False, 0)
244 self.column_combo.connect("changed",self.__column_changed)239 self.column_combo.connect("changed",self.__column_changed)
245 self.column_combo.set_active(0)240 self.column_combo.set_active(0)
246241
247 self.pack_start(self.filter_space, False, False)242 self.pack_start(self.filter_space, False, False, 0)
248243
249 button_box = gtk.HBox(False,2)244 button_box = Gtk.HBox(False,2)
250 button_box.show()245 button_box.show()
251 self.pack_start(button_box,False,False)246 self.pack_start(button_box,False,False, 0)
252247
253 #add a button that can create a new row in the grid filter248 #add a button that can create a new row in the grid filter
254 add_button = gtk.Button(stock = gtk.STOCK_ADD)249 add_button = Gtk.Button(stock = Gtk.STOCK_ADD)
255 add_button.show()250 add_button.show()
256 vb2 = gtk.VBox(False, 5)251 vb2 = Gtk.VBox(False, 5)
257 vb2.show()252 vb2.show()
258 vb2.pack_start(add_button, True, False)253 vb2.pack_start(add_button, True, False, 0)
259 button_box.pack_start(vb2, False, False)254 button_box.pack_start(vb2, False, False, 0)
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) )
261256
262 #add a button to remove the row if applicable257 #add a button to remove the row if applicable
263 if removable:258 if removable:
264 rm_button = gtk.Button(stock = gtk.STOCK_REMOVE)259 rm_button = Gtk.Button(stock = Gtk.STOCK_REMOVE)
265 rm_button.show()260 rm_button.show()
266 vb3 = gtk.VBox(False, 5)261 vb3 = Gtk.VBox(False, 5)
267 vb3.show()262 vb3.show()
268 vb3.pack_start(rm_button, True, False)263 vb3.pack_start(rm_button, True, False, 0)
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) )
270 button_box.pack_start(vb3)265 button_box.pack_start(vb3, True, True, 0)
271266
272 __gsignals__ = {'add_row_requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,267 __gsignals__ = {'add_row_requested' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
273 (gobject.TYPE_PYOBJECT,)),268 (GObject.TYPE_PYOBJECT,)),
274 'remove_row_requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,269 'remove_row_requested' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
275 (gobject.TYPE_PYOBJECT,)),270 (GObject.TYPE_PYOBJECT,)),
276 'refilter_requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,271 'refilter_requested' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
277 (gobject.TYPE_PYOBJECT,))272 (GObject.TYPE_PYOBJECT,))
278 }273 }
279 274
280 def __column_changed(self, widget, data = None):275
281 """column_changed: internal signal handler for the user changing276 def __column_changed(self, widget, data = None):
282 the combo for the column that they wish to apply the filter to.277 """column_changed: internal signal handler for the user changing
283 removes the other filter widgets and replaces them widgets stored in278 the combo for the column that they wish to apply the filter to.
284 the filter widget.279 removes the other filter widgets and replaces them widgets stored in
285280 the filter widget.
286 """281
287282 """
288 if len(self.filter_space.get_children()) > 0:283
289 self.filter_space.remove(self.filter_space.get_children()[0]) 284 if len(self.filter_space.get_children()) > 0:
290 iter = widget.get_model().get_iter(widget.get_active())285 self.filter_space.remove(self.filter_space.get_children()[0])
291 filter_box = widget.get_model().get_value(iter,1)286 iter = widget.get_model().get_iter(widget.get_active())
292 self.filter_space.pack_start(filter_box, False, False)287 filter_box = widget.get_model().get_value(iter,1)
293288 self.filter_space.pack_start(filter_box, False, False, 0)
294 def __filter_changed(self,widget, data=None):289
295 """filter_changed: internal signal handler called when the FilterRow has changed.290 def __filter_changed(self,widget, data=None):
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.
297 active (a heading is selected in the combo and the user has entered292 Used to tell the GridFilter to refilter. Only emits if the filter is
298 text in the filter.293 active (a heading is selected in the combo and the user has entered
299294 text in the filter.
300 """295
296 """
301 297
302 #if not self.wait_for_input:298 #if not self.wait_for_input:
303 #if self.__get_current_filter_combo().get_active > -1:299 #if self.__get_current_filter_combo().get_active > -1:
304 self.emit('refilter_requested',self)300 self.emit('refilter_requested',self)
305301
306 def __get_current_filter_combo(self):302 def __get_current_filter_combo(self):
307 """get_current_filter_combo: internal function that retrieves303 """get_current_filter_combo: internal function that retrieves
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.
309305
310 """306 """
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())
312 return self.column_combo.get_model().get_value(iter,1)308 return self.column_combo.get_model().get_value(iter,1)
313309
314 def is_match(self, store_iter, model):310 def is_match(self, store_iter, model):
315 """is_match: returns true if the filter set in the FilterRow matches311 """is_match: returns true if the filter set in the FilterRow matches
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
317 to hide or show a row.313 to hide or show a row.
318314
319 Typically called for each treeview row and each FilterRow in response315 Typically called for each treeview row and each FilterRow in response
320 to a change in one of the FilterRows.316 to a change in one of the FilterRows.
321317
322 arguments:318 arguments:
323 store_iter: the iter pointing the the row in the treeview to test319 store_iter: the iter pointing the the row in the treeview to test
324 model: the treeview model containing the rows being tested320 model: the treeview model containing the rows being tested
325321
326 """322 """
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())
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)
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)
330326
331 orig_val = model.get_value(store_iter.copy(), treeview_col)327 orig_val = model.get_value(store_iter.copy(), treeview_col)
332 return filter_widget.filter(orig_val)328 return filter_widget.filter(orig_val)
333329
334class BlankFilterBox( gtk.HBox):330class BlankFilterBox( Gtk.HBox):
335 """BlankFilterBox provides a base class for FilterCombos, as331 """BlankFilterBox provides a base class for FilterCombos, as
336 well as an empty combo that can be used without subclassing332 well as an empty combo that can be used without subclassing
337 by calling BlankFilterBox.append333 by calling BlankFilterBox.append
338
339 """
340 __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
341 (gobject.TYPE_PYOBJECT,)),
342 }
343
344
345 def __init__(self):
346 """create a BlankFilterBox
347
348 """
349
350 gtk.HBox.__init__(self,False)
351 self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT)
352 self.combo = gtk.ComboBox(self.__combo_store)
353 cell = gtk.CellRendererText()
354 self.combo.pack_start(cell, True)
355 self.combo.add_attribute(cell, 'text', 0)
356 self.combo.show()
357 self.combo.connect("changed",self.__changed)
358 self.entry = gtk.Entry()
359 self.entry.show()
360 self.entry.connect("changed",self.__changed)
361 self.pack_start(self.combo, False, False)
362 self.pack_start(self.entry)
363
364 def filter(self, orig_val):
365 if self.combo.get_active() == -1:
366 return True
367 filt_iter = self.combo.get_model().get_iter(self.combo.get_active())
368 filt_func = self.combo.get_model().get_value(filt_iter,1)
369 target_val = self.entry.get_text()
370 return filt_func(orig_val, self.entry.get_text())
371
372 def __changed(self, widget, data=None):
373 self.emit("changed",data)
374
375 def append(self, text, func):
376 """append: adds a row to the FilterCombo that includes a
377 string to display in the combo, and a function to determine
378 if a row should displayed or hidden by the filter.
379
380 func should take a value indicated by text, and a value entered by
381 the user in the supplied gtk.TextEntry, and return True if the
382 row should be displayed or False if it should be hidden.
383334
384 """335 """
385336 __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
386 self.__combo_store.append([text, func])337 (GObject.TYPE_PYOBJECT,)),
338 }
339
340
341 def __init__(self):
342 """create a BlankFilterBox
343
344 """
345
346 Gtk.HBox.__init__(self,False)
347 self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT)
348 self.combo = Gtk.ComboBox.new_with_model(self.__combo_store)
349 cell = Gtk.CellRendererText()
350 self.combo.pack_start(cell, True)
351 self.combo.add_attribute(cell, 'text', 0)
352 self.combo.show()
353 self.combo.connect("changed",self.__changed)
354 self.entry = Gtk.Entry()
355 self.entry.show()
356 self.entry.connect("changed",self.__changed)
357 self.pack_start(self.combo, False, False, 0)
358 self.pack_start(self.entry, True, True, 0)
359
360 def filter(self, orig_val):
361 if self.combo.get_active() == -1:
362 return True
363 filt_iter = self.combo.get_model().get_iter(self.combo.get_active())
364 filt_func = self.combo.get_model().get_value(filt_iter,1)
365 target_val = self.entry.get_text()
366 return filt_func(orig_val, self.entry.get_text())
367
368 def __changed(self, widget, data=None):
369 self.emit("changed",data)
370
371 def append(self, text, func):
372 """append: adds a row to the FilterCombo that includes a
373 string to display in the combo, and a function to determine
374 if a row should displayed or hidden by the filter.
375
376 func should take a value indicated by text, and a value entered by
377 the user in the supplied Gtk.TextEntry, and return True if the
378 row should be displayed or False if it should be hidden.
379
380 """
381
382 self.__combo_store.append([text, func])
387383
388class StringFilterBox( BlankFilterBox ):384class StringFilterBox( BlankFilterBox ):
389 """StringFilterBox: A default string filter class for use in a FilterRow.385 """StringFilterBox: A default string filter class for use in a FilterRow.
390386
391 Lets the user specify if the row should be displayed based on387 Lets the user specify if the row should be displayed based on
392 containing, not containing, starting with, or ending with a user specified388 containing, not containing, starting with, or ending with a user specified
393 string.389 string.
394390
395
396 """391 """
397 def __init__(self):392
398 """create a StringFilterBox.393 def __init__(self):
399394 """create a StringFilterBox.
400 """395
401396 """
402 BlankFilterBox.__init__(self)397
403 self.append(_("contains"),self.contains)398 BlankFilterBox.__init__(self)
404 self.append(_("does not contain"),self.not_contains)399 self.append(_("contains"),self.contains)
405 self.append(_("starts with"),self.starts_with)400 self.append(_("does not contain"),self.not_contains)
406 self.append(_("ends with"),self.ends_with)401 self.append(_("starts with"),self.starts_with)
407402 self.append(_("ends with"),self.ends_with)
408 def contains(self, orig_val, target_val):403
409 if len(self.entry.get_text()) == 0 or orig_val is None:404 def contains(self, orig_val, target_val):
410 return True405 if len(self.entry.get_text()) == 0 or orig_val is None:
411 return orig_val.find(target_val) > -1406 return True
412407 return orig_val.find(target_val) > -1
413 def not_contains(self, orig_val, target_val):408
414 if len(target_val) == 0 or orig_val is None:409 def not_contains(self, orig_val, target_val):
415 return True410 if len(target_val) == 0 or orig_val is None:
416 return orig_val.find(target_val) == -1411 return True
417412 return orig_val.find(target_val) == -1
418 def starts_with(self, orig_val, target_val):413
419 if len(target_val) == 0 or orig_val is None:414 def starts_with(self, orig_val, target_val):
420 return True415 if len(target_val) == 0 or orig_val is None:
421 return orig_val.startswith(target_val)416 return True
422417 return orig_val.startswith(target_val)
423 def ends_with(self, orig_val, target_val):418
424 if len(target_val) == 0 or orig_val is None:419 def ends_with(self, orig_val, target_val):
425 return True420 if len(target_val) == 0 or orig_val is None:
426 return orig_val.endswith(target_val)421 return True
422 return orig_val.endswith(target_val)
427423
428424
429class TagsFilterBox( BlankFilterBox ):425class TagsFilterBox( BlankFilterBox ):
430 """TagsFilterBox: A default tag filter class for use in a FilterRow.426 """TagsFilterBox: A default tag filter class for use in a FilterRow.
431427
432 Lets the user specify if the row should be displayed based on428 Lets the user specify if the row should be displayed based on
433 containing a one tag or all tags. Assumes tags are seperated by429 containing a one tag or all tags. Assumes tags are seperated by
@@ -435,382 +431,382 @@
435431
436 """432 """
437433
438 def __init__(self):434 def __init__(self):
439 BlankFilterBox.__init__(self)435 BlankFilterBox.__init__(self)
440 self.append(_("has any of these tags"), self._filter_any)436 self.append(_("has any of these tags"), self._filter_any)
441 self.append(_("has all of these tags"), self._filter_all)437 self.append(_("has all of these tags"), self._filter_all)
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)
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)
444440
445 def _filter_any(self, orig_val, target_val):441 def _filter_any(self, orig_val, target_val):
446 """442 """
447 _filter_any: filter function that hides rows443 _filter_any: filter function that hides rows
448 if none of the tags supplied in "bugs_tags_s" are found444 if none of the tags supplied in "bugs_tags_s" are found
449 in the gtk.TextEntry.445 in the Gtk.TextEntry.
450446
451 Do not call directly447 Do not call directly
452448
453 """449 """
454450
455 if len(target_val) == 0:451 if len(target_val) == 0:
456 return True452 return True
457453
458 tags_on_bug = orig_val.split()454 tags_on_bug = orig_val.split()
459 tags_in_filter = target_val.split()455 tags_in_filter = target_val.split()
460456
461 for tag in tags_in_filter:457 for tag in tags_in_filter:
462 if tag in tags_on_bug:458 if tag in tags_on_bug:
463 return True459 return True
464 return False460 return False
465461
466 def _filter_all(self, orig_val, target_val):462 def _filter_all(self, orig_val, target_val):
467 """463 """
468 _filter_any: filter function that hides rows464 _filter_any: filter function that hides rows
469 if not all of the tags supplied in "bugs_tags_s" are found465 if not all of the tags supplied in "bugs_tags_s" are found
470 in the gtk.TextEntry.466 in the Gtk.TextEntry.
471467
472 Do not call directly468 Do not call directly
473469
474 """470 """
475 if len(target_val) == 0:471 if len(target_val) == 0:
476 return True472 return True
477473
478 tags_on_bug = orig_val.split()474 tags_on_bug = orig_val.split()
479 tags_in_filter = self.entry.get_text().split()475 tags_in_filter = self.entry.get_text().split()
480476
481 for tag in tags_in_filter:477 for tag in tags_in_filter:
482 if tag not in tags_on_bug:478 if tag not in tags_on_bug:
483 return False479 return False
484 return True480 return True
485481
486 def _filter_not(self, orig_val, target_val):482 def _filter_not(self, orig_val, target_val):
487 """483 """
488 _filter_not: filter function that hides rows484 _filter_not: filter function that hides rows
489 if one of the tags supplied in "bugs_tags_s" are found485 if one of the tags supplied in "bugs_tags_s" are found
490 in the gtk.TextEntry.486 in the Gtk.TextEntry.
491487
492 Do not call directly488 Do not call directly
493489
494 """490 """
495 if len(target_val) == 0:491 if len(target_val) == 0:
496 return True492 return True
497493
498 tags_on_bug = orig_val.split()494 tags_on_bug = orig_val.split()
499 tags_in_filter = target_val.split()495 tags_in_filter = target_val.split()
500 496
501 for tag in tags_in_filter:497 for tag in tags_in_filter:
502 if tag not in tags_on_bug:498 if tag not in tags_on_bug:
503 return True499 return True
504 return False500 return False
505501
506 def _filter_not_all(self, orig_val, target_val):502 def _filter_not_all(self, orig_val, target_val):
507 """503 """
508 _filter_not all: filter function that hides rows504 _filter_not all: filter function that hides rows
509 if all of the tags supplied in "bugs_tags_s" are found505 if all of the tags supplied in "bugs_tags_s" are found
510 in the gtk.TextEntry.506 in the Gtk.TextEntry.
511507
512 Do not call directly508 Do not call directly
513509
514 """510 """
515 if len(self.entry.get_text()) == 0:511 if len(self.entry.get_text()) == 0:
516 return True512 return True
517513
518 tags_on_bug = orig_val.split()514 tags_on_bug = orig_val.split()
519 tags_in_filter = target_val.split()515 tags_in_filter = target_val.split()
520516
521 for tag in tags_in_filter:517 for tag in tags_in_filter:
522 if tag in tags_on_bug:518 if tag in tags_on_bug:
523 return False519 return False
524 return True520 return True
525 521
526class IntegerFilterBox( gtk.HBox ):522class IntegerFilterBox( Gtk.HBox ):
527 """523 """
528524
529 """525 """
530 __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,526 __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
531 (gobject.TYPE_PYOBJECT,)),527 (GObject.TYPE_PYOBJECT,)),
532 }528 }
533529
534 def __init__(self):530 def __init__(self):
535 gtk.HBox.__init__(self, False, 10)531 Gtk.HBox.__init__(self, False, 10)
536 self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT)532 self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT)
537 self.combo = gtk.ComboBox(self.__combo_store)533 self.combo = Gtk.ComboBox.new_with_model(self.__combo_store)
538 cell = gtk.CellRendererText()534 cell = Gtk.CellRendererText()
539 self.combo.pack_start(cell, True)535 self.combo.pack_start(cell, True)
540 self.combo.add_attribute(cell, 'text', 0)536 self.combo.add_attribute(cell, 'text', 0)
541 self.combo.show()537 self.combo.show()
542 self.combo.connect("changed",self.__changed)538 self.combo.connect("changed",self.__changed)
543 adj = gtk.Adjustment(0,-1000000000,1000000000,1)539 adj = Gtk.Adjustment(0,-1000000000,1000000000,1)
544 540
545 self.spinner = gtk.SpinButton(adj,1,0)541 self.spinner = Gtk.SpinButton.new(adj,1,0)
546 self.spinner.set_activates_default(True)542 self.spinner.set_activates_default(True)
547 self.spinner.show()543 self.spinner.show()
548 self.spinner.set_numeric(True)544 self.spinner.set_numeric(True)
549545
550 self.spinner.connect("value-changed",self.__changed)546 self.spinner.connect("value-changed",self.__changed)
551 self.pack_start(self.combo, False, False)547 self.pack_start(self.combo, False, False, 0)
552 self.pack_start(self.spinner)548 self.pack_start(self.spinner, True, True, 0)
553549
554 self.__combo_store.append(["=",self._equals])550 self.__combo_store.append(["=",self._equals])
555 self.__combo_store.append(["<",self._less_than])551 self.__combo_store.append(["<",self._less_than])
556 self.__combo_store.append([">",self._greater_than])552 self.__combo_store.append([">",self._greater_than])
557 self.__combo_store.append(["<=",self._less_than_equals])553 self.__combo_store.append(["<=",self._less_than_equals])
558 self.__combo_store.append([">=",self._greater_than_equals])554 self.__combo_store.append([">=",self._greater_than_equals])
559555
560 def __changed(self, widget, data=None):556 def __changed(self, widget, data=None):
561 self.emit("changed",data)557 self.emit("changed",data)
562558
563 def filter(self, orig_val):559 def filter(self, orig_val):
564 if self.combo.get_active() == -1:560 if self.combo.get_active() == -1:
565 return True561 return True
566562
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())
568 filt_func = self.combo.get_model().get_value(filt_iter,1)564 filt_func = self.combo.get_model().get_value(filt_iter,1)
569565
570 try:566 try:
571 target_val = int(self.spinner.get_value_as_int())567 target_val = int(self.spinner.get_value_as_int())
572568 except Exception, inst:
573569 print inst
574 except Exception, inst:570 return False
575 print inst571
576 return False572 return filt_func(orig_val, target_val)
577573
578 return filt_func(orig_val, target_val)574 def _equals(self, orig_val, target_val):
579575 if orig_val == "":
580 def _equals(self, orig_val, target_val):576 return False
581 if orig_val == "":577 return int(orig_val) == target_val
582 return False578
583 return int(orig_val) == target_val579 def _less_than(self, orig_val, target_val):
584580 if orig_val == "":
585 def _less_than(self, orig_val, target_val):581 return False
586 if orig_val == "":582 return int(orig_val) < target_val
587 return False583
588 return int(orig_val) < target_val584 def _greater_than(self, orig_val, target_val):
589585 if orig_val == "":
590 def _greater_than(self, orig_val, target_val):586 return False
591 if orig_val == "":587 return int(orig_val) > target_val
592 return False588
593 return int(orig_val) > target_val589 def _less_than_equals(self, orig_val, target_val):
594590 if orig_val == "":
595 def _less_than_equals(self, orig_val, target_val):591 return False
596 if orig_val == "":592 return int(orig_val) <= target_val
597 return False593
598 return int(orig_val) <= target_val594 def _greater_than_equals(self, orig_val, target_val):
599595 if orig_val == "":
600 def _greater_than_equals(self, orig_val, target_val):596 return False
601 if orig_val == "":597 return int(orig_val) >= target_val
602 return False598
603 return int(orig_val) >= target_val599class DateFilterBox( Gtk.HBox ):
604600 """DateFilterCombo: A default date filter class for use in a FilterRow.
605class DateFilterBox( gtk.HBox ):
606 """DateFilterCombo: A default date filter class for use in a FilterRow.
607601
608 Lets the user specify if the row should be displayed based on602 Lets the user specify if the row should be displayed based on
609 the settings in a date widget.603 the settings in a date widget.
610604
611 """605 """
612 __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,606 __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
613 (gobject.TYPE_PYOBJECT,)),607 (GObject.TYPE_PYOBJECT,)),
614 }608 }
615609
616 def __init__(self):610 def __init__(self):
617 """create a CheckFilterCombo611 """create a CheckFilterCombo
618 612
619 """613 """
620 gtk.HBox.__init__(self, False, 10)614 Gtk.HBox.__init__(self, False, 10)
621615
622 self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT)616 self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT)
623 self.combo = gtk.ComboBox(self.__combo_store)617 self.combo = Gtk.ComboBox.new_with_model(self.__combo_store)
624 cell = gtk.CellRendererText()618 cell = Gtk.CellRendererText()
625 self.combo.pack_start(cell, False)619 self.combo.pack_start(cell, False)
626 self.combo.add_attribute(cell, 'text', 0)620 self.combo.add_attribute(cell, 'text', 0)
627 self.combo.show()621 self.combo.show()
628 self.combo.connect("changed",self.__changed)622 self.combo.connect("changed",self.__changed)
629623
630 self.__combo_store.append([ _("before"),self.before ])624 self.__combo_store.append([ _("before"),self.before ])
631 self.__combo_store.append([ _("on or before"),self.on_before ])625 self.__combo_store.append([ _("on or before"),self.on_before ])
632 self.__combo_store.append([ _("on"), self.on_date ])626 self.__combo_store.append([ _("on"), self.on_date ])
633 self.__combo_store.append([ _("on or after"),self.on_after ])627 self.__combo_store.append([ _("on or after"),self.on_after ])
634 self.__combo_store.append([ _("after"),self.after ])628 self.__combo_store.append([ _("after"),self.after ])
635629
636 self.calendar = gtk.Calendar()630 self.calendar = Gtk.Calendar()
637 self.calendar.show()631 self.calendar.show()
638 self.calendar.connect("day-selected", self.__changed)632 self.calendar.connect("day-selected", self.__changed)
639 vb = gtk.VBox(False, 5)633 vb = Gtk.VBox(False, 5)
640 vb.show()634 vb.show()
641 vb.pack_start(self.combo, True, False)635 vb.pack_start(self.combo, True, False, 0)
642 self.pack_start(vb, False, False)636 self.pack_start(vb, False, False, 0)
643 self.pack_start(self.calendar, False, False)637 self.pack_start(self.calendar, False, False, 0)
644638
645 def before(self, orig_val):639 def before(self, orig_val):
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())
647 if stored_date is None:641 if stored_date is None:
648 return False642 return False
649 return stored_date < target_date643 return stored_date < target_date
650644
651 def on_before(self, orig_val):645 def on_before(self, orig_val):
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())
653 if stored_date is None:647 if stored_date is None:
654 return False648 return False
655 return stored_date <= target_date649 return stored_date <= target_date
656650
657 def on_date(self, orig_val):651 def on_date(self, orig_val):
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())
659 if stored_date is None:653 if stored_date is None:
660 return False654 return False
661 return stored_date == target_date655 return stored_date == target_date
662656
663 def on_after(self, orig_val):657 def on_after(self, orig_val):
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())
665 if stored_date is None:659 if stored_date is None:
666 return False660 return False
667 return stored_date >= target_date661 return stored_date >= target_date
668662
669 def after(self, orig_val):663 def after(self, orig_val):
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())
671 if stored_date is None:665 if stored_date is None:
672 return False666 return False
673 return stored_date > target_date667 return stored_date > target_date
674668
675 def __get_dates(self, orig_val, target_date):669 def __get_dates(self, orig_val, target_date):
676 target_date = self.calendar.get_date()670 target_date = self.calendar.get_date()
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]))
678 if orig_val is not None and len(orig_val) > 0:672 if orig_val is not None and len(orig_val) > 0:
679 p = orig_val.split("-")673 p = orig_val.split("-")
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]))
681 else:675 else:
682 stored_date = None676 stored_date = None
683 return (stored_date, target_date)677 return (stored_date, target_date)
684678
685 def filter(self, orig_val):679 def filter(self, orig_val):
686 if self.combo.get_active() == -1:680 if self.combo.get_active() == -1:
687 return True681 return True
688682
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())
690 filt_func = self.combo.get_model().get_value(filt_iter,1)684 filt_func = self.combo.get_model().get_value(filt_iter,1)
691 return filt_func(orig_val)685 return filt_func(orig_val)
692686
693 def __changed(self, widget, data=None):687 def __changed(self, widget, data=None):
694 self.emit("changed",data)688 self.emit("changed",data)
695689
696690class CheckFilterBox( Gtk.HBox ):
697class CheckFilterBox( gtk.HBox ):691 """CheckFilterCombo: A default checkbox filter class for use in a FilterRow.
698 """CheckFilterCombo: A default checkbox filter class for use in a FilterRow.
699692
700 Lets the user specify if the row should be displayed based on693 Lets the user specify if the row should be displayed based on
701 whether a Checkbox is active, inactive, or not set.694 whether a Checkbox is active, inactive, or not set.
702695
703 """696 """
704 __gsignals__ = {'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,697 __gsignals__ = {'changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE,
705 (gobject.TYPE_PYOBJECT,)),698 (GObject.TYPE_PYOBJECT,)),
706 }699 }
707700
708 def __init__(self):701 def __init__(self):
709 """create a CheckFilterCombo702 """create a CheckFilterCombo
710 703
711 """704 """
712 gtk.HBox.__init__(self, False, 10)705 Gtk.HBox.__init__(self, False, 10)
713706
714 self.__combo_store = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_PYOBJECT)707 self.__combo_store = Gtk.ListStore(GObject.TYPE_STRING,GObject.TYPE_PYOBJECT)
715 self.combo = gtk.ComboBox(self.__combo_store)708 self.combo = Gtk.ComboBox.new_with_model(self.__combo_store)
716 cell = gtk.CellRendererText()709 cell = Gtk.CellRendererText()
717 self.combo.pack_start(cell, True)710 self.combo.pack_start(cell, True)
718 self.combo.add_attribute(cell, 'text', 0)711 self.combo.add_attribute(cell, 'text', 0)
719 self.combo.show()712 self.combo.show()
720 self.combo.connect("changed",self.__changed)713 self.combo.connect("changed",self.__changed)
721714
722 self.__combo_store.append([ _("checked"),self.filter_checked ])715 self.__combo_store.append([ _("checked"),self.filter_checked ])
723 self.__combo_store.append([ _("not Checked"),self.filter_not_checked ])716 self.__combo_store.append([ _("not Checked"),self.filter_not_checked ])
724 self.__combo_store.append([ _("unset"), self.filter_unset ])717 self.__combo_store.append([ _("unset"), self.filter_unset ])
725718
726 self.pack_start(self.combo, False, False)719 self.pack_start(self.combo, False, False, 0)
727720
728 def filter(self, orig_val):721 def filter(self, orig_val):
729 if self.combo.get_active() == -1:722 if self.combo.get_active() == -1:
730 return True723 return True
731724
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())
733 filt_func = self.combo.get_model().get_value(filt_iter,1)726 filt_func = self.combo.get_model().get_value(filt_iter,1)
734 return filt_func(orig_val)727 return filt_func(orig_val)
735728
736 def filter_checked(self, orig_val):729 def filter_checked(self, orig_val):
737 return orig_val == 1730 return orig_val == 1
738731
739 def filter_not_checked(self, orig_val):732 def filter_not_checked(self, orig_val):
740 return orig_val == 0733 return orig_val == 0
741734
742 def filter_unset(self, orig_val):735 def filter_unset(self, orig_val):
743 return orig_val == -1736 return orig_val == -1
744737
745 def __changed(self, widget, data=None):738 def __changed(self, widget, data=None):
746 self.emit("changed",data)739 self.emit("changed",data)
747740
748741
749class NumericFilterBox( BlankFilterBox ):742class NumericFilterBox( BlankFilterBox ):
750 """NumericFilterCombo: A default number filter class for use in a FilterRow.743 """NumericFilterCombo: A default number filter class for use in a FilterRow.
751744
752 Lets the user specify if the row should be displayed based on numeric745 Lets the user specify if the row should be displayed based on numeric
753 relationships to a number specified by the user.746 relationships to a number specified by the user.
754747
755 """748 """
756749
757750
758 def __init__(self):751 def __init__(self):
759 """create a NumericFilterCombo752 """create a NumericFilterCombo
760753
761 """754 """
762 BlankFilterBox.__init__( self )755 BlankFilterBox.__init__( self )
763 self.append("=",self._equals )756 self.append("=",self._equals )
764 self.append("<",self._less_than )757 self.append("<",self._less_than )
765 self.append(">",self._greater_than )758 self.append(">",self._greater_than )
766 self.append("<=",self._less_than_equals)759 self.append("<=",self._less_than_equals)
767 self.append(">=",self._greater_than_equals )760 self.append(">=",self._greater_than_equals )
768761
769 def _equals(self, orig_val):762 def _equals(self, orig_val):
770 try:763 try:
771 return float(orig_val) == float(self.entry.get_text())764 return float(orig_val) == float(self.entry.get_text())
772 except:765 except:
773 return True766 return True
774767
775 def _less_than(self, orig_val):768 def _less_than(self, orig_val):
776 try:769 try:
777 return float(orig_val) < float(self.entry.get_text())770 return float(orig_val) < float(self.entry.get_text())
778 except:771 except:
779 return True772 return True
780773
781 def _greater_than(self, orig_val):774 def _greater_than(self, orig_val):
782 try:775 try:
783 return float(orig_val) > float(self.entry.get_text())776 return float(orig_val) > float(self.entry.get_text())
784 except:777 except:
785 return True778 return True
786779
787 def _less_than_equals(self, orig_val):780 def _less_than_equals(self, orig_val):
788 try:781 try:
789 return float(orig_val) <= float(self.entry.get_text())782 return float(orig_val) <= float(self.entry.get_text())
790 except:783 except:
791 return True784 return True
792785
793 def _greater_than_equals(self, orig_val):786 def _greater_than_equals(self, orig_val):
794 try:787 try:
795 return float(orig_val) >= float(self.entry.get_text())788 return float(orig_val) >= float(self.entry.get_text())
796 except:789 except:
797 return True790 return True
791
792
793# Test case begins here.
798794
799def __delete_test(button, grid):795def __delete_test(button, grid):
800 grid.remove_selected_rows(delete=True)796 grid.remove_selected_rows(delete=True)
801797
802if __name__ == "__main__":798if __name__ == "__main__":
803 """creates a test CouchGrid if called directly"""799 """creates a test DictionaryGrid and GridFilter if called directly"""
804 from couch_grid import CouchGrid800 from dictionary_grid import DictionaryGrid
805801
806 #create and show a test window802 #create and show a test window
807 win = gtk.Window(gtk.WINDOW_TOPLEVEL)803 win = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
808 win.set_title("DictionaryGrid Test Window")804 win.set_title("CouchGrid Test Window")
809 win.connect("destroy",gtk.main_quit)805 win.connect("destroy",Gtk.main_quit)
810 win.show()806 win.show()
811807
812 #create a top level container808 #create a top level container
813 vbox = gtk.VBox(False, 10)809 vbox = Gtk.VBox(False, 10)
814 vbox.show()810 vbox.show()
815 win.add(vbox)811 win.add(vbox)
816812
@@ -821,9 +817,7 @@
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"},
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"}]
823819
824 database_name = "couch_widget_test"820 grid = DictionaryGrid(dictionaries=dicts, editable=True)
825 record_type = "couch_grid_filter_test"
826 grid = CouchGrid(database_name, record_type=record_type, dictionaries=dicts, editable=True)
827 grid.columns["tags"].set_title("modified title")821 grid.columns["tags"].set_title("modified title")
828 grid.show()822 grid.show()
829823
@@ -831,15 +825,15 @@
831 hints = {}825 hints = {}
832 filt = GridFilter(grid,hints)826 filt = GridFilter(grid,hints)
833 filt.show()827 filt.show()
834 vbox.pack_start(filt, False, False)828 vbox.pack_start(filt, False, False, 0)
835 vbox.pack_end(grid, True, True)829 vbox.pack_end(grid, True, True, 0)
836830
837 delete_button = gtk.Button("Delete Selected")831 delete_button = Gtk.Button("Delete Selected")
838 delete_button.connect("clicked",__delete_test,grid)832 delete_button.connect("clicked",__delete_test,grid)
839 delete_button.show()833 delete_button.show()
840834
841835
842 vbox.pack_start(delete_button,False, False)836 vbox.pack_start(delete_button,False, False, 0)
843 gtk.main()837 Gtk.main()
844838
845839
846840
=== modified file 'quickly/widgets/media_player_box.py'
--- quickly/widgets/media_player_box.py 2011-01-17 03:40:02 +0000
+++ quickly/widgets/media_player_box.py 2012-03-06 08:52:21 +0000
@@ -50,14 +50,14 @@
50#You can add Widgets to the MediaPlayerBox simply by packing them in50#You can add Widgets to the MediaPlayerBox simply by packing them in
51player.pack_start(my_widget, False, False)51player.pack_start(my_widget, False, False)
5252
53#You can get a reference to the controls, which are a gtk.Toolbar53#You can get a reference to the controls, which are a Gtk.Toolbar
54mybutton = gtk.ToolButton()54mybutton = Gtk.ToolButton()
55player.controls.insert(mybutton, 0)55player.controls.insert(mybutton, 0)
5656
57#You can access the playbutton, slider, or time label directly as well57#You can access the playbutton, slider, or time label directly as well
58player.play_button.hide()#a gtk.ToggleToolButton58player.play_button.hide()#a Gtk.ToggleToolButton
59player.slider.hide()#a gtk.HScale59player.slider.hide()#a Gtk.HScale
60player.time_label.hide()#a gtk.Label60player.time_label.hide()#a Gtk.Label
6161
62#If you want access to all the gstreamer knobs and dials, you can just62#If you want access to all the gstreamer knobs and dials, you can just
63#get a reference to the playbin (see gstreamer documentation for details.63#get a reference to the playbin (see gstreamer documentation for details.
@@ -67,13 +67,13 @@
67player.playbin.emit(signal_name)67player.playbin.emit(signal_name)
6868
69Extending69Extending
70A WebCamBox is gtk.VBox70A WebCamBox is Gtk.VBox
71A WebCamBox is a gtk.VBox that contains a gtk.DrawingArea for displaying71A WebCamBox is a Gtk.VBox that contains a Gtk.DrawingArea for displaying
72video output, and a thin wrapper around a playbin, which is a gstreamer72video output, and a thin wrapper around a playbin, which is a gstreamer
73pipleine sublcass that provides all the media playing functionality.73pipleine sublcass that provides all the media playing functionality.
7474
75To add GUI elements simple, create them and pack them into MediaPlayerBox, since75To add GUI elements simple, create them and pack them into MediaPlayerBox, since
76it's just a gtk.VBox76it's just a Gtk.VBox
7777
78Similarly, to add to or change the media player functionality, modify properties on78Similarly, to add to or change the media player functionality, modify properties on
79the playbin. You may also want to overide _on_message and/or _on_sync_message79the playbin. You may also want to overide _on_message and/or _on_sync_message
@@ -81,18 +81,21 @@
8181
82"""82"""
8383
84
84import sys85import sys
85import os86import os
86import gtk87from gi.repository import Gtk
88from gi.repository import Gdk
89from gi.repository import GdkX11
90from gi.repository import GObject
87import gst91import gst
88import datetime92import datetime
89import gobject
9093
91import gettext94import gettext
92from gettext import gettext as _95from gettext import gettext as _
93gettext.textdomain('quickly-widgets')96gettext.textdomain('quickly-widgets')
9497
95class MediaPlayerBox(gtk.VBox):98class MediaPlayerBox(Gtk.VBox):
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.
97 It works for video and sound files.100 It works for video and sound files.
98101
@@ -102,13 +105,13 @@
102 """Creates a MediaPlayerBox, Note that this does not start media.105 """Creates a MediaPlayerBox, Note that this does not start media.
103 For that, set the uri property and then call play().106 For that, set the uri property and then call play().
104 107
105 This function has no arguments108 This function has no argumentsf
106109
107 """110 """
108 gtk.VBox.__init__(self, False, 5)111 Gtk.VBox.__init__(self, False, 5)
109 self.video_window = gtk.DrawingArea()112 self.video_window = Gtk.DrawingArea()
110 self.video_window.connect("realize",self.__on_video_window_realized)113 self.video_window.connect("realize",self.__on_video_window_realized)
111# self.pack_start(self.video_window, True, True)114# self.pack_start(self.video_window, True, True, 0)
112 self.video_window.show()115 self.video_window.show()
113 self.connect("destroy", self.on_destroy)116 self.connect("destroy", self.on_destroy)
114117
@@ -121,21 +124,21 @@
121 self.__uri = ""124 self.__uri = ""
122 self.realized = False125 self.realized = False
123126
124 self.controls = gtk.Toolbar()127 self.controls = Gtk.Toolbar()
125 if show_controls:128 if show_controls:
126 self.controls.show()129 self.controls.show()
127 self.pack_start(self.controls, False, False)130 self.pack_start(self.controls, False, False, 0)
128 self.pack_start(self.video_window, True, True)131 self.pack_start(self.video_window, True, True, 0)
129132
130 self.play_button = gtk.ToggleToolButton()133 self.play_button = Gtk.ToggleToolButton()
131 self.play_button.set_stock_id(gtk.STOCK_MEDIA_PLAY)134 self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PLAY)
132 self.play_button.show()135 self.play_button.show()
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)
134 self.controls.add(self.play_button)137 self.controls.add(self.play_button)
135 138
136 item = gtk.ToolItem()139 item = Gtk.ToolItem()
137 item.show()140 item.show()
138 self.slider = gtk.HScale()141 self.slider = Gtk.HScale()
139 self.slider_changed_handler = None142 self.slider_changed_handler = None
140 self.slider.set_draw_value(False)143 self.slider.set_draw_value(False)
141 self.slider.set_increments(10,60)144 self.slider.set_increments(10,60)
@@ -145,9 +148,9 @@
145 item.add(self.slider)148 item.add(self.slider)
146 self.controls.insert(item, -1)149 self.controls.insert(item, -1)
147150
148 item2 = gtk.ToolItem()151 item2 = Gtk.ToolItem()
149 item2.show()152 item2.show()
150 self.time_label = gtk.Label("")153 self.time_label = Gtk.Label("")
151 self.time_label.show()154 self.time_label.show()
152 item2.add(self.time_label)155 item2.add(self.time_label)
153 self.controls.insert(item2, -1)156 self.controls.insert(item2, -1)
@@ -175,7 +178,7 @@
175 self.slider.set_sensitive(True)178 self.slider.set_sensitive(True)
176 self._reformat_slider()179 self._reformat_slider()
177 self._start_slider_updates()180 self._start_slider_updates()
178 self.play_button.set_stock_id(gtk.STOCK_MEDIA_PAUSE)181 self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PAUSE)
179 self._set_play_button_active(True)182 self._set_play_button_active(True)
180183
181 def pause(self):184 def pause(self):
@@ -186,11 +189,12 @@
186 This function has no arguments189 This function has no arguments
187 190
188 """191 """
192
189 self.playbin.set_state(gst.STATE_PAUSED)193 self.playbin.set_state(gst.STATE_PAUSED)
190 self.slider.set_sensitive(True)194 self.slider.set_sensitive(True)
191 self._reformat_slider()195 self._reformat_slider()
192 self.slider.set_sensitive(True)196 self.slider.set_sensitive(True)
193 self.play_button.set_stock_id(gtk.STOCK_MEDIA_PLAY)197 self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PLAY)
194 self._set_play_button_active(False)198 self._set_play_button_active(False)
195199
196 def stop(self):200 def stop(self):
@@ -203,7 +207,7 @@
203207
204 self.playbin.set_state(gst.STATE_NULL)208 self.playbin.set_state(gst.STATE_NULL)
205 self.slider.set_sensitive(False)209 self.slider.set_sensitive(False)
206 self.play_button.set_stock_id(gtk.STOCK_MEDIA_PLAY)210 self.play_button.set_stock_id(Gtk.STOCK_MEDIA_PLAY)
207 self._set_play_button_active(False)211 self._set_play_button_active(False)
208 self.slider.set_value(0)212 self.slider.set_value(0)
209213
@@ -305,7 +309,7 @@
305 if self.playbin.get_state()[1] == gst.STATE_NULL:309 if self.playbin.get_state()[1] == gst.STATE_NULL:
306 self.slider.set_range(0, 0)310 self.slider.set_range(0, 0)
307 else:311 else:
308 gobject.idle_add(self._set_slider_range)312 GObject.idle_add(self._set_slider_range)
309313
310 def _set_slider_range(self):314 def _set_slider_range(self):
311 dur = self.duration315 dur = self.duration
@@ -313,16 +317,16 @@
313 return True317 return True
314 else:318 else:
315 self._duration_time_str = self._formatted_time(dur)319 self._duration_time_str = self._formatted_time(dur)
316 gtk.gdk.threads_enter()320 Gdk.threads_enter()
317 self.slider.set_range(0, dur)321 self.slider.set_range(0, dur)
318 gtk.gdk.threads_leave()322 Gdk.threads_leave()
319 return False323 return False
320324
321 def _start_slider_updates(self):325 def _start_slider_updates(self):
322 if self.playbin.get_state()[1] == gst.STATE_NULL:326 if self.playbin.get_state()[1] == gst.STATE_NULL:
323 self.slide.set_value(0)327 self.slide.set_value(0)
324 else:328 else:
325 gobject.timeout_add(1000, self._set_slider_position)329 GObject.timeout_add(1000, self._set_slider_position)
326330
327 def _set_slider_position(self):331 def _set_slider_position(self):
328 if self._slider_changed_handler is not None:332 if self._slider_changed_handler is not None:
@@ -395,7 +399,7 @@
395 if message_name == "prepare-xwindow-id":399 if message_name == "prepare-xwindow-id":
396 imagesink = message.src400 imagesink = message.src
397 imagesink.set_property("force-aspect-ratio", True)401 imagesink.set_property("force-aspect-ratio", True)
398 imagesink.set_xwindow_id(self.video_window.window.xid)402 imagesink.set_xwindow_id(self.video_window.get_window().get_xid())
399403
400 def __on_video_window_realized(self, widget, data=None):404 def __on_video_window_realized(self, widget, data=None):
401 """__on_video_window_realized - internal signal handler, used405 """__on_video_window_realized - internal signal handler, used
@@ -407,16 +411,16 @@
407 self._set_video_window_id()411 self._set_video_window_id()
408412
409 def _set_video_window_id(self):413 def _set_video_window_id(self):
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:
411 x = self.video_window.window.xid415 x = self.video_window.get_window().get_xid()
412 self.realized = True416 self.realized = True
413417
414 def on_destroy(self, widget, data=None):418 def on_destroy(self, widget, data=None):
415 #clean up the camera before exiting419 #clean up the camera before exiting
416 self.playbin.set_state(gst.STATE_NULL)420 self.playbin.set_state(gst.STATE_NULL)
417421
418 __gsignals__ = {'end-of-file' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,422 __gsignals__ = {'end-of-file' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
419 (gobject.TYPE_PYOBJECT,)),423 (GObject.TYPE_PYOBJECT,)),
420 } 424 }
421425
422def __seek_func(sender, mb):426def __seek_func(sender, mb):
@@ -445,16 +449,15 @@
445449
446if __name__ == "__main__":450if __name__ == "__main__":
447 """creates a test WebCamBox"""451 """creates a test WebCamBox"""
448 import quickly.prompts
449452
450 #create and show a test window453 #create and show a test window
451 win = gtk.Window(gtk.WINDOW_TOPLEVEL)454 win = Gtk.Window()
452 win.set_title("WebCam Test Window")455 win.set_title("WebCam Test Window")
453 win.connect("destroy",gtk.main_quit)456 win.connect("destroy",Gtk.main_quit)
454 win.show()457 win.show()
455458
456 #create a top level container459 #create a top level container
457 vbox = gtk.VBox(False, 10)460 vbox = Gtk.VBox(False, 10)
458 vbox.show()461 vbox.show()
459 win.add(vbox)462 win.add(vbox)
460463
@@ -464,47 +467,47 @@
464 vbox.add(mb)467 vbox.add(mb)
465 mb.show()468 mb.show()
466469
467 uri_entry = gtk.Entry()470 uri_entry = Gtk.Entry()
468471
469 play_butt = gtk.Button("Play")472 play_butt = Gtk.Button("Play")
470 pause_butt = gtk.Button("Pause")473 pause_butt = Gtk.Button("Pause")
471 stop_butt = gtk.Button("Stop")474 stop_butt = Gtk.Button("Stop")
472 seek_butt = gtk.Button("Seek")475 seek_butt = Gtk.Button("Seek")
473 controls_butt = gtk.ToggleButton("Controls")476 controls_butt = Gtk.ToggleButton("Controls")
474 time_label = gtk.Label("")477 time_label = Gtk.Label("")
475478
476 play_butt.connect("clicked", lambda x:mb.play())479 play_butt.connect("clicked", lambda x:mb.play())
477 play_butt.show()480 play_butt.show()
478 mb.pack_end(play_butt, False)481 mb.pack_end(play_butt, False, False, 0)
479482
480 uri_entry.connect("activate", __set_uri, (mb, uri_entry))483 uri_entry.connect("activate", __set_uri, (mb, uri_entry))
481 uri_entry.set_text("file:///home/rick/Videos/VID00110.AVI")484 uri_entry.set_text("file:///home/rick/Videos/VID00110.AVI")
482 uri_entry.show()485 uri_entry.show()
483 mb.pack_end(uri_entry, False)486 mb.pack_end(uri_entry, False, False, 0)
484487
485 pause_butt.connect("clicked", lambda x:mb.pause())488 pause_butt.connect("clicked", lambda x:mb.pause())
486 pause_butt.show()489 pause_butt.show()
487 mb.pack_end(pause_butt, False)490 mb.pack_end(pause_butt, False, False, 0)
488491
489 stop_butt.connect("clicked", lambda x:mb.stop())492 stop_butt.connect("clicked", lambda x:mb.stop())
490 stop_butt.show()493 stop_butt.show()
491 mb.pack_end(stop_butt, False)494 mb.pack_end(stop_butt, False, False, 0)
492495
493 seek_butt.connect("clicked", __seek_func, mb)496 seek_butt.connect("clicked", __seek_func, mb)
494 seek_butt.show()497 seek_butt.show()
495 mb.pack_end(seek_butt, False)498 mb.pack_end(seek_butt, False, False, 0)
496499
497 controls_butt.connect("clicked", __controls_func, mb)500 controls_butt.connect("clicked", __controls_func, mb)
498 controls_butt.show()501 controls_butt.show()
499 mb.pack_end(controls_butt, False)502 mb.pack_end(controls_butt, False, False, 0)
500503
501 mb.connect("end-of-file", __on_media_ended)504 mb.connect("end-of-file", __on_media_ended)
502505
503 time_label.show()506 time_label.show()
504 mb.pack_end(time_label, False)507 mb.pack_end(time_label, False, False, 0)
505508
506 gobject.timeout_add(1000, __seek_time, (mb, time_label))509 GObject.timeout_add(1000, __seek_time, (mb, time_label))
507510
508 gtk.main()511 Gtk.main()
509512
510513
511514
=== modified file 'quickly/widgets/press_and_hold_button.py'
--- quickly/widgets/press_and_hold_button.py 2011-01-17 20:27:12 +0000
+++ quickly/widgets/press_and_hold_button.py 2012-03-06 08:52:21 +0000
@@ -34,14 +34,14 @@
34pah.set_labe("Press and Hold")34pah.set_labe("Press and Hold")
3535
36Extending36Extending
37A PressAndHoldButton is gtk.Button37A PressAndHoldButton is Gtk.Button
3838
39"""39"""
4040
41import gobject41from gi.repository import GObject
42import gtk42from gi.repository import Gtk
4343
44class PressAndHoldButton(gtk.Button):44class PressAndHoldButton(Gtk.Button):
45 def __init__(self):45 def __init__(self):
46 """Create a PressAndHoldButton46 """Create a PressAndHoldButton
4747
@@ -51,7 +51,7 @@
5151
52 """52 """
5353
54 gtk.Button.__init__(self)54 Gtk.Button.__init__(self)
55 self.timeout = 25055 self.timeout = 250
56 self.connect("pressed",self.__pressed)56 self.connect("pressed",self.__pressed)
57 self.connect("released",self.__released)57 self.connect("released",self.__released)
@@ -60,7 +60,7 @@
60 def __pressed(self, widget, data=None):60 def __pressed(self, widget, data=None):
61 self.__continue_ticking = True61 self.__continue_ticking = True
62 widget.emit("tick",self)62 widget.emit("tick",self)
63 gobject.timeout_add(self.timeout, self.__tick)63 GObject.timeout_add(self.timeout, self.__tick)
6464
65 def __released(self, widget, data=None):65 def __released(self, widget, data=None):
66 self.__continue_ticking = False66 self.__continue_ticking = False
@@ -70,7 +70,44 @@
70 self.emit("tick",self)70 self.emit("tick",self)
71 return self.__continue_ticking71 return self.__continue_ticking
7272
73 __gsignals__ = {'tick' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,73 __gsignals__ = {'tick' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
74 (gobject.TYPE_PYOBJECT,)),74 (GObject.TYPE_PYOBJECT,)),
75 }75 }
7676
77def __test_tick(sender, widget, label):
78 """internal method for testing.
79 Do not use.
80 """
81
82 label.set_text(str(int(label.get_text()) + 1))
83
84
85
86if __name__ == "__main__":
87 """creates a test PressAndHoldButton"""
88
89 #create and show a test window
90 win = Gtk.Window()
91 win.set_title("Press and Hold Test Window")
92 win.connect("destroy",Gtk.main_quit)
93 win.show()
94
95 #create a top level container
96 vbox = Gtk.VBox(False, 10)
97 vbox.show()
98 win.add(vbox)
99
100 button = PressAndHoldButton()
101 button.set_label("Press and hold")
102 button.show()
103 vbox.pack_start(button, False, False, 5)
104
105 label = Gtk.Label("0")
106 label.show()
107 vbox.pack_end(label, False, False, 5)
108
109 button.timeout = 10
110
111 button.connect("tick",__test_tick, label)
112
113 Gtk.main()
77114
=== removed file 'quickly/widgets/tests/test_asycnh_task_progress_box.py'
--- quickly/widgets/tests/test_asycnh_task_progress_box.py 2010-03-30 23:38:02 +0000
+++ quickly/widgets/tests/test_asycnh_task_progress_box.py 1970-01-01 00:00:00 +0000
@@ -1,51 +0,0 @@
1### BEGIN LICENSE
2# Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com
3#This program is free software: you can redistribute it and/or modify it
4#under the terms of the GNU General Public License version 3, as published
5#by the Free Software Foundation.
6#
7#This program is distributed in the hope that it will be useful, but
8#WITHOUT ANY WARRANTY; without even the implied warranties of
9#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
10#PURPOSE. See the GNU General Public License for more details.
11#
12#You should have received a copy of the GNU General Public License along
13#with this program. If not, see <http://www.gnu.org/licenses/>.
14### END LICENSE
15
16"""Tests for the AsyncTaskProgressBox"""
17
18from testtools import TestCase
19from quickly.widgets.asynch_task_progressbox import AsynchTaskProgressBox
20
21class TestAsynchTaskProgessBox(TestCase):
22 """Test the CouchGrid functionality"""
23
24 def setUp(self):
25 TestCase.setUp(self)
26
27 def tearDown(self):
28 TestCase.tearDown(self)
29
30 def test_constructions(self):
31 """Test a simple creating An AsynchTaskProgressBox """
32 box = AsynchTaskProgressBox(self.asynch_function)
33 self.assertEqual((box != None), True)
34
35 #A function to run asynchronously
36 def asynch_function( self, params ):
37 #pull values from the params that were set above
38 for x in range(params["start"],params["stop"]):
39 #check if to see if the user has told the task to stop
40 if params["kill"] == True:
41 #return a string if the user stopped the task
42 return "stopped at " + str(x)
43 else:
44 #if the user did not try to stop the task, go ahead and do something
45 print x
46 #this is a processor intensive task, so
47 #sleep the loop to keep the UI from bogging down
48 time.sleep(.5)
49 #if the loop completes, return a string
50 return "counted all"
51
520
=== removed file 'quickly/widgets/tests/test_couch_grid.py'
--- quickly/widgets/tests/test_couch_grid.py 2010-09-02 01:03:54 +0000
+++ quickly/widgets/tests/test_couch_grid.py 1970-01-01 00:00:00 +0000
@@ -1,284 +0,0 @@
1# Copyright 2009 Canonical Ltd.
2#
3# This file is part of desktopcouch.
4#
5# desktopcouch is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3
7# as published by the Free Software Foundation.
8#
9# desktopcouch is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
16#
17# Authors: Rick Spencer <rick.spencer@canonical.com>
18
19"""Tests for the CouchGrid object"""
20
21from testtools import TestCase
22
23from desktopcouch.records.record import Record
24from desktopcouch.records.server import CouchDatabase
25from quickly.widgets.couch_grid import CouchGrid
26
27
28class TestCouchGrid(TestCase):
29 """Test the CouchGrid functionality"""
30
31 def setUp(self):
32 TestCase.setUp(self)
33 self.dbname = self._testMethodName
34 self.db = CouchDatabase(self.dbname, create=True)
35 self.record_type = "test_record_type"
36
37 def tearDown(self):
38 """tear down each test"""
39 TestCase.tearDown(self)
40 #delete the database
41 del self.db._server[self.dbname]
42
43 def test_constructor_guarded(self):
44 """Ensure that CouchGrid cannot be constructed without a
45 database name.
46 """
47 try:
48 cw = CouchGrid(None)
49 except TypeError, inst:
50 self.assertEqual(
51 inst.args[0],"database_name is required and must be a string")
52
53 def test_new_rows_with_headings(self):
54 """Test a simple creating a CouchGrid """
55
56 #create a test widget with test database values
57 cw = CouchGrid(self.dbname)
58
59 #allow editing
60 cw.editable = True
61
62 #create headers/keys
63 cw.keys = ["Key1", "Key2", "Key3", "Key4"]
64
65 #set the record_type for the TreeView
66 #it will not populate without this value being set
67 cw.record_type = self.record_type
68
69 #create a row with all four columns set
70 cw.append_row({"Key1":"val1", "Key2":"val2", "Key2":"val3", "Key4":"val4"})
71
72 #create a row with only the second column set
73 cw.append_row({"Key1":"", "Key2":"val2"})
74
75 #create an empty row (which will not be saved until the user edits it)
76 cw.append_row({})
77
78 #if this all worked, there should be three rows in the model
79 model = cw.get_model()
80 self.assertEqual(len(model), 3)
81
82 def test_headings_no_stored_records(self):
83 record_type = "a_new_record_type"
84 dicts = [{"key1":"val1"},{"key1":"val2"}]
85 cw = CouchGrid(self.dbname, record_type=record_type,dictionaries=dicts)
86 self.assertEqual(len(cw.get_model()),2)
87 self.assertEqual(cw.get_model().get_n_columns(),2)
88
89 def test_no_headings_or_stored_records(self):
90 """test when there is no defined headings and no stored records
91 to infer headings from. Should raise a proper exception.
92 """
93
94 try:
95 #create a test widget with test database values
96 cw = CouchGrid(self.dbname)
97
98 #set the record_type for the TreeView
99 #it will not populate without this value being set
100 cw.record_type = self.record_type
101
102 #create a row with all four columns set
103 cw.append_row(["val1", "val2", "val3", "val4"])
104
105 #create a row with only the second column set
106 cw.append_row(["", "val2"])
107
108 #create an empty row (which will not be saved until the
109 #user edits it)
110 cw.append_row([])
111
112 #if this all worked, there should be three rows in the model
113 model = cw.get_model()
114
115 #should be catching the following exception
116 except RuntimeError, inst:
117 self.assertEquals(
118 inst.args[0].find("Cannot infer columns for CouchGrid"),0)
119
120
121 def test_all_from_database(self):
122 #create some records
123 db = CouchDatabase(self.dbname, create=True)
124 db.put_record(Record({
125 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
126 "record_type": self.record_type}))
127 db.put_record(Record({
128 "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3",
129 "record_type": self.record_type}))
130
131 #build the CouchGrid
132 cw = CouchGrid(self.dbname)
133 cw.record_type = self.record_type
134 #make sure there are three columns and two rows
135 self.assertEqual(cw.get_model().get_n_columns(),4)
136 self.assertEqual(len(cw.get_model()),2)
137
138 def test_delete_selected_rows(self):
139 #create some records
140 db = CouchDatabase(self.dbname, create=True)
141 ids = []
142 for i in xrange(0,10):
143 ids.append( db.put_record(Record({
144 "key1_1": "val1_%s" % str(i), "iter_count": i,
145 "record_type": self.record_type})))
146
147 #build the CouchGrid
148 cw = CouchGrid(self.dbname, record_type = self.record_type)
149 cw.selected_record_ids = [ids[0],ids[5],ids[9]]
150 cw.remove_selected_rows(delete=True)
151 self.assertEqual(self.db.get_record(ids[0]) is None,True)
152 self.assertEqual(self.db.get_record(ids[5]) is None,True)
153 self.assertEqual(self.db.get_record(ids[9]) is None,True)
154
155 self.assertEqual(self.db.get_record(ids[1]) is not None,True)
156 self.assertEqual(self.db.get_record(ids[2]) is not None,True)
157 self.assertEqual(self.db.get_record(ids[3]) is not None,True)
158 self.assertEqual(self.db.get_record(ids[4]) is not None,True)
159 self.assertEqual(self.db.get_record(ids[6]) is not None,True)
160 self.assertEqual(self.db.get_record(ids[7]) is not None,True)
161 self.assertEqual(self.db.get_record(ids[8]) is not None,True)
162
163 def test_dont_delete_selected_rows(self):
164 #create some records
165 db = CouchDatabase(self.dbname, create=True)
166 ids = []
167 for i in xrange(0,10):
168 ids.append( db.put_record(Record({
169 "key1_1": "val1_%s" % str(i), "iter_count": i,
170 "record_type": self.record_type})))
171
172 #build the CouchGrid
173 cw = CouchGrid(self.dbname, record_type = self.record_type)
174 cw.selected_record_ids = [ids[0],ids[5],ids[9]]
175 cw.remove_selected_rows(delete=False)
176 cw.selected_record_ids = [ids[1],ids[4],ids[8]]
177 cw.remove_selected_rows()
178 self.assertEqual(self.db.get_record(ids[0]) is not None,True)
179 self.assertEqual(self.db.get_record(ids[5]) is not None,True)
180 self.assertEqual(self.db.get_record(ids[9]) is not None,True)
181
182 self.assertEqual(self.db.get_record(ids[1]) is not None,True)
183 self.assertEqual(self.db.get_record(ids[2]) is not None,True)
184 self.assertEqual(self.db.get_record(ids[3]) is not None,True)
185 self.assertEqual(self.db.get_record(ids[4]) is not None,True)
186 self.assertEqual(self.db.get_record(ids[6]) is not None,True)
187 self.assertEqual(self.db.get_record(ids[7]) is not None,True)
188 self.assertEqual(self.db.get_record(ids[8]) is not None,True)
189
190
191
192 def test_selected_id_property(self):
193 #create some records
194 db = CouchDatabase(self.dbname, create=True)
195 id1 = db.put_record(Record({
196 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
197 "record_type": self.record_type}))
198 id2 = db.put_record(Record({
199 "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3",
200 "record_type": self.record_type}))
201
202 #build the CouchGrid
203 cw = CouchGrid(self.dbname)
204 cw.record_type = self.record_type
205
206 #make sure the record ids are selected properly
207 cw.selected_record_ids = [id1]
208 self.assertEqual(cw.selected_record_ids[0], id1)
209 cw.selected_record_ids = [id2]
210 self.assertEqual(cw.selected_record_ids[0], id2)
211
212 def test_single_col_from_database(self):
213 #create some records
214 self.db.put_record(Record({
215 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
216 "record_type": self.record_type}))
217 self.db.put_record(Record({
218 "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3",
219 "record_type": self.record_type}))
220 #build the CouchGrid
221 cw = CouchGrid(self.dbname)
222 cw.keys = ["key1_1"]
223 cw.record_type = self.record_type
224 #make sure there are three columns and two rows
225 self.assertEqual(cw.get_model().get_n_columns(),2)
226 self.assertEqual(len(cw.get_model()),2)
227
228 def test_optional_record_type_arg(self):
229 """Test a simple creating a CouchGrid """
230 #create some records
231 self.db.put_record(Record({
232 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
233 "record_type": self.record_type}))
234 self.db.put_record(Record({
235 "key1_1": "val1_1", "key1_2": "val2_2", "key1_3": "val2_3",
236 "record_type": self.record_type}))
237
238 #create a test widget with test database values
239 cw = CouchGrid(self.dbname, record_type=self.record_type)
240
241 #make sure there are three columns and two rows
242 self.assertEqual(cw.get_model().get_n_columns(),4)
243 self.assertEqual(len(cw.get_model()),2)
244
245 def test_optional_args_no_stored_records(self):
246 """Test a simple creating a CouchGrid """
247
248 #create a test widget with test database values
249 cw = CouchGrid(
250 self.dbname, record_type=self.record_type,
251 keys=["Key1", "Key2", "Key3", "Key4"])
252
253 #create a row with all four columns set
254 cw.append_row({"Key1":"val1", "Key2":"val2", "Key2":"val3", "Key4":"val4"})
255
256 #create a row with only the second column set
257 cw.append_row({"Key1":"", "Key2":"val2"})
258
259 #create an empty row (which will not be saved until the user edits it)
260 cw.append_row({})
261
262 #if this all worked, there should be three rows in the model
263 model = cw.get_model()
264 self.assertEqual(len(model), 3)
265
266 def test_programatically_add_row(self):
267 """test appending different sized rows programatically"""
268 #create some records
269 self.db.put_record(Record({
270 "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
271 "record_type": self.record_type}))
272 self.db.put_record(Record({
273 "key1_1": "val2_1", "key1_2": "val2_2", "key1_3": "val2_3",
274 "record_type": self.record_type}))
275
276 #create a test widget with test database values
277 cw = CouchGrid(self.dbname, record_type=self.record_type)
278
279 #allow editing
280 cw.append_row({"key1_1":"boo", "key1_2":"ray"})
281
282 #make sure there are three columns and two rows
283 self.assertEqual(cw.get_model().get_n_columns(),4)
284 self.assertEqual(len(cw.get_model()),3)
2850
=== modified file 'quickly/widgets/tests/test_dictionary_grid.py'
--- quickly/widgets/tests/test_dictionary_grid.py 2011-09-05 06:23:54 +0000
+++ quickly/widgets/tests/test_dictionary_grid.py 2012-03-06 08:52:21 +0000
@@ -18,7 +18,7 @@
1818
19from testtools import TestCase19from testtools import TestCase
20from quickly.widgets.dictionary_grid import DictionaryGrid20from quickly.widgets.dictionary_grid import DictionaryGrid
21import gobject21from gi.repository import GObject
22from quickly.widgets.grid_column import StringColumn, IntegerColumn, CurrencyColumn,CheckColumn, DateColumn22from quickly.widgets.grid_column import StringColumn, IntegerColumn, CurrencyColumn,CheckColumn, DateColumn
2323
24class TestDictionaryGrid(TestCase):24class TestDictionaryGrid(TestCase):
@@ -42,7 +42,6 @@
42 grid.append_row({"key1":"val11","key2":"val12"})42 grid.append_row({"key1":"val11","key2":"val12"})
43 self.assertEqual(len(grid.get_model()),1)43 self.assertEqual(len(grid.get_model()),1)
4444
45
46 def test_constructor_with_dicts(self):45 def test_constructor_with_dicts(self):
47 """test creating a grid with dictionaries in the contructor"""46 """test creating a grid with dictionaries in the contructor"""
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"},
@@ -184,13 +183,13 @@
184 key = c.key183 key = c.key
185 col_type = c.column_type184 col_type = c.column_type
186 if key == "id":185 if key == "id":
187 self.assertEqual(col_type,gobject.TYPE_STRING)186 self.assertEqual(col_type,GObject.TYPE_STRING)
188 elif key == "price":187 elif key == "price":
189 self.assertEqual(col_type,gobject.TYPE_STRING)188 self.assertEqual(col_type,GObject.TYPE_STRING)
190 elif key == "bool?":189 elif key == "bool?":
191 self.assertEqual(col_type,gobject.TYPE_STRING)190 self.assertEqual(col_type,GObject.TYPE_STRING)
192 elif key == "foo":191 elif key == "foo":
193 self.assertEqual(col_type,gobject.TYPE_INT)192 self.assertEqual(col_type,GObject.TYPE_INT)
194 else:193 else:
195 self.assertEqual("Extra key Found",False) 194 self.assertEqual("Extra key Found",False)
196195
@@ -253,9 +252,9 @@
253 dicts = [{"price":"100.00","id":"50","bool?":"Yes"}]252 dicts = [{"price":"100.00","id":"50","bool?":"Yes"}]
254 grid = DictionaryGrid(dicts, keys)253 grid = DictionaryGrid(dicts, keys)
255 grid.editable = False254 grid.editable = False
256 ed1 = grid.columns["price"].get_cell_renderers()[0].get_property("editable")255 ed1 = grid.columns["price"].get_cells()[0].get_property("editable")
257 self.assertTrue(not ed1)256 self.assertTrue(not ed1)
258 ed2 = grid.columns["bool?"].get_cell_renderers()[0].get_property("activatable")257 ed2 = grid.columns["bool?"].get_cells()[0].get_property("activatable")
259 self.assertTrue(not ed2)258 self.assertTrue(not ed2)
260259
261 def test_set_a_column_title(self):260 def test_set_a_column_title(self):
262261
=== modified file 'quickly/widgets/text_editor.py'
--- quickly/widgets/text_editor.py 2011-02-03 16:28:49 +0000
+++ quickly/widgets/text_editor.py 2012-03-06 08:52:21 +0000
@@ -35,25 +35,26 @@
3535
36Configuring36Configuring
37#Configure as a TextView37#Configure as a TextView
38self.editor.set_wrap_mode(gtk.WRAP_CHAR)38self.editor.set_wrap_mode(Gtk.WRAP_CHAR)
3939
40#Access the gtk.TextBuffer if needed40#Access the Gtk.TextBuffer if needed
41buffer = self.editor.get_buffer()41buffer = self.editor.get_buffer()
4242
43Extending43Extending
44A TextEditor is gtk.TextView44A TextEditor is Gtk.TextView
4545
46"""46"""
4747
48
48try:49try:
49 import pygtk50 from gi.repository import Gtk
50 pygtk.require("2.0")51 from gi.repository import Gdk
51 import gtk
52 import re52 import re
53except:53except:
54 print "couldn't load depencies"54 print "couldn't load depencies"
5555
56class TextEditor( gtk.TextView ):56
57class TextEditor( Gtk.TextView ):
57 """TextEditor encapsulates management of TextBuffer and TextIter for58 """TextEditor encapsulates management of TextBuffer and TextIter for
58 common functionality, such as cut, copy, paste, undo, redo, and 59 common functionality, such as cut, copy, paste, undo, redo, and
59 highlighting of text.60 highlighting of text.
@@ -65,10 +66,10 @@
6566
66 """67 """
6768
68 gtk.TextView.__init__( self)69 Gtk.TextView.__init__(self)
69 self.undo_max = None70 self.undo_max = None
70 self._highlight_strings = []71 self._highlight_strings = []
71 found_tag = gtk.TextTag("highlight")72 found_tag = Gtk.TextTag(name="highlight")
72 found_tag.set_property("background","yellow")73 found_tag.set_property("background","yellow")
73 self.get_buffer().get_tag_table().add(found_tag)74 self.get_buffer().get_tag_table().add(found_tag)
7475
@@ -77,7 +78,8 @@
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)
78 self._auto_bullet = None79 self._auto_bullet = None
79 self.auto_bullets = False80 self.auto_bullets = False
80 self.clipboard = gtk.Clipboard()81 display = self.get_display()
82 self.clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD)
8183
82 self.undos = []84 self.undos = []
83 self.redos = []85 self.redos = []
@@ -92,7 +94,7 @@
92 """94 """
93 start_iter = self.get_buffer().get_iter_at_offset(0)95 start_iter = self.get_buffer().get_iter_at_offset(0)
94 end_iter = self.get_buffer().get_iter_at_offset(-1)96 end_iter = self.get_buffer().get_iter_at_offset(-1)
95 return self.get_buffer().get_text(start_iter,end_iter)97 return self.get_buffer().get_text(start_iter,end_iter, False)
9698
97 @text.setter99 @text.setter
98 def text(self, text):100 def text(self, text):
@@ -179,6 +181,7 @@
179181
180182
181 self._highlight_strings = []183 self._highlight_strings = []
184
182 self._highlight()185 self._highlight()
183186
184 def _highlight(self):187 def _highlight(self):
@@ -189,7 +192,7 @@
189192
190 start_iter = self.get_buffer().get_iter_at_offset(0)193 start_iter = self.get_buffer().get_iter_at_offset(0)
191 end_iter = self.get_buffer().get_iter_at_offset(-1)194 end_iter = self.get_buffer().get_iter_at_offset(-1)
192 text = self.get_buffer().get_text(start_iter,end_iter)195 text = self.get_buffer().get_text(start_iter,end_iter, False)
193 self.get_buffer().remove_all_tags(start_iter, end_iter)196 self.get_buffer().remove_all_tags(start_iter, end_iter)
194 for s in self._highlight_strings:197 for s in self._highlight_strings:
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)]
@@ -213,7 +216,7 @@
213 handler.216 handler.
214217
215 """218 """
216219
217 self.get_buffer().copy_clipboard(self.clipboard) 220 self.get_buffer().copy_clipboard(self.clipboard)
218221
219 def paste(self, widget=None, data=None):222 def paste(self, widget=None, data=None):
@@ -306,7 +309,7 @@
306 cur_line = iter.get_line()309 cur_line = iter.get_line()
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)
308 pl_offset = prev_line_iter.get_offset()311 pl_offset = prev_line_iter.get_offset()
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)
310 if pl_text.strip().find("*") == 0:313 if pl_text.strip().find("*") == 0:
311 ws = ""314 ws = ""
312 if not pl_text.startswith("*"):315 if not pl_text.startswith("*"):
@@ -320,7 +323,7 @@
320 """323 """
321324
322 self._highlight()325 self._highlight()
323 text = self.get_buffer().get_text(start_iter,end_iter) 326 text = self.get_buffer().get_text(start_iter,end_iter, False)
324 cmd = {"action":"insert","offset":start_iter.get_offset(),"text":text}327 cmd = {"action":"insert","offset":start_iter.get_offset(),"text":text}
325 self._add_undo(cmd)328 self._add_undo(cmd)
326329
@@ -335,25 +338,25 @@
335 del(self.undos[0])338 del(self.undos[0])
336 self.undos.append(cmd)339 self.undos.append(cmd)
337340
338class TestWindow(gtk.Window):341class TestWindow(Gtk.Window):
339 """For testing and demonstrating AsycnTaskProgressBox.342 """For testing and demonstrating AsycnTaskProgressBox.
340343
341 """344 """
342 def __init__(self):345 def __init__(self):
343 #create a window a VBox to hold the controls346 #create a window a VBox to hold the controls
344 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)347 Gtk.Window.__init__(self)
345 self.set_title("TextEditor Test Window")348 self.set_title("TextEditor Test Window")
346 windowbox = gtk.VBox(False, 2)349 windowbox = Gtk.VBox(False, 2)
347 windowbox.show()350 windowbox.show()
348 self.add(windowbox)351 self.add(windowbox)
349 self.editor = TextEditor()352 self.editor = TextEditor()
350 self.editor.show()353 self.editor.show()
351 windowbox.pack_end(self.editor)354 windowbox.pack_end(self.editor, True, True, 0)
352 self.set_size_request(200,200)355 self.set_size_request(200,200)
353 self.show()356 self.show()
354 self.maximize()357 self.maximize()
355 358
356 self.connect("destroy", gtk.main_quit)359 self.connect("destroy", Gtk.main_quit)
357 self.editor.text = "this is some inserted text"360 self.editor.text = "this is some inserted text"
358 self.editor.append("\nLine 3")361 self.editor.append("\nLine 3")
359 self.editor.prepend("Line1\n")362 self.editor.prepend("Line1\n")
@@ -364,35 +367,35 @@
364 self.editor.add_highlight("some")367 self.editor.add_highlight("some")
365 self.editor.undo_max = 100368 self.editor.undo_max = 100
366 self.editor.auto_bullets = True369 self.editor.auto_bullets = True
367 cut_button = gtk.Button("Cut")370 cut_button = Gtk.Button("Cut")
368 cut_button.connect("clicked",self.editor.cut)371 cut_button.connect("clicked",self.editor.cut)
369 cut_button.show()372 cut_button.show()
370 windowbox.pack_start(cut_button, False)373 windowbox.pack_start(cut_button, False, False, 0)
371374
372 copy_button = gtk.Button("Copy")375 copy_button = Gtk.Button("Copy")
373 copy_button.connect("clicked",self.editor.copy)376 copy_button.connect("clicked",self.editor.copy)
374 copy_button.show()377 copy_button.show()
375 windowbox.pack_start(copy_button, False)378 windowbox.pack_start(copy_button, False, False, 0)
376379
377 paste_button = gtk.Button("Paste")380 paste_button = Gtk.Button("Paste")
378 paste_button.connect("clicked",self.editor.paste)381 paste_button.connect("clicked",self.editor.paste)
379 paste_button.show()382 paste_button.show()
380 windowbox.pack_start(paste_button, False)383 windowbox.pack_start(paste_button, False, False, 0)
381384
382 undo_button = gtk.Button("Undo")385 undo_button = Gtk.Button("Undo")
383 undo_button.connect("clicked",self.editor.undo)386 undo_button.connect("clicked",self.editor.undo)
384 undo_button.show()387 undo_button.show()
385 windowbox.pack_start(undo_button, False)388 windowbox.pack_start(undo_button, False, False, 0)
386389
387 redo_button = gtk.Button("Redo")390 redo_button = Gtk.Button("Redo")
388 redo_button.connect("clicked",self.editor.redo)391 redo_button.connect("clicked",self.editor.redo)
389 redo_button.show()392 redo_button.show()
390 windowbox.pack_start(redo_button, False)393 windowbox.pack_start(redo_button, False, False, 0)
391394
392 print self.editor.text395 print self.editor.text
393396
394397
395if __name__== "__main__":398if __name__== "__main__":
396 test = TestWindow()399 test = TestWindow()
397 gtk.main()400 Gtk.main()
398401
399402
=== modified file 'quickly/widgets/url_fetch_progressbox.py'
--- quickly/widgets/url_fetch_progressbox.py 2011-09-05 06:19:23 +0000
+++ quickly/widgets/url_fetch_progressbox.py 2012-03-06 08:52:21 +0000
@@ -1,5 +1,6 @@
1### BEGIN LICENSE1### BEGIN LICENSE
2# Copyright (C) 2010 Stuart Langridge stuart.langridge@canonical.com2# Copyright (C) 2010 Stuart Langridge stuart.langridge@canonical.com
3# Copyright (C) 2012 Rick Spencer rick.spencer@canonical.com
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
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
5#by the Free Software Foundation.6#by the Free Software Foundation.
@@ -14,18 +15,18 @@
14### END LICENSE15### END LICENSE
1516
16try:17try:
17 import pygtk18 from gi.repository import GObject
18 pygtk.require("2.0")19 from gi.repository import Gtk
19 import gtk, gobject, gio20 from gi.repository import Gio
21 from gi.repository import GLib
20 import gettext22 import gettext
21 from gettext import gettext as _23 from gettext import gettext as _
22 gettext.textdomain('quickly-widgets')24 gettext.textdomain('quickly-widgets')
23 import glib
24except:25except:
25 print "couldn't load dependencies"26 print "couldn't load dependencies"
2627
2728
28class UrlFetchProgressBox(gtk.HBox):29class UrlFetchProgressBox(Gtk.HBox):
29 """UrlFetchProgressBox: encapsulates a pulsating progressbar, a cancel30 """UrlFetchProgressBox: encapsulates a pulsating progressbar, a cancel
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
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
@@ -37,10 +38,10 @@
37 Cancelling fires the "downloaded" signal with a value of None.38 Cancelling fires the "downloaded" signal with a value of None.
38 """39 """
3940
40 __gsignals__ = {'downloaded' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 41 __gsignals__ = {'downloaded' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
41 (gobject.TYPE_PYOBJECT,)),42 (GObject.TYPE_PYOBJECT,)),
42 'download-error' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, 43 'download-error' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
43 (gobject.TYPE_PYOBJECT,))}44 (GObject.TYPE_PYOBJECT,))}
4445
45 def __init__(self, url, destroy_after_fetching=True, cancelable=True):46 def __init__(self, url, destroy_after_fetching=True, cancelable=True):
46 """Create an UrlFetchProgressBox47 """Create an UrlFetchProgressBox
@@ -51,39 +52,37 @@
51 is fetched? Defaults to True.52 is fetched? Defaults to True.
52 cancelable -- whether to show cancel button. Defaults to True.53 cancelable -- whether to show cancel button. Defaults to True.
53 """54 """
54 gtk.HBox.__init__( self, False, 2)55 Gtk.HBox.__init__( self, False, 2)
55 self.progressbar = gtk.ProgressBar()56 self.progressbar = Gtk.ProgressBar()
56 gobject.timeout_add(10, self.__tick)57 GObject.timeout_add(10, self.__tick)
57 self.running = True58 self.running = True
58 parts = [x for x in url.split("/") if x]59 parts = [x for x in url.split("/") if x]
59 self.progressbar.set_text(_("Downloading %s") % parts[-1])60 self.progressbar.set_text(_("Downloading %s") % parts[-1])
60 self.progressbar.show()61 self.progressbar.show()
61 self.pack_start(self.progressbar, True)62 self.pack_start(self.progressbar, True, True, 0)
62 self.destroy_after_fetching = destroy_after_fetching63 self.destroy_after_fetching = destroy_after_fetching
63 self.cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)64 self.cancel_button = Gtk.Button(stock=Gtk.STOCK_CANCEL)
64 if cancelable:65 if cancelable:
65 self.cancel_button.show()66 self.cancel_button.show()
66 self.cancel_button.set_sensitive(False)67 self.cancel_button.set_sensitive(False)
67 self.cancel_button.connect("clicked",self.__cancel)68 self.cancel_button.connect("clicked",self.__cancel)
68 self.pack_end(self.cancel_button, False)69 self.pack_end(self.cancel_button, False, False, 0)
69 self.cancel_button.set_sensitive(True)70 self.cancel_button.set_sensitive(True)
70 self.__canceller = gio.Cancellable()71 self.__canceller = Gio.Cancellable()
71 self.stream = gio.File(url)72 self.stream = Gio.file_new_for_uri(url)
72 self.stream.load_contents_async(self.__download_finished, cancellable=self.__canceller)73 self.stream.load_contents_async(self.__canceller, self.__download_finished, None)
73 74
75
74 def __tick(self):76 def __tick(self):
75 self.progressbar.pulse()77 self.progressbar.pulse()
76 return self.running78 return self.running
77 79
78 def __download_finished(self, gdaemonfile, result):80 def __download_finished(self, gdaemonfile, result, data=None):
79 try:81 try:
80 content = self.stream.load_contents_finish(result)[0]82 #GIO documentation says that the file is [0] in the tuple
81 except gio.Error, e:83 #but it is realy [1]
82 if e.code == 19:84 content = self.stream.load_contents_finish(result)[1]
83 self.emit("downloaded", None)85 except Exception, e:
84 else:
85 self.emit("download-error",e)
86 except glib.GError, e:
87 self.emit("download-error",e)86 self.emit("download-error",e)
88 else:87 else:
89 self.emit("downloaded", content)88 self.emit("downloaded", content)
@@ -97,29 +96,34 @@
97 self.running = False96 self.running = False
98 if self.destroy_after_fetching: self.destroy()97 if self.destroy_after_fetching: self.destroy()
9998
100class TestWindow(gtk.Window):99class TestWindow(Gtk.Window):
101 def __init__(self):100 def __init__(self):
102 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)101 Gtk.Window.__init__(self)
103 self.set_title("UrlFetchProgressBox test")102 self.set_title("UrlFetchProgressBox test")
104 self.vbox = gtk.VBox()103 self.vbox = Gtk.VBox()
105 btn = gtk.Button(stock=gtk.STOCK_EXECUTE)104 btn = Gtk.Button(stock=Gtk.STOCK_EXECUTE)
106 btn.connect("clicked", self.start_download)105 btn.connect("clicked", self.start_download)
107 self.vbox.pack_end(btn)106 self.vbox.pack_end(btn, True, True, 0)
108 self.add(self.vbox)107 self.add(self.vbox)
109 self.set_size_request(300,200)108 self.set_size_request(300,200)
110 self.connect("destroy", gtk.main_quit)109 self.connect("destroy", Gtk.main_quit)
111 110
112 def start_download(self, btn):111 def start_download(self, btn):
113 prog = UrlFetchProgressBox("http://www.ubuntu.com/desktop/get-ubuntu/download")112 prog = UrlFetchProgressBox("http://www.ubuntu.com/desktop/get-ubuntu/download")
113
114 prog.connect("downloaded", self.downloaded)114 prog.connect("downloaded", self.downloaded)
115 self.vbox.pack_start(prog, expand=False)115 prog.connect("download-error", self.errored)
116 self.vbox.pack_start(prog, False, False, 0)
116 prog.show()117 prog.show()
117 118
119 def errored(self, widget, e):
120 print "encountered error: %s " % e.message
121
118 def downloaded(self, widget, content):122 def downloaded(self, widget, content):
119 print "downloaded %s bytes of content" % len(content)123 print "downloaded %s bytes of content" % len(content)
120124
121if __name__ == "__main__":125if __name__ == "__main__":
122 w = TestWindow()126 w = TestWindow()
123 w.show_all()127 w.show_all()
124 gtk.main()128 Gtk.main()
125129
126130
=== modified file 'quickly/widgets/web_cam_box.py'
--- quickly/widgets/web_cam_box.py 2011-01-04 03:19:55 +0000
+++ quickly/widgets/web_cam_box.py 2012-03-06 08:52:21 +0000
@@ -53,13 +53,13 @@
53cam.pack_start(my_widget, False, False)53cam.pack_start(my_widget, False, False)
5454
55Extending55Extending
56A WebCamBox is gtk.VBox56A WebCamBox is Gtk.VBox
57A WebCamBox is a gtk.VBox that contains a gtk.DrawingArea for displaying57A WebCamBox is a Gtk.VBox that contains a Gtk.DrawingArea for displaying
58webcam output, and a thin wrapper around a camerabin, which is a gstreamer58webcam output, and a thin wrapper around a camerabin, which is a gstreamer
59pipleine sublcass that provides all the camera functionality.59pipleine sublcass that provides all the camera functionality.
6060
61To add GUI elements simple, create them and pack them into WebCamBox, since61To add GUI elements simple, create them and pack them into WebCamBox, since
62it's just a gtk.VBox62it's just a Gtk.VBox
6363
64Similarly, to add to or change the web cam functionality, modify properties on64Similarly, to add to or change the web cam functionality, modify properties on
65the camerabin. You may also want to overide _on_message and/or _on_sync_message65the camerabin. You may also want to overide _on_message and/or _on_sync_message
@@ -67,18 +67,20 @@
6767
68"""68"""
6969
70from gi.repository import Gtk
71from gi.repository import GdkX11
72from gi.repository import GObject
73
70import sys74import sys
71import os75import os
72import gtk
73import gst76import gst
74import datetime77import datetime
75import gobject
7678
77import gettext79import gettext
78from gettext import gettext as _80from gettext import gettext as _
79gettext.textdomain('quickly-widgets')81gettext.textdomain('quickly-widgets')
8082
81class WebCamBox(gtk.VBox):83class WebCamBox(Gtk.VBox):
82 """WebCamBox - A VBox that tries to turn on and display the default webcam for the84 """WebCamBox - A VBox that tries to turn on and display the default webcam for the
83 computer on which it is running. It is also capable of saving an image85 computer on which it is running. It is also capable of saving an image
84 from the webcam to the user's Picture directory.86 from the webcam to the user's Picture directory.
@@ -92,10 +94,10 @@
92 This function has no arguments94 This function has no arguments
9395
94 """96 """
95 gtk.VBox.__init__(self, False, 5)97 Gtk.VBox.__init__(self, False, 5)
96 self.video_window = gtk.DrawingArea()98 self.video_window = Gtk.DrawingArea()
97 self.video_window.connect("realize",self.__on_video_window_realized)99 self.video_window.connect("realize",self.__on_video_window_realized)
98 self.pack_start(self.video_window, True, True)100 self.pack_start(self.video_window, True, True, 0)
99 self.video_window.show()101 self.video_window.show()
100 self.connect("destroy", self.on_destroy)102 self.connect("destroy", self.on_destroy)
101103
@@ -105,7 +107,7 @@
105 bus.enable_sync_message_emission()107 bus.enable_sync_message_emission()
106 bus.connect("message", self._on_message)108 bus.connect("message", self._on_message)
107 bus.connect("sync-message::element", self._on_sync_message)109 bus.connect("sync-message::element", self._on_sync_message)
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"))
109 self.filename_prefix = ""111 self.filename_prefix = ""
110 self.realized = False112 self.realized = False
111113
@@ -155,7 +157,7 @@
155 """157 """
156158
157 stamp = str(datetime.datetime.now())159 stamp = str(datetime.datetime.now())
158 extension = ".png"160 extension = ".jpg"
159 directory = os.environ["HOME"] + _("/Pictures/")161 directory = os.environ["HOME"] + _("/Pictures/")
160 self.filename = directory + self.filename_prefix + stamp + extension162 self.filename = directory + self.filename_prefix + stamp + extension
161 self.camerabin.set_property("filename", self.filename)163 self.camerabin.set_property("filename", self.filename)
@@ -217,7 +219,7 @@
217 if message_name == "prepare-xwindow-id":219 if message_name == "prepare-xwindow-id":
218 imagesink = message.src220 imagesink = message.src
219 imagesink.set_property("force-aspect-ratio", True)221 imagesink.set_property("force-aspect-ratio", True)
220 imagesink.set_xwindow_id(self.video_window.window.xid)222 imagesink.set_xwindow_id(self.video_window.get_window().get_xid())
221223
222 def __on_video_window_realized(self, widget, data=None):224 def __on_video_window_realized(self, widget, data=None):
223 """__on_video_window_realized - internal signal handler, used225 """__on_video_window_realized - internal signal handler, used
@@ -229,16 +231,16 @@
229 self._set_video_window_id()231 self._set_video_window_id()
230232
231 def _set_video_window_id(self):233 def _set_video_window_id(self):
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:
233 x = self.video_window.window.xid235 x = self.video_window.get_window().get_xid()
234 self.realized = True236 self.realized = True
235237
236 def on_destroy(self, widget, data=None):238 def on_destroy(self, widget, data=None):
237 #clean up the camera before exiting239 #clean up the camera before exiting
238 self.camerabin.set_state(gst.STATE_NULL)240 self.camerabin.set_state(gst.STATE_NULL)
239 241
240 __gsignals__ = {'image-captured' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,242 __gsignals__ = {'image-captured' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
241 (gobject.TYPE_PYOBJECT,)),243 (GObject.TYPE_PYOBJECT,)),
242 } 244 }
243245
244def __image_captured(widget, data=None):246def __image_captured(widget, data=None):
@@ -247,20 +249,21 @@
247249
248 """250 """
249251
250 quickly.prompts.info("WebCam Test",data) 252 #quickly.prompts.info("WebCam Test",data)
253 print data
251254
252if __name__ == "__main__":255if __name__ == "__main__":
253 """creates a test WebCamBox"""256 """creates a test WebCamBox"""
254 import quickly.prompts257 #import quickly.prompts
255258
256 #create and show a test window259 #create and show a test window
257 win = gtk.Window(gtk.WINDOW_TOPLEVEL)260 win = Gtk.Window()
258 win.set_title("WebCam Test Window")261 win.set_title("WebCam Test Window")
259 win.connect("destroy",gtk.main_quit)262 win.connect("destroy",Gtk.main_quit)
260 win.show()263 win.show()
261264
262 #create a top level container265 #create a top level container
263 vbox = gtk.VBox(False, 10)266 vbox = Gtk.VBox(False, 10)
264 vbox.show()267 vbox.show()
265 win.add(vbox)268 win.add(vbox)
266269
@@ -271,27 +274,27 @@
271 mb.play()274 mb.play()
272275
273 mb.connect("image-captured", __image_captured)276 mb.connect("image-captured", __image_captured)
274 play_butt = gtk.Button("Play")277 play_butt = Gtk.Button("Play")
275 pause_butt = gtk.Button("Pause")278 pause_butt = Gtk.Button("Pause")
276 stop_butt = gtk.Button("Stop")279 stop_butt = Gtk.Button("Stop")
277 pic_butt = gtk.Button("Picture")280 pic_butt = Gtk.Button("Picture")
278281
279 play_butt.connect("clicked", lambda x:mb.play())282 play_butt.connect("clicked", lambda x:mb.play())
280 play_butt.show()283 play_butt.show()
281 mb.pack_end(play_butt, False)284 mb.pack_end(play_butt, False, False, 0)
282285
283 pause_butt.connect("clicked", lambda x:mb.pause())286 pause_butt.connect("clicked", lambda x:mb.pause())
284 pause_butt.show()287 pause_butt.show()
285 mb.pack_end(pause_butt, False)288 mb.pack_end(pause_butt, False, False, 0)
286289
287 stop_butt.connect("clicked", lambda x:mb.stop())290 stop_butt.connect("clicked", lambda x:mb.stop())
288 stop_butt.show()291 stop_butt.show()
289 mb.pack_end(stop_butt, False)292 mb.pack_end(stop_butt, False, False, 0)
290293
291 pic_butt.connect("clicked", lambda x:mb.take_picture())294 pic_butt.connect("clicked", lambda x:mb.take_picture())
292 pic_butt.show()295 pic_butt.show()
293 mb.pack_end(pic_butt, False)296 mb.pack_end(pic_butt, False, False, 0)
294297
295 gtk.main()298 Gtk.main()
296299
297300

Subscribers

People subscribed via source and target branches