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)