QuicklyWidgets
Ubuntu Opportunistic Developers Week March 2010 - Speed your development with quickly.widgets - Rick Spencer - Mar 4 2010
(03:01:19 PM) rickspencer3: hi all (03:01:43 PM) rickspencer3: shall we dive into quickly-widgets? (03:01:55 PM) rickspencer3: or maybe you need some time to recover from that stellar class by rockstar (03:03:06 PM) rickspencer3: ok, I guess I'll get started (03:03:32 PM) rickspencer3: ok (03:03:35 PM) rickspencer3: my code is ugly (03:03:40 PM) rickspencer3: but functional ;) (03:03:54 PM) rickspencer3: as usual I put my class notes on the wiki (03:03:55 PM) rickspencer3: https://wiki.ubuntu.com/UbuntuOpportunisticDeveloperWeek/Quidgets (03:04:11 PM) rickspencer3: so, quickly-widgets started as a personal side project of mine (03:04:17 PM) rickspencer3: and it got totally out of control (03:04:26 PM) rickspencer3: at some point I discovered it was writing itself at night (03:04:35 PM) rickspencer3: the goal of quickly widgets is to make PyGtk programming more easy and fun (03:04:50 PM) rickspencer3: PyGtk is a fantastic UI tool kit (03:04:53 PM) rockstar left the room ("Leaving"). (03:04:55 PM) rickspencer3: It is very powerful, functional, and flexible (03:05:02 PM) rickspencer3: However, at the moment, it lacks some higher order abstractions that developers have come to expect (03:05:11 PM) rickspencer3: The result being that developers have to write reams of code using multiple classes and such for things that are more simple in other frameworks (03:05:18 PM) rickspencer3: for example, popping dialogs, putting data into a grid and such (03:05:34 PM) rickspencer3: so I'm hoping quickly-widgets can fill in some of these use cases (03:05:42 PM) rickspencer3: please note that quickly-widgets is very early in development and experimental (03:05:52 PM) rickspencer3: this means that it is to be buggy and incomplete (03:06:00 PM) rickspencer3: or at least I strongly suspect that it is (03:06:06 PM) rickspencer3: but it also means that if you want to, you can have a lot of influence on how it develops (03:06:15 PM) rickspencer3: so bug reports and branches, more than welcome (03:06:17 PM) rickspencer3: btw (03:06:22 PM) rickspencer3: so what is Quidgets? (03:06:28 PM) rickspencer3: "Quidgets" is the original name of quickly-widgets (03:06:32 PM) rickspencer3: it was a silly name (03:06:36 PM) rickspencer3: I made it up without really thinking (03:06:42 PM) rickspencer3: the project is still called Quidgets (03:06:47 PM) rickspencer3: so you can get the code: (03:06:52 PM) rickspencer3: $bzr branch lp:quidgets (03:06:57 PM) rickspencer3: quickly-widgets is currently in universe for Lucid (03:07:07 PM) rickspencer3: (this scares me a bit, but what the heck ;) ) (03:07:11 PM) rickspencer3: it is not packaged for Karmic (03:07:16 PM) rickspencer3: though if someone wanted to do that, I would not complain too much ;) (03:07:23 PM) rickspencer3: currently, quickly widgets has two modules (03:07:29 PM) rickspencer3: quickly.prompts (03:07:33 PM) rickspencer3: and quickly.widgets (03:07:37 PM) rickspencer3: quickly.widgets has some general widgets (03:07:47 PM) rickspencer3: but also some widgets for displaying data, basically extending TreeView and making it simpler to use (03:07:55 PM) rickspencer3: I'll discuss each of these three areas in a bit more depth (03:08:02 PM) rickspencer3: But first, Questions? (03:08:15 PM) mode (+v ClassBot) by ChanServ (03:08:16 PM) ClassBot: oskude asked: does quickly-widgets work with or without glade (the editor) ? (03:08:23 PM) rickspencer3: currently there is no glade integration (03:08:37 PM) rickspencer3: I don't think it would be too hard to add this, but it hasn't been tackled yet (03:08:49 PM) rickspencer3: I think to do this would require crafting a library file in XML (03:09:10 PM) rickspencer3: but then also making sure the properties were properly setable in the glade propery editor (03:09:34 PM) rickspencer3: Before we look at the modules, a word on the mental model of using quickly-widgets (03:09:40 PM) rickspencer3: There are 3 "levels" to consuming quickly-widgets (03:09:47 PM) rickspencer3: Most of the time, you should be fine with the first level, which is the "Use" level (03:09:56 PM) rickspencer3: At this level, you just make a one-liner that uses the API and does something useful (03:10:04 PM) rickspencer3: like, get as string from the user: (03:10:04 PM) rickspencer3: quickly.prompts.string("My Title","Give me a string","default val") (03:10:17 PM) rickspencer3: or maybe you want do display the data in a dictionary: (03:10:17 PM) rickspencer3: dg = DictionaryGrid(my_list_of_dicts) (03:10:17 PM) rickspencer3: dg.show() (03:10:29 PM) rickspencer3: see, you just go ahead and "use" the classes (03:10:35 PM) rickspencer3: sometimes you want to tweak something a bit. In these cases you go to the configure level. (03:10:41 PM) rickspencer3: When you are doing "configure" you might end up a using the underlying PyGtk API a bit (03:10:47 PM) rickspencer3: For example, a dictionary grid is really just a Treeview (03:10:56 PM) rickspencer3: So if you want to configure the title for a column, you might go (03:11:02 PM) rickspencer3: dg.get_columns()[0].set_title("New Title") (03:11:09 PM) rickspencer3: some of the configuration might be done in the quickly.widgets library though (03:11:16 PM) rickspencer3: like if you want to display certain keys from your dictionary, you can use a list of keys to display (03:11:22 PM) rickspencer3: keys = ["key1","key2'] (03:11:22 PM) rickspencer3: dg = DictionaryGrid(my_dict,keys=keys) (03:11:33 PM) rickspencer3: I'll talk about the specifics here a bit later (03:11:47 PM) rickspencer3: but the point is, you can go beyond the one liner if you need to (03:12:08 PM) rickspencer3: so if you start with the one liner, but then need to expand out a bit (03:12:21 PM) rickspencer3: you don't throw away your code, just add some "configure" code (03:12:29 PM) rickspencer3: let's say you get back a feature request from a user or customer, and you need to do something that is not suppported by quickly-widgets through using or configuring (03:12:34 PM) rickspencer3: well, quickly-widgets strives to consume PyGtk in such a way that you can extend classes as needed to accomplish your goals (03:12:42 PM) rickspencer3: for example, if you want a grid that displays data from a certain SQL database somewhere, you can inherit from DictionaryGrid and add the SQL functionality. (03:12:51 PM) rickspencer3: This is exactly what CouchGrid does, but for generally desktopcouchy things (03:12:59 PM) rickspencer3: So, before I discuss using the prompts a bit, any questions? (03:13:10 PM) ClassBot: \sh asked: which audience did you have in mind when you started Quickly? (03:13:11 PM) AndChat is now known as Guest68634 (03:13:23 PM) rickspencer3: well, fundamentally I was scratching my own itch (03:13:31 PM) rickspencer3: I was working on bughugger and thought ... (03:13:43 PM) rickspencer3: "I cannot believe I am writing this same TreeView code *again*" (03:14:00 PM) rickspencer3: I vowed at that moment to never craft a TreeView again (03:14:09 PM) rickspencer3: though I don't mind writing code that writes TreeViews ;) (03:14:21 PM) rickspencer3: beyond me, I am thinking about application writers on Ubuntu (03:14:41 PM) rickspencer3: so I don't mind making some distro specific assumptions (03:15:11 PM) rickspencer3: ultimately, I am thinking about folks who, like me, often can visualize and app, and want to get straight to the fun part (03:15:33 PM) rickspencer3: without bothering with all the boiler plate of a powerful library like Gtk (03:15:37 PM) rickspencer3: more questions? (03:16:00 PM) rickspencer3: ok (03:16:02 PM) rickspencer3: prompts (03:16:03 PM) rickspencer3: Prompts are basically dialogs that you can use to interact with the user without writing too much code (03:16:12 PM) rickspencer3: rather than creating a PyGtk dialog or similar yourself, and then populating it, for certain scenarios a prompt in quickly-widgets can take care of it for you (03:16:17 PM) rickspencer3: the prompts module is right under quickly, so you can get it like this: (03:16:18 PM) rickspencer3: from quickly import prompts (03:16:34 PM) rickspencer3: or just quickly.prompts.string() if you want (03:16:41 PM) rickspencer3: there are three kinds of prompts (03:16:46 PM) rickspencer3: the first kind just displays info the the user (03:16:50 PM) rickspencer3: these take a couple of strings from you and display a message box to the user with the appropriate icon (03:17:01 PM) rickspencer3: so, you can go (03:17:06 PM) rickspencer3: prompts.error("Title for the Error Dialog","Error text") (03:17:23 PM) rickspencer3: this will pop up a little dialog that says "Error text" (03:17:32 PM) rickspencer3: with a dialog error icon next to it (03:17:39 PM) rickspencer3: there is also prompts.warning and prompts.info (03:17:44 PM) rickspencer3: they works the same as error (03:17:55 PM) rickspencer3: The next kind of prompt collects some info from the user (03:18:02 PM) rickspencer3: the kind of prompts built in of this kind are: (03:18:07 PM) rickspencer3: string, date, integer, decimal, and price (03:18:13 PM) rickspencer3: they all take care of displaying an appropriate widget for the user input for you (03:18:31 PM) rickspencer3: like a textbox for a string, a calendar for a date, and a spinner appropriately formatted if appropriate (03:18:38 PM) rickspencer3: they let you set a title, a message, a default value, and in the case of the numeric ones some other defaults if you want (03:18:44 PM) rickspencer3: the functions all return a gtk.RESPONSE and a value (03:18:52 PM) rickspencer3: make sure you check the response because if the user cancelled, you want to do the right thing (03:19:04 PM) rickspencer3: so to get a string, go: (03:19:04 PM) rickspencer3: response, val = quickly.prompts.string() (03:19:04 PM) rickspencer3: if response == gtk.RESPONSE_OK: (03:19:04 PM) rickspencer3: #do something with val (03:19:18 PM) rickspencer3: where val is a string (03:19:25 PM) rickspencer3: you can get a date in the same way (03:19:33 PM) rickspencer3: when dealing with date values are tuples in the form of integers for (year,month,day) (03:19:52 PM) rickspencer3: where month is zero indexed (Jaunary is 0, December is 11) (03:20:19 PM) rickspencer3: and you use tuples for the default value in teh same way (03:20:24 PM) rickspencer3: so: (03:20:24 PM) rickspencer3: response, date = quickly.widgets.date("Title String","Enter your birthday",(1968,03,22)) (03:20:29 PM) rickspencer3: so that's *April* because the months are zero indexed (03:20:37 PM) rickspencer3: if you want to get an integer, you can set min_value, and max_value as well (03:20:42 PM) rickspencer3: if you look at the doc string, you can see other options as well (03:20:47 PM) rickspencer3: decimal and price are also rich in options (03:20:52 PM) rickspencer3: price is just decimal, but with 2 decimal places set (03:20:58 PM) rickspencer3: however, they all work basically the same way, response, val = function() (03:21:10 PM) rickspencer3: if you need to configure the prompt a bit more, for each function, there is a symetrical class that you can use (03:21:20 PM) rickspencer3: so for string(), if you want to own the dialog for some reason, you can use StringPrompt (03:21:24 PM) rickspencer3: sp = StringPrompt(title, text, default_string) (03:21:29 PM) rickspencer3: then you can party on sp if you want (03:21:34 PM) rickspencer3: there is a similar dialog for date, integer, decimal, and price (03:21:40 PM) rickspencer3: each has a "get_value()" function to extract the value (03:21:51 PM) rickspencer3: The last set of prompts are FileChooserDialogs (03:21:56 PM) rickspencer3: they work the same way (03:22:01 PM) rickspencer3: response, path = quickly.prompts.choose_directory(title) (03:22:12 PM) rickspencer3: there is choose_directory, save_image_file, and open_image_file (03:22:19 PM) rickspencer3: there are classes for these as well (03:22:24 PM) rickspencer3: DirectoryChooserDialog, OpenImageDialog, and SaveImageDialog (03:22:33 PM) rickspencer3: these lack get_value, because they are just subclasses of FileChooserDialog (03:22:38 PM) rickspencer3: which already has a get_filename() function (03:22:43 PM) rickspencer3: before I talk about the gtk.Widgets, any questions? (03:23:20 PM) ClassBot: tgalati4 asked: I noticed that quidgets is only packaged for Lucid, no Karmic. Any plans? (03:23:29 PM) rickspencer3: I'm not planning this myself (03:23:41 PM) rickspencer3: but I would be happy to work with anyone who wanted to do this (03:24:14 PM) rickspencer3: btw, I should have mentioned that the package name is quickly-widgets in universe (03:24:31 PM) rickspencer3: alright (03:24:39 PM) rickspencer3: moving on to quickly.widgets (03:24:41 PM) rickspencer3: for widgets, there are a few miscelaneous ones, and then the grid related ones (03:24:46 PM) rickspencer3: I'll start with the miscelaneous ones (03:24:51 PM) rickspencer3: the most miscelaneous one is (03:24:57 PM) rickspencer3: from quickly.widgets.camera_button import CameraButton (03:25:06 PM) rickspencer3: CameraButton wraps up the PyGame web cam API and serves up pixbufs (03:25:13 PM) rickspencer3: I don't like how this is implemented at the moment, and am planning to pull it into it's own package for a while, until I can make it work well (03:25:18 PM) rickspencer3: however, it's kinda fun when it works (03:25:32 PM) rickspencer3: there's also a subclass of button called PressAndHold button (03:25:39 PM) rickspencer3: this one is quite useful and functional (03:25:55 PM) rickspencer3: this guy fires a signal every 250ms, so long as the user is holding the button down (03:26:00 PM) rickspencer3: so this allows you to do something while the user is holding down a button (03:26:04 PM) rickspencer3: I use this in photobomb to manage rotation of times for example (03:26:11 PM) rickspencer3: first, create the button, then connect to the signal (03:26:17 PM) rickspencer3: button = PressAndHoldButton() (03:26:18 PM) rickspencer3: button.connect("tick",action) (03:26:35 PM) rickspencer3: then write a function that does something every time the signal fires (03:26:42 PM) rickspencer3: def action(self, widget=None, data=None): (03:26:49 PM) rickspencer3: #do something every 250ms (03:27:14 PM) rickspencer3: for photobomb "something" is selected_item.rotate() or such (03:27:19 PM) rickspencer3: the last misc. quickly.widget is (03:27:24 PM) rickspencer3: from quickly.widgets.asynch_task_progressbox import AsynchTaskProgressBox (03:27:31 PM) rickspencer3: in general, you should avoid threads in your python app (03:27:35 PM) rickspencer3: you should use things like gobject.timeout_add() or gobject.idle_add() (03:27:40 PM) rickspencer3: and the gio library (03:27:45 PM) rickspencer3: however, for times when you absolutely must use a thread AsynchTaskProgressBox can make it easier for you (03:27:56 PM) rickspencer3: this derives from gtk.ProgressBox, so it supplies some throbbing UI for you if you need it (03:28:07 PM) rickspencer3: the basic idea is to write a function that you want to run on a thread (03:28:11 PM) rickspencer3: I usually call this a task (03:28:17 PM) rickspencer3: create a dictionary of parameters if you want to pass some params to the function (03:28:21 PM) rickspencer3: create the AsynchTaskProgressBox (03:28:27 PM) rickspencer3: if you want to know when the task is done, connect to the complete event (03:28:33 PM) rickspencer3: here's some photobomb code for example (03:28:39 PM) rickspencer3: params = {"directory":directory} (03:28:39 PM) rickspencer3: pb = AsynchTaskProgressBox(self.__load_task, params, False) (03:28:39 PM) rickspencer3: pb.connect("complete",self.__load_task_complete) (03:28:52 PM) rickspencer3: for your task, you can simulate a killable thread (in python you can't really kill a thread, just wait for it to end) (03:29:06 PM) rickspencer3: if every (03:29:15 PM) rickspencer3: if *ever* even (03:29:16 PM) rickspencer3: you do this by checking a special key that is added to params called "kill" (03:29:23 PM) rickspencer3: if it's set to True, you can stop working (03:29:31 PM) rickspencer3: here's a bit of code from photobomb to demonstrate (03:29:39 PM) rickspencer3: def __load_task(self, params): (03:29:39 PM) rickspencer3: pictures_dir = params["directory"] (03:29:39 PM) rickspencer3: images = [] (03:29:39 PM) rickspencer3: files = os.listdir(pictures_dir) (03:29:39 PM) rickspencer3: for f in files: (03:29:40 PM) rickspencer3: try: (03:29:42 PM) rickspencer3: if params["kill"]: (03:29:44 PM) rickspencer3: return None (03:29:46 PM) rickspencer3: #etc.... (03:29:50 PM) rickspencer3: So the last bit about quickly-widgets is about Grids and Filters and stuff (03:29:56 PM) rickspencer3: before I do that, any questions so far? (03:30:34 PM) ClassBot: stevec33 asked: Why is it best to avoid threads? (03:31:51 PM) rickspencer3: because the implentation in Python leaves a lot to be desired (03:31:51 PM) rickspencer3: like there is a global lock and scuh (03:31:52 PM) rickspencer3: they are not true threads that get deployed and run seperately on CPUs and such (03:31:52 PM) rickspencer3: but mostly, it's really hard to write code that does not have bad bugs in it (03:32:19 PM) rickspencer3: this is especially true with PyGtk (03:32:35 PM) rickspencer3: you have to call threads_enter() and threads_leave() and all this when updating the UI (03:32:39 PM) rickspencer3: or it goes quite haywire (03:33:15 PM) rickspencer3: ok (03:33:18 PM) rickspencer3: on to grids (03:33:19 PM) rickspencer3: lots of the code in quickly-widgets is about presenting data to users in a TreeView (03:33:27 PM) rickspencer3: in PyGtk, gtk.TreeView handles both displaying grid data and data in trees (03:33:35 PM) rickspencer3: quickly-widgets focuses on the Grid (03:33:44 PM) rickspencer3: and totally ignores the tree case (03:34:04 PM) rickspencer3: trees are UI elements where there are nested nodes with those little disclosure triangles and such (03:34:12 PM) rickspencer3: so, grids (03:34:13 PM) rickspencer3: the essentially scenario is that you have some tabular data to display (03:34:21 PM) rickspencer3: gtk.TreeView can let you display this in any manner you like (03:34:26 PM) rickspencer3: so long as you: (03:34:33 PM) rickspencer3: 1. Create a TreeView (03:34:39 PM) rickspencer3: 2. Cerate a treeview model store of some kind (03:34:44 PM) rickspencer3: 3. create columns (03:34:49 PM) rickspencer3: 4. create cellrenders for each column (03:34:53 PM) rickspencer3: 5. popluate the treeview model (03:35:09 PM) rickspencer3: each of these steps taking multiple lines of code *per column* (03:35:28 PM) rickspencer3: and this does not include letting users change the values in the grid (03:35:30 PM) rickspencer3: filtering (03:35:38 PM) rickspencer3: adding data after it is created, etc... (03:35:44 PM) rickspencer3: from quickly.widgets.dictionary_grid import DictionaryGrid tries to handle this for you in essentially, one line of code (03:35:53 PM) rickspencer3: the basic interaction is that you create a DictionaryGrid by handing it a list of dictionary, and it makes a TreeView for you (03:36:01 PM) rickspencer3: you just show it and add it to your form (03:36:05 PM) rickspencer3: Here's a bit from the dictionary grid test program (03:36:11 PM) rickspencer3: first, a list of dictionaries (03:36:17 PM) rickspencer3: dicts = [{"key?": True, "price":0.00,"tags" : "aaa bbb ccc","_foo":"bar","bing count":20}, (03:36:17 PM) rickspencer3: {"ID": 11, "key?": False, "price":2.00,"tags" : "bbb ccc ddd","_foo":"bar"}, (03:36:17 PM) rickspencer3: {"key?": True, "price":33.00,"tags" : "ccc ddd eee","_foo":"bar","bing count":15}, (03:36:17 PM) rickspencer3: {"ID": 3, "tags" : "ddd eee fff","_foo":"bar"}, (03:36:17 PM) rickspencer3: {"ID": 4, "price":5.00,"_foo":"bar"}] (03:36:22 PM) rickspencer3: then, create the DictionaryGrid: (03:36:28 PM) rickspencer3: grid = DictionaryGrid(dicts) (03:36:44 PM) rickspencer3: seriously, that;s all it takes to show the data in the dictionaries to a user (03:36:51 PM) rickspencer3: if you want the user to be able to edit the grid, set it to editable: (03:37:01 PM) rickspencer3: grid.editable = True (03:37:08 PM) rickspencer3: note that setting some of the properties, including editable will cause the grid to reset itself (03:37:22 PM) rickspencer3: if it's editable, the user can change the data in the dictionary (03:37:35 PM) rickspencer3: there are some properties that you can use to extract the data (03:37:38 PM) rickspencer3: like (03:37:57 PM) rickspencer3: grid.rows (03:38:00 PM) rickspencer3: this gives you all the rows (03:38:14 PM) rickspencer3: where as (03:38:27 PM) rickspencer3: grid.selected_rows gives you the currently selected row (03:38:50 PM) rickspencer3: some handy functions you get for free, like (03:38:53 PM) rickspencer3: grid.remove_selected_rows() (03:39:27 PM) rickspencer3: remove_selected_rows (03:39:31 PM) rickspencer3: oops (03:39:35 PM) rickspencer3: you can add to your grid by appending rows (03:39:35 PM) rickspencer3: grid.append_row(my_dict) (03:39:44 PM) rickspencer3: if you want to add an empty row (03:39:48 PM) rickspencer3: append_row({}) (03:39:57 PM) rickspencer3: then if the grid is editable, the user can fill itin (03:40:04 PM) rickspencer3: by default, the grid will display a column for every key it encounters UNLESS that key starts with "__" (two underscores), in which case it will be ignored (03:40:15 PM) rickspencer3: if you want to control the keys displayed, you can do this when you create it: (03:40:15 PM) rickspencer3: grid = DictionaryGrid(dicts,list_of_keys) (03:40:25 PM) rickspencer3: or you can set the keys later (03:40:25 PM) rickspencer3: grid.keys = list_of_keys (03:40:31 PM) rickspencer3: this causes the grid to reset, btw (03:40:36 PM) rickspencer3: Columns will sort properly most of the time (03:40:43 PM) rickspencer3: this is accomplished by having each column be a certain type (03:40:50 PM) rickspencer3: there is the default StingColumn, but there is also IntegerColumn, CurrencyColumn, TagsColumn, and CheckColumn (03:41:15 PM) rickspencer3: by "extending" GridColumn you can create your own column types if you need to (03:41:20 PM) rickspencer3: DictionaryGrid tries to guess the right column type to use based on the name of the key (03:41:26 PM) rickspencer3: So if you choose your key names correctly, you can get a lot of good functionality for free (03:41:31 PM) rickspencer3: for example, and key that ends with "?" DictionaryGrid will assume it is a boolean, and use CheckBoxes to show the values (03:41:35 PM) rickspencer3: and provide the appropriate sortign for you (03:41:41 PM) rickspencer3: the key "price" is assumed to be currency (03:41:46 PM) rickspencer3: anything ending in count will be an IntegerColumn, same with a key called "id" (03:41:53 PM) rickspencer3: a key called "tags" will be a tags column (03:41:57 PM) rickspencer3: tags columns are mostly important for filtering, which I will discuss in a moment (03:42:04 PM) rickspencer3: you can control the columntypes manually if you want, by passing in "Type Hints" (03:42:09 PM) rickspencer3: here's some code from the tests: (03:42:17 PM) rickspencer3: keys = ["id","price","bool?","foo"] (03:42:18 PM) rickspencer3: hints = {"id":StringColumn, "price":IntegerColumn, (03:42:18 PM) rickspencer3: "bool?":CurrencyColumn,"foo":CheckColumn} (03:42:18 PM) rickspencer3: dicts = [{"price":100,"id":"asdfas","bool?":10.01,"foo":True}] (03:42:18 PM) rickspencer3: grid = DictionaryGrid(dicts, keys, hints) (03:42:30 PM) rickspencer3: in this case hints are overriding the defaults (03:42:37 PM) rickspencer3: so I should talk about filtering next, but first, questions? (03:43:44 PM) rickspencer3: I hope there are no question be my content is so riveting, and the clarity is so profound (03:43:47 PM) rickspencer3: :) (03:43:49 PM) rickspencer3: moving on ... (03:43:57 PM) rickspencer3: so one cool thing about gtk.TreeView is the filtering capabilities (03:44:02 PM) rickspencer3: Filtering even pretty long lists works really fast (03:44:16 PM) rickspencer3: but this is a TON of work in PyGtk (03:44:18 PM) rickspencer3: from quickly.widgets.grid_filter import GridFilter makes a filter UI for you (03:44:22 PM) rickspencer3: this will let the user add filters to filter columns (03:44:29 PM) rickspencer3: if you've seen the bughugger UI, then you have seen GridFilter and DictionaryGrid in action (03:44:37 PM) rickspencer3: you just need to hand it a Grid to filter (03:44:42 PM) rickspencer3: it will pick up filter types from the GridColumns in the Grid (03:44:47 PM) rickspencer3: So for example columns with strings will get a filter with "contains substring" while a column with numbers will get ">,<.=", etc... (03:45:16 PM) rickspencer3: a tags column gets "contains one of these tags", "contains all of these tags", etc... (03:45:20 PM) rickspencer3: all this just by creating it: (03:45:24 PM) rickspencer3: filt = GridFilter(grid) (03:45:24 PM) rickspencer3: filt.show() (03:45:30 PM) rickspencer3: if you want to override the default filter types, you can pass in hints (03:45:36 PM) rickspencer3: this works pretty much like in the grid (03:45:41 PM) rickspencer3: you pass in a dictionary that matches up keys to filter types (03:45:52 PM) rickspencer3: well, actually to filter combos, but that's a bit more detail than necessary (03:45:56 PM) rickspencer3: there are StringFitlerCombo, TagsFilterCombo, CheckFilterCombo etc... (03:46:02 PM) rickspencer3: there is also blank filter combo that you can use to easily make your own filters (03:46:08 PM) rickspencer3: for example, in bughugger I created a custom filter to filter status and importance like this: (03:46:20 PM) rickspencer3: sw_filter2 = BlankFilterCombo() (03:46:21 PM) rickspencer3: sw_filter2.append("=",lambda x,y: convert_to_num(x) == float(y) ) (03:46:21 PM) rickspencer3: sw_filter2.append("<",lambda x,y: convert_to_num(x) < float(y) ) (03:46:21 PM) rickspencer3: sw_filter2.append(">",lambda x,y: convert_to_num(x) > float(y) ) (03:46:21 PM) rickspencer3: sw_filter2.append("<=",lambda x,y: convert_to_num(x) <= float(y) ) (03:46:21 PM) rickspencer3: sw_filter2.append(">=",lambda x,y: convert_to_num(x) >= float(y) ) (03:46:23 PM) rickspencer3: filter_hints = {"status":sw_filter,"importance":sw_filter2} (03:46:29 PM) rickspencer3: each call to append adds a string and a function call (03:46:51 PM) rickspencer3: the function gets called when the users changes the filter in some way (03:47:08 PM) rickspencer3: the function should return True to show the row (03:47:11 PM) rickspencer3: or False to hide the row (03:47:34 PM) rickspencer3: lastly I'll discuss CouchGrid (03:47:39 PM) rickspencer3: but first, Questions? (03:47:53 PM) ***rickspencer3 tap tap (03:47:56 PM) rickspencer3: is this thing on? (03:48:06 PM) ClassBot: tgalati4 asked: Besides bughugger and photobomb, any other apps using quidgets that we can use for reference? (03:48:12 PM) rickspencer3: I don't know (03:48:23 PM) rickspencer3: I get a lot of questions about using the widgets (03:48:31 PM) rickspencer3: but I haven't tracked what folks are doing with it (03:48:36 PM) rickspencer3: sorry :/ (03:48:55 PM) rickspencer3: ok (03:49:03 PM) rickspencer3: before we break, I'll touch on CouchGrid (03:49:13 PM) rickspencer3: the flagship of the quickly-widgets package (03:49:19 PM) rickspencer3: I feel this has been talked about a lot (03:49:24 PM) rickspencer3: In Lucid, a CouchGrid is a DictionaryGrid (03:49:43 PM) rickspencer3: this is different than the CouchGrid that was desktopcouch.records.couch_grid import CouchGrid (03:50:03 PM) rickspencer3: in Lucid you get all the sorting and filtering of DictionaryGrid, plus persistance in desktopcouch (03:50:10 PM) rickspencer3: you need to define a database and a record type (03:50:15 PM) rickspencer3: if you want, you can pass in a dictionary with some starter data as well (03:50:21 PM) rickspencer3: here is some code from the GridFilter test app (03:50:30 PM) rickspencer3: database_name = "couch_widget_test" (03:50:30 PM) rickspencer3: record_type = "couch_grid_filter_test" (03:50:30 PM) rickspencer3: hints = {} (03:50:30 PM) rickspencer3: grid = CouchGrid(database_name, record_type=record_type, dictionaries=dicts) (03:50:30 PM) rickspencer3: grid.show() (03:50:31 PM) rickspencer3: filt = GridFilter(grid,hints) (03:50:33 PM) rickspencer3: filt.show() (03:50:39 PM) rickspencer3: this sets up a database for you (03:50:49 PM) rickspencer3: if the databaes is there and there are records already (03:50:53 PM) rickspencer3: it displays those for you! (03:51:03 PM) rickspencer3: if there are new records it persists them for you (03:51:11 PM) rickspencer3: so, that (03:51:18 PM) rickspencer3: s the last bit of content I had planned (03:51:19 PM) rickspencer3: Questions? (03:53:42 PM) rickspencer3: okay (03:53:56 PM) rickspencer3: so, if you're hacking and have questions, want to contribute (03:53:57 PM) rickspencer3: etc... (03:54:07 PM) rickspencer3: join #quickly any time and ask away (03:54:22 PM) rickspencer3: again, the project is early, so feedback and contributions can have a big impact at this point (03:54:44 PM) rickspencer3: this is community developed software, so it's there to be molded by the community :) (03:54:54 PM) ClassBot: tgalati4 asked: What's on the roadmap on quidgets? What's the burning next todo item? (03:55:01 PM) rickspencer3: thanks for the question tgalati4 (03:55:27 PM) rickspencer3: next step is to get some feedback on the Grid related stuff (03:55:30 PM) rickspencer3: and fix bugs (03:55:36 PM) rickspencer3: I think that saves the most work there (03:55:50 PM) rickspencer3: also, I'd like to make the web cam button good (03:56:01 PM) rickspencer3: someone yesterday suggest gstreamer, I should give that a try (03:56:12 PM) rickspencer3: also, there may be more file chooser dialogs for the prompts (03:56:32 PM) rickspencer3: I'd like to add any widgets that folks like that they would like to find an upstream home for (03:56:47 PM) rickspencer3: but one of the most immediate burning things for me is to write documentation (03:58:29 PM) rickspencer3: please note that there is a fair amount of documentation already in the code itself (03:58:42 PM) rickspencer3: and you can access this with pydocs (03:59:15 PM) rickspencer3: thanks everyone! (03:59:35 PM) rickspencer3: I'm looking forward to working with folks to make quickly-widgets more functional and more easy and more fun (03:59:36 PM) rickspencer3: :)
MeetingLogs/OpWeek1003/QuicklyWidgets (last edited 2010-03-04 21:01:37 by pool-71-182-100-128)