Merge lp:~mbudde/earcandy/reorganization into lp:earcandy

Proposed by Michael Budde
Status: Merged
Merge reported by: KillerKiwi
Merged at revision: not available
Proposed branch: lp:~mbudde/earcandy/reorganization
Merge into: lp:earcandy
Diff against target: None lines
To merge this branch: bzr merge lp:~mbudde/earcandy/reorganization
Reviewer Review Type Date Requested Status
KillerKiwi Approve
Review via email: mp+6748@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Budde (mbudde) wrote :

In this branch the tree has been reorganized to make the application a package (earcandylib). ear_candy.py has been moved to earcandylib/EarCandy.py and a earcandy script for running the application has been created. Data files have been moved to data/.

A distutils script (setup.py) has also been added. A tar ball can now be created with ./setup.py sdist.

A .desktop file has been added for easily running the application when it has been installed.

Revision history for this message
David D Lowe (flimm) wrote :

Hello Michael Budde! It seems like we were working on the same bug #379266 in parallel, so a lot of our work is similar, if not identical (earcandy.desktop has the exact same comment!).
So I'm going to comment on the changes you've made versus what's now in the trunk (lp:earcandy revision 50).

I find your approach to the runnable script earcandy interesting. You basically moved the code under if __name__ == "__main__" from the EarCandy.py to the script. Theoretically, this is the best method, practically, I've found from my personal experience with Epidermis that it makes namespaces rather confusing. The other approach which I took calls the script by running subprocess.call(['python', ear_candy.py']), creating two python processes one depending on the other, which is a bit of a waste but makes the result in System Monitor prettier. The final approach (that I can think of) is to launch it by running subprocess.Popen(['earcandy.py']) which is lighter on resources but less pretty in System Monitor.

Since you're using distutils, the data files will be separated from the .py files, so you created a function called find_data_file, similar to my find_program_file. You check to see in which directory of /usr/ or /usr/local/ the files are installed. This actually superfluous, they're always going to be under sys.prefix, according to the distutils documentation.

Having a seperate EarCandyInfo.py file for the application's metadata is certainly interesting. Having one place to modify the program's version number is nice, but do you really need it for things like APPNAME?

I'm going to go ahead and comment on your Ubuntu packaging branch too, lp:~mbudde/earcandy/ubuntu revision 59 if you don't mind. You included a dependency which I somehow forgot: pulseaudio, well done! Having the Ubuntu packaging in a separate branch from the trunk is an interesting idea, and one which a lot of projects go by. I personally prefer having the debian/ folder in the original source, keeps the developers responsible for the packaging and things like dependencies.

Obviously, I'm not the developer of EarCandy, it'll be up to KillerKiwi to decide what to take and what to keep. Thanks for contributing!

Revision history for this message
Michael Budde (mbudde) wrote :
Download full text (3.9 KiB)

> Hello Michael Budde! It seems like we were working on the same bug #379266 in
> parallel, so a lot of our work is similar, if not identical (earcandy.desktop
> has the exact same comment!).
> So I'm going to comment on the changes you've made versus what's now in the
> trunk (lp:earcandy revision 50).
>

Yeah, I noticed your branch the day after I started my branch. I've certainly
got some inspiration from your branch, hope you don't mind that ;)

> I find your approach to the runnable script earcandy interesting. You
> basically moved the code under if __name__ == "__main__" from the EarCandy.py
> to the script. Theoretically, this is the best method, practically, I've found
> from my personal experience with Epidermis that it makes namespaces rather
> confusing. The other approach which I took calls the script by running
> subprocess.call(['python', ear_candy.py']), creating two python processes one
> depending on the other, which is a bit of a waste but makes the result in
> System Monitor prettier. The final approach (that I can think of) is to launch
> it by running subprocess.Popen(['earcandy.py']) which is lighter on resources
> but less pretty in System Monitor.

I'm not sure what you mean when you say that it "makes namespaces rather confusing".
Do you have any examples where your method would be prefered to mine?

> Since you're using distutils, the data files will be separated from the .py
> files, so you created a function called find_data_file, similar to my
> find_program_file. You check to see in which directory of /usr/ or /usr/local/
> the files are installed. This actually superfluous, they're always going to be
> under sys.prefix, according to the distutils documentation.

Running setup.py in the source tree gives me this:
$ ./setup.py -n install
running install
[..snip..]
running install_data
creating /usr/local/share/earcandy
copying data/earsLabel.png -> /usr/local/share/earcandy
copying data/pulseoptions.glade -> /usr/local/share/earcandy
copying data/settings.xml -> /usr/local/share/earcandy
copying data/earsLabelOff.png -> /usr/local/share/earcandy
copying earcandy.desktop -> /usr/local/share/applications
creating /usr/local/share/pixmaps
copying data/earsLabel.png -> /usr/local/share/pixmaps
running install_egg_info
Writing /usr/local/lib/python2.6/dist-packages/earcandy-0.5.egg-info

Also I don't quite like how you've copied find_program_file() to every source file
that needs it. Right now it's a pain to edit that function as you have to do it in three
places. Why not put it in a separate file and import it?

> Having a seperate EarCandyInfo.py file for the application's metadata is
> certainly interesting. Having one place to modify the program's version number
> is nice, but do you really need it for things like APPNAME?

You're right. That probably is overkill.

> I'm going to go ahead and comment on your Ubuntu packaging branch too,
> lp:~mbudde/earcandy/ubuntu revision 59 if you don't mind. You included a
> dependency which I somehow forgot: pulseaudio, well done! Having the Ubuntu
> packaging in a separate branch from the trunk is an interesting idea, and one
> which a lot of projects go by. I personally ...

Read more...

Revision history for this message
KillerKiwi (killerkiwi2005) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'MANIFEST.in'
2--- MANIFEST.in 1970-01-01 00:00:00 +0000
3+++ MANIFEST.in 2009-05-23 12:12:46 +0000
4@@ -0,0 +1,2 @@
5+include earcandy earcandy.desktop MANIFEST.in
6+recursive-include data *
7
8=== added directory 'data'
9=== renamed file 'earsLabel.png' => 'data/earsLabel.png'
10=== renamed file 'earsLabelOff.png' => 'data/earsLabelOff.png'
11=== renamed file 'pulseoptions.glade' => 'data/pulseoptions.glade'
12=== renamed file 'settings.xml' => 'data/settings.xml'
13=== added file 'earcandy'
14--- earcandy 1970-01-01 00:00:00 +0000
15+++ earcandy 2009-05-22 14:09:54 +0000
16@@ -0,0 +1,8 @@
17+#!/usr/bin/env python
18+
19+from earcandylib.EarCandy import EarCandy
20+
21+if __name__ == '__main__':
22+ ec = EarCandy()
23+ ec.run()
24+ ec.save()
25
26=== added file 'earcandy.desktop'
27--- earcandy.desktop 1970-01-01 00:00:00 +0000
28+++ earcandy.desktop 2009-05-23 12:12:46 +0000
29@@ -0,0 +1,12 @@
30+[Desktop Entry]
31+Version=1.0
32+Encoding=UTF-8
33+Name=Ear Candy
34+Comment=Gracefully fade applications' volume in and out
35+Generic Name=Sound level manager
36+Terminal=false
37+Icon=earsLabel
38+Type=Application
39+Exec=earcandy
40+Categories=Utility;
41+StartupNotify=True
42
43=== added directory 'earcandylib'
44=== renamed file 'Client.py' => 'earcandylib/Client.py'
45--- Client.py 2009-05-21 19:43:15 +0000
46+++ earcandylib/Client.py 2009-05-22 15:58:21 +0000
47@@ -17,7 +17,7 @@
48 self.category = "" # music | video | phone
49 self.window_position_fade = False
50
51- self.volume_step = 2
52+ self.volume_step = 2
53 self.has_focus = False
54 self.fullscreen = False
55 self.icon = None
56@@ -60,7 +60,7 @@
57 # check meter levels on others
58 for sink in self.sinks.values():
59 if sink.volume_meter > 0: return True
60-
61+
62 # HACK: Check how long its been inactive... and if more than a second count as expired
63 if timestamp - sink.volume_meter_last_non_zero < 1:
64 return True
65@@ -107,7 +107,7 @@
66
67 def __fade_mute(self):
68 self.volume_target = self.volume_step # if we goto 0 than our volume meter will never register a value
69-
70+
71 def set_primary(self, value):
72 if value or self.category == "default":
73 self.__fade_in()
74@@ -127,7 +127,7 @@
75 el.setAttribute("category",self.category)
76 el.setAttribute("window_position_fade", str(self.window_position_fade))
77 el.setAttribute("icon_name", str(self.icon_name))
78-
79+
80 def from_xml(self, el):
81
82 self.name = el.getAttribute("name")
83
84=== renamed file 'DesktopFiles.py' => 'earcandylib/DesktopFiles.py'
85=== renamed file 'ear_candy' => 'earcandylib/EarCandy.py' (properties changed: +x to -x)
86--- ear_candy 2009-05-21 19:43:15 +0000
87+++ earcandylib/EarCandy.py 2009-05-22 15:58:21 +0000
88@@ -16,19 +16,20 @@
89 from xml.dom.minidom import *
90
91 from window.WindowWatcher import WindowWatcher
92-from pulseaudio.PulseAudio import PulseAudio
93+from pulseaudio.PulseAudio import PulseAudio
94 from Client import Client
95 from Sink import Sink
96 from Threads import threaded
97 from TrayIcon import EarCandyStatusIcon
98-from EarCandyPrefs import EarCandayPref
99-from VolumeSlider import EarCandyVolumeSlider
100+from EarCandyPrefs import EarCandayPref
101+from VolumeSlider import EarCandyVolumeSlider
102 from EarCandyDBus import EarCandyDBusClient
103+from Paths import get_data_path
104
105 # Turn on gtk threading
106 gtk.gdk.threads_init()
107
108-gtk.window_set_default_icon_from_file(os.path.join(os.path.dirname(__file__), "earsLabel.png"))
109+gtk.window_set_default_icon_from_file(get_data_path("earsLabel.png"))
110
111 # hack to get around 0.2 not have alsaaudio.cards()
112 def reset_alsa_volumes(value=100):
113@@ -50,7 +51,7 @@
114
115
116 for track in mixer.list_tracks():
117-
118+
119 if track.flags & gst.interfaces.MIXER_TRACK_OUTPUT and track.num_channels > 0:
120 print "|", track.label
121
122@@ -85,7 +86,7 @@
123 rules.appendChild(el)
124 client.to_xml(el)
125 skip.append(client.name)
126-
127+
128
129 doc.documentElement.setAttribute("fade_timer_speed", str(self.fade_timer_speed))
130 doc.documentElement.setAttribute("mute_level", str(self.mute_level))
131@@ -146,8 +147,8 @@
132 self.active = True
133 self.display = {"": "[ unknown ]", "phone" : "Phone (VoIP)", "video" : "Video Player", "music" : "Music Player", "default" : "Notification" }
134 self.ignore = ["EsounD client (UNIX socket client)", "ear-candy", "Native client (UNIX socket client)"]
135- self.config_file = os.path.expanduser("~/.config/Ear Candy/settings.xml")
136- self.default_config_file = os.path.join(os.path.dirname(__file__),"settings.xml")
137+ self.config_file = os.path.expanduser("~/.config/Ear Candy/settings.xml")
138+ self.default_config_file = get_data_path("settings.xml")
139
140 self.pa_clients = {} # clients by name
141 self.pa_clients_by_id = {} # clients by id
142@@ -161,13 +162,11 @@
143
144 self.pref = None
145
146- self.fade_timer_speed = 0.1
147+ self.fade_timer_speed = 0.1
148 self.mute_level = 20
149 self.follow_new_outputs = True
150
151- self.priority = { "" : [], "phone" : [], "video" : [], "music" : [] }
152-
153- pass
154+ self.priority = { "" : [], "phone" : [], "video" : [], "music" : [] }
155
156 def run(self):
157 self.ecb = EarCandyDBusClient(self)
158@@ -179,7 +178,7 @@
159 else:
160
161 self.tray = EarCandyStatusIcon(self)
162-
163+
164 self.slider = EarCandyVolumeSlider(self)
165
166 self.ecb.start_service()
167@@ -187,7 +186,7 @@
168 self.pa = PulseAudio( self.on_new_pa_client, self.on_remove_pa_client, self.on_new_pa_sink, self.on_remove_pa_sink, self.on_new_pa_output, self.on_remove_pa_output, self.on_volume_change, self.pa_volume_meter)
169 self.ww = WindowWatcher(self.on_active_window_change)
170 self.fade_timer()
171-
172+
173 gtk.main()
174
175
176@@ -207,7 +206,7 @@
177 def fade_timer(self):
178 while True:
179 time.sleep(self.fade_timer_speed)
180- gobject.idle_add(self.adjust_volumes)
181+ gobject.idle_add(self.adjust_volumes)
182
183 def adjust_volumes(self):
184
185@@ -217,11 +216,11 @@
186 for sink in self.pa_sinks.values():
187 if sink.set_volume():
188 # set pa volume
189- self.pa.set_sink_volume(sink.index, sink.volume, sink.channels)
190+ self.pa.set_sink_volume(sink.index, sink.volume, sink.channels)
191
192 def __set_primary_client(self):
193 # Apply fade rules
194-
195+
196 if self.active:
197 for client in self.pa_clients.values():
198 # self.pref.update_client( client )
199@@ -256,7 +255,7 @@
200 flagFound = False
201 for key in self.priority.keys():
202 if key:
203- for client in self.priority[key]:
204+ for client in self.priority[key]:
205
206 if not flagFound and self.active and client.is_active():
207 # Toggle old client to inactive
208@@ -271,7 +270,7 @@
209 # all clients with no category should be treated as active for volume
210 for client in self.priority[key]:
211 client.set_primary(True)
212-
213+
214
215 def on_volume_change(self, level):
216 self.slider.set_volume( level )
217@@ -281,7 +280,7 @@
218 # /desktop/gnome/sound/default_mixer_device
219
220 del( self.pa_outputs[index] )
221-
222+
223 # fall back to previous value...
224 for value in self.pa_outputs.values():
225 self.set_last_output(value)
226@@ -289,7 +288,7 @@
227
228 def set_last_output(self, name):
229 self.last_output_name = name
230-
231+
232 # Update gconf key that governs multimedia key controls
233 gconf_key = "/desktop/gnome/sound/default_mixer_device"
234 prefix = "pulsemixer:"
235@@ -299,12 +298,12 @@
236 print
237 print "== Change gnome default sound device =="
238 client.set_string(gconf_key, prefix + name)
239-
240+
241
242 def on_new_pa_output(self, index, output_name, startup):
243 self.pa_outputs[index] = output_name
244 self.save()
245-
246+
247 if self.follow_new_outputs and (self.last_output_name == "" or output_name.lower().count("usb") > 0):
248 self.set_last_output(output_name)
249 # Move all streams to the new output ;)
250@@ -318,7 +317,7 @@
251 def on_new_pa_sink(self, index, name, client_index, volume, sink, channels):
252
253 if not self.pa_sinks.has_key(index):
254- print
255+ print
256 print "== pa sink input =="
257 print "sink:", index, name
258 if self.pa_clients_by_id.has_key(client_index):
259@@ -329,13 +328,13 @@
260 client.sinks[index] = sink
261 self.pa_sinks[index] = sink
262 # insure the sink input is on the correct output..
263- if self.follow_new_outputs and self.last_output_name:
264+ if self.follow_new_outputs and self.last_output_name:
265 self.pa.move_sink(sink.index, self.last_output_name)
266 else:
267 return
268 else:
269 self.pa_sinks[index].volume = volume
270-
271+
272 if self.pref:
273 self.pref.update_client( self.pa_clients_by_id[client_index] )
274 self.on_active_window_change( self.last_application, "pa")
275@@ -344,10 +343,11 @@
276 if self.pa_sinks.has_key(index):
277 sink = self.pa_sinks[index]
278 sink.volume_meter = level
279- if(level > sink.client.volume_step): sink.volume_meter_last_non_zero = time.mktime(datetime.datetime.now().timetuple())
280+ if (level > sink.client.volume_step):
281+ sink.volume_meter_last_non_zero = time.mktime(datetime.datetime.now().timetuple())
282
283 def on_remove_pa_sink(self, index):
284- print
285+ print
286 print "== pa remove sink input =="
287 print index
288 if self.pa_sinks.has_key(index):
289@@ -357,7 +357,7 @@
290 if self.pref:
291 self.pref.update_client( client )
292 self.on_active_window_change( self.last_application, "pa")
293-
294+
295
296 def get_unregistered_clients(self):
297 clients = []
298@@ -371,11 +371,11 @@
299 return clients
300
301 def on_new_pa_client(self, index, name):
302- print
303+ print
304 print "== pa sink client =="
305 print name
306 # Link all clients with same name into same object
307- if not self.pa_clients.has_key(index) or not self.pa_clients[index].name == name:
308+ if not self.pa_clients.has_key(index) or not self.pa_clients[index].name == name:
309 if not self.pa_clients.has_key(name):
310 client = Client(self, name)
311 self.pa_clients[name] = client
312@@ -419,10 +419,10 @@
313 self.last_application = application
314
315 def match_client_to_application(self, client, application, index=0):
316- if client.test_focus_window(application.window_name, application.command, application.name):
317+ if client.test_focus_window(application.window_name, application.command, application.name):
318 #print "MATCH pa client:", client.name, index
319
320- client.fullscreen = application.fullscreen
321+ client.fullscreen = application.fullscreen
322 client.icon = application.icon
323 client.icon_name = application.icon_name
324 client.description = application.description
325@@ -447,13 +447,3 @@
326 if name.startswith(alsa_plugin):
327 name = name[len(alsa_plugin): -1]
328 return name
329-
330-
331-if __name__ == '__main__':
332-
333- os.chdir(os.path.dirname(sys.argv[0]))
334-
335- ec = EarCandy()
336- ec.run()
337- ec.save()
338-
339
340=== renamed file 'EarCandyAppSelect.py' => 'earcandylib/EarCandyAppSelect.py'
341--- EarCandyAppSelect.py 2009-02-20 02:08:20 +0000
342+++ earcandylib/EarCandyAppSelect.py 2009-05-22 15:58:21 +0000
343@@ -16,14 +16,15 @@
344 from glade_window import GladeWindow
345 from window.WindowWatcher import WindowWatcher
346 from Client import Client
347+from Paths import get_data_path
348
349 class EarCandyAppSelect(GladeWindow):
350 def __init__(self, core):
351-
352+
353 self.core = core
354-
355- GladeWindow.__init__(self, "pulseoptions.glade", "dialog_select")
356-
357+
358+ GladeWindow.__init__(self, get_data_path("pulseoptions.glade"), "dialog_select")
359+
360 self.combobox_clients = self.wtree.get_widget("combobox_clients")
361 self.treeview_applications = self.wtree.get_widget("treeview_applications")
362 self.combobox_category = self.wtree.get_widget("combobox_category")
363@@ -33,7 +34,7 @@
364 "on_button_cancel_clicked" : self.on_button_cancel_clicked
365 }
366 self.wtree.signal_autoconnect(signals)
367-
368+
369 # Setup tree
370 column = gtk.TreeViewColumn((''))
371 column.set_spacing(4)
372@@ -48,8 +49,8 @@
373 column.set_attributes(cell, markup=1)
374 self.treeview_applications.append_column(column)
375 self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, object)
376- self.treeview_applications.set_model(self.store)
377- self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)
378+ self.treeview_applications.set_model(self.store)
379+ self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)
380
381 self.update()
382
383@@ -65,8 +66,8 @@
384 if self.store.get_value(iter, 1) == app_name: return
385 iter = self.store.iter_next(iter)
386
387- self.store.append(([icon, app_name, client]))
388-
389+ self.store.append(([icon, app_name, client]))
390+
391 def update(self):
392
393 icon_theme = gtk.icon_theme_get_default()
394@@ -76,7 +77,7 @@
395 self.combobox_clients.append_text(client.name)
396
397 for app in self.core.ww.applications.values():
398- self.store.append(([app.icon, app.name, app]))
399+ self.store.append(([app.icon, app.name, app]))
400
401 self.combobox_clients.set_active(0)
402
403@@ -88,7 +89,7 @@
404 def on_button_add_clicked(self, widget):
405
406 client = self.core.pa_clients[self.combobox_clients.get_active_text()]
407-
408+
409
410 # override with selected app
411 model, iter = self.treeview_applications.get_selection().get_selected()
412@@ -107,6 +108,6 @@
413
414 self.core.pref.update_client( client )
415 self.stop()
416-
417+
418 def on_button_cancel_clicked(self, widget):
419 self.stop()
420
421=== renamed file 'EarCandyDBus.py' => 'earcandylib/EarCandyDBus.py'
422--- EarCandyDBus.py 2009-05-19 04:28:48 +0000
423+++ earcandylib/EarCandyDBus.py 2009-05-22 15:58:21 +0000
424@@ -6,10 +6,9 @@
425 DBUS_KEY = "org.earCandy.EarCandyDBus2"
426
427 class EarCandyDBusClient(dbus.service.Object):
428-
429+
430 def __init__(self, core):
431 self.core = core
432-
433
434 def is_running(self):
435 dbus.mainloop.glib.DBusGMainLoop (set_as_default=True)
436@@ -34,7 +33,7 @@
437 dbus.mainloop.glib.DBusGMainLoop (set_as_default=True)
438 bus = dbus.SessionBus ()
439 dbus.service.Object.__init__(self, bus, "/", DBUS_KEY)
440- print
441+ print
442 print "registered as dbus service..."
443 print
444
445
446=== added file 'earcandylib/EarCandyInfo.py'
447--- earcandylib/EarCandyInfo.py 1970-01-01 00:00:00 +0000
448+++ earcandylib/EarCandyInfo.py 2009-05-23 11:53:46 +0000
449@@ -0,0 +1,8 @@
450+NAME = "Ear Candy"
451+APPNAME = "earcandy"
452+VERSION = "0.5"
453+DESC = "A PulseAudio volume manager"
454+WEBSITE = "http://edge.launchpad.net/earcandy"
455+AUTHOR = "Jason Taylor"
456+AUTHOR_EMAIL = "killerkiwi2005@gmail.com"
457+LICENSE = "GNU GPL v2"
458
459=== renamed file 'EarCandyPrefs.py' => 'earcandylib/EarCandyPrefs.py'
460--- EarCandyPrefs.py 2009-05-19 04:28:48 +0000
461+++ earcandylib/EarCandyPrefs.py 2009-05-22 15:58:21 +0000
462@@ -17,15 +17,16 @@
463 from Threads import threaded
464 from glade_window import GladeWindow
465 from EarCandyAppSelect import EarCandyAppSelect
466+from Paths import get_data_path
467
468 class EarCandayPref(GladeWindow):
469 def __init__(self, core):
470-
471+
472 self.core = core
473
474 self.vals = {"" : 0, "music" : 1, "video" : 2, "phone": 3}
475
476- GladeWindow.__init__(self, "pulseoptions.glade", "dialog_options")
477+ GladeWindow.__init__(self, get_data_path("pulseoptions.glade"), "dialog_options")
478
479 self.treeview_pulse_clients = self.wtree.get_widget("treeview_pulse_clients")
480 self.label_pulse_client = self.wtree.get_widget("label_pulse_client")
481@@ -63,9 +64,9 @@
482 "on_comboboxentry_window_match_changed" : self.on_comboboxentry_window_match_changed,
483 "on_entry_pulse_client_description_changed" : self.on_entry_pulse_client_description_changed,
484 "on_toolbutton_delete_clicked" : self.on_toolbutton_delete_clicked,
485-
486- "on_hscale_mute_level_value_changed" : self.on_hscale_mute_level_value_changed,
487- "on_hscale_fade_value_changed" : self.on_hscale_fade_value_changed,
488+
489+ "on_hscale_mute_level_value_changed" : self.on_hscale_mute_level_value_changed,
490+ "on_hscale_fade_value_changed" : self.on_hscale_fade_value_changed,
491
492 "on_close_button_clicked" : self.on_close_button_clicked,
493 "on_button_add_new_clicked" : self.on_button_add_new_clicked,
494@@ -74,7 +75,7 @@
495 "on_checkbutton_output_toggled" : self.on_checkbutton_output_toggled
496 }
497 self.wtree.signal_autoconnect(signals)
498-
499+
500 # Setup tree
501 #column = gtk.TreeViewColumn('Target', gtk.CellRendererProgress(), value=4)
502 #self.treeview_pulse_clients.append_column(column)
503@@ -145,7 +146,7 @@
504
505 self.selected_client = model.get_value(iter, 5)
506 self.selected_client.window_position_fade = model[path][3]
507-
508+
509 def on_hscale_mute_level_value_changed(self, widget):
510 self.core.mute_level = self.hscale_mute_level.get_value()
511 print "Mute Level ", self.core.mute_level
512@@ -170,7 +171,8 @@
513 model, iter = self.treeview_pulse_clients.get_selection().get_selected()
514 if not iter: return
515 self.selected_client = model.get_value(iter, 2)
516- md = gtk.MessageDialog(None, flags=0, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format="Are you sure you want to delete this rule?")
517+ md = gtk.MessageDialog(None, flags=0, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO,
518+ message_format="Are you sure you want to delete this rule?")
519 result = md.run()
520 md.destroy()
521 if result == gtk.RESPONSE_YES:
522@@ -179,17 +181,20 @@
523 del self.core.pa_clients[ key ]
524 self.store.remove(iter)
525 self.core.save()
526-
527+
528 def on_button_add_new_clicked(self, widget):
529-
530+
531 if len(self.core.get_unregistered_clients()) > 0:
532-
533+
534 ecas = EarCandyAppSelect(self.core)
535 client = ecas.run()
536 if client:
537 self.update_client( client )
538 else:
539- md = gtk.MessageDialog(self.window, flags=0, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK, message_format="There are currently no pulseaudio streams unassigned,\n\nYou will need to start your sound application playing first")
540+ md = gtk.MessageDialog(self.window, flags=0,
541+ type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK,
542+ message_format="There are currently no PulseAudio streams assigned.\n\n"
543+ "You will need to start your sound application playing first")
544 result = md.run()
545 md.destroy()
546
547@@ -201,9 +206,9 @@
548
549 def on_entry_command_changed(self, widget):
550 self.current_client.rule_re_command = re.compile(widget.get_text(), re.IGNORECASE)
551-
552+
553 def on_entry_window_title_changed(self, widget):
554- self.current_client.rule_re_window_title = re.compile(widget.get_text(), re.IGNORECASE)
555+ self.current_client.rule_re_window_title = re.compile(widget.get_text(), re.IGNORECASE)
556
557 def on_combobox_profile_changed(self, widget):
558 for key in self.vals.keys():
559@@ -213,7 +218,7 @@
560
561 def on_vscale_volume_change_value(self, widget, a, b):
562 self.current_client.volume_default = self.vscale_volume.get_value()
563-
564+
565 def on_vscale_mute_change_value(self, widget, a, b):
566 self.current_client.volume_mute = self.vscale_mute.get_value()
567
568@@ -222,24 +227,24 @@
569 if cvh.description:
570 text = cvh.description
571 model.set_value(iter,1, text)
572-
573- def on_entry_pulse_client_description_changed(self, widget):
574+
575+ def on_entry_pulse_client_description_changed(self, widget):
576 model, iter = self.treeview_pulse_clients.get_selection().get_selected()
577- cvh = model.get_value(iter, 5)
578+ cvh = model.get_value(iter, 5)
579 #cvh.description = self.entry_pulse_client_description.get_text()
580 self.update_label(cvh, model, iter)
581-
582+
583 def on_comboboxentry_window_match_changed(self, widget):
584 model, iter = self.treeview_pulse_clients.get_selection().get_selected()
585- cvh = model.get_value(iter, 5)
586+ cvh = model.get_value(iter, 5)
587 cvh.window_name_rule = self.comboboxentry_window_match.get_text()
588-
589+
590 def on_treeview_pulse_clients_cursor_changed(self, widget):
591 model, iter = self.treeview_pulse_clients.get_selection().get_selected()
592 if not iter: return
593 self.selected_client = model.get_value(iter, 5)
594 self.__update_detail( self.selected_client)
595-
596+
597 def on_checkbutton_channel_window_toggled(self, widget):
598 self.current_client.window_position_fade = self.checkbutton_channel_window.get_active()
599
600@@ -249,7 +254,6 @@
601
602 def on_close_button_clicked(self,widget=None):
603 self.stop()
604-
605
606 def __get_title(self, client):
607 text = client.name
608@@ -262,15 +266,17 @@
609 client.iter = None
610
611 self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, str, bool, int, object)
612- self.treeview_pulse_clients.set_model(self.store)
613+ self.treeview_pulse_clients.set_model(self.store)
614
615 icon_theme = gtk.icon_theme_get_default()
616 icon = icon_theme.lookup_icon("audio-volume-medium", 32, 0).load_icon()
617 for client in self.core.pa_clients.values() :
618 if client.category and client.is_active() and not (client.name in self.core.ignore):
619- client.iter = self.store.append(([client.icon or icon, self.__get_title(client), self.core.display[client.category], client.window_position_fade, 50, client]))
620+ client.iter = self.store.append(([client.icon or icon, self.__get_title(client),
621+ self.core.display[client.category],
622+ client.window_position_fade, 50, client]))
623
624- self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)
625+ self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)
626
627 def update_client(self, client):
628
629@@ -303,12 +309,15 @@
630 if client.has_rule() and not client.name in self.core.ignore:
631 icon_theme = gtk.icon_theme_get_default()
632 icon = icon_theme.lookup_icon("audio-volume-medium", 32, 0).load_icon()
633- client.iter = self.store.append(([client.icon or icon, self.__get_title(client), self.core.display[client.category], client.window_position_fade, client.get_volume(), client]))
634+ client.iter = self.store.append(([client.icon or icon, self.__get_title(client),
635+ self.core.display[client.category],
636+ client.window_position_fade, client.get_volume(),
637+ client]))
638
639 self.store.set_sort_column_id(1,gtk.SORT_ASCENDING)
640
641 def run(self):
642- self.window.show()
643+ self.window.show()
644 self.hscale_mute_level.set_value(self.core.mute_level)
645 self.hscale_fade.set_value(self.core.fade_timer_speed)
646 self.checkbutton_tray.set_active( self.core.tray.get_visible() )
647@@ -320,19 +329,14 @@
648 self.core.save()
649 self.core.close_preferances()
650 self.window.destroy()
651-
652+
653 @threaded
654 def remove_client_on_timer(self, client):
655 #time.sleep(60)
656 #gobject.idle_add(self.remove_client, client)
657 return
658-
659+
660 def remove_client(self, client):
661 if not client.is_active() and client.iter :
662 self.store.remove(client.iter)
663 client.iter = None
664-
665-
666-
667-
668-
669
670=== renamed file 'Freedesktop.py' => 'earcandylib/Freedesktop.py'
671--- Freedesktop.py 2009-02-06 10:31:04 +0000
672+++ earcandylib/Freedesktop.py 2009-05-22 15:58:21 +0000
673@@ -81,7 +81,7 @@
674 items.sort()
675 for item in items:
676 file_object.write(item[0] + '=' + item[1] + '\n')
677-
678+
679 # turn command into an array
680 def get_exec_array(self):
681 command = []
682@@ -89,4 +89,3 @@
683 for r in re.finditer(p, self.get("Exec")):
684 command.append( str(r.group(0)) )
685 return command
686-
687
688=== added file 'earcandylib/Paths.py'
689--- earcandylib/Paths.py 1970-01-01 00:00:00 +0000
690+++ earcandylib/Paths.py 2009-05-23 12:53:07 +0000
691@@ -0,0 +1,27 @@
692+
693+from os.path import isdir, isfile, join, dirname, abspath
694+
695+
696+DATA_DIR = None
697+
698+
699+_path = abspath(join(dirname(__file__), '..', 'data'))
700+if isdir(_path):
701+ DATA_DIR = _path
702+else:
703+ # We are installed on the system
704+ for prefix in ["/usr", "/usr/local"]:
705+ _path = join(prefix, "share", "earcandy")
706+ if isdir(_path):
707+ DATA_DIR = _path
708+
709+if DATA_DIR == None:
710+ raise IOError, "cannot find data directory"
711+
712+
713+def get_data_path(filename):
714+ path = join(DATA_DIR, filename)
715+ if isfile(path):
716+ return path
717+ else:
718+ raise IOError, "file not found"
719
720=== renamed file 'Sink.py' => 'earcandylib/Sink.py'
721--- Sink.py 2009-05-19 04:28:48 +0000
722+++ earcandylib/Sink.py 2009-05-22 15:58:21 +0000
723@@ -1,6 +1,6 @@
724-import math
725+import math
726 import time
727-
728+
729 class Sink():
730 def __init__(self, index, name, volume, client, channels=1):
731
732@@ -15,7 +15,7 @@
733
734 self.__previous_left = 0
735 self.__previous_right = 0
736-
737+
738 def set_volume(self):
739
740 if len(self.volume) > 2:
741@@ -26,9 +26,9 @@
742 # Mono channel
743 target_volume = round(self.volume[1])
744
745- if target_volume < self.client.volume_target:
746+ if target_volume < self.client.volume_target:
747 target_volume = round(target_volume + self.client.volume_step)
748- elif target_volume > self.client.volume_target:
749+ elif target_volume > self.client.volume_target:
750 target_volume = round(target_volume - self.client.volume_step)
751
752 if len(self.volume) > 2:
753@@ -38,7 +38,7 @@
754 if len(self.volume) > 2:
755 if self.client.balance < 0: left = math.fabs(target_volume - (target_volume / 100 * self.client.balance))
756 if self.client.balance > 0: right = math.fabs(target_volume - (target_volume / 100 * self.client.balance))
757-
758+
759 if self.volume[1] < left:
760 self.volume[1] = round(self.volume[1]) + self.client.volume_step
761 if self.volume[1] > left:
762@@ -57,20 +57,20 @@
763 else:
764 self.volume[1] = round(target_volume)
765 self.volume.append(target_volume) #FIXME: remove second channel volume
766-
767+
768 # we dont want to get stuck in a loop because volumes arn't exactly the same
769 result = math.fabs(self.volume[1] - self.__previous_left) > self.client.volume_step / 2
770 result = result and (math.fabs(self.volume[2] - self.__previous_right) > self.client.volume_step /2)
771-
772+
773 # We never want a zero volume as we cant read meter levels from zero volume
774 result = result and not self.volume[1] == self.client.volume_step
775- result = result and not self.volume[2] == self.client.volume_step
776+ result = result and not self.volume[2] == self.client.volume_step
777 if result:
778 print
779 print "Adjust Volume", self.client.name, self.volume[1] , self.__previous_left
780
781 self.__previous_left = self.volume[1]
782 self.__previous_right = self.volume[2]
783-
784+
785 return result
786
787
788=== renamed file 'Threads.py' => 'earcandylib/Threads.py'
789=== renamed file 'TrayIcon.py' => 'earcandylib/TrayIcon.py'
790--- TrayIcon.py 2009-05-19 04:28:48 +0000
791+++ earcandylib/TrayIcon.py 2009-05-22 15:58:21 +0000
792@@ -1,15 +1,17 @@
793 #!/usr/bin/env python
794
795-import os
796 import sys
797 import gtk
798
799+import EarCandyInfo
800+from Paths import get_data_path
801+
802
803 class EarCandyStatusIcon(gtk.StatusIcon):
804 def __init__(self, core):
805 gtk.StatusIcon.__init__(self)
806 self.core = core
807-
808+
809 menu = '''
810 <ui>
811 <menubar name="Menubar">
812@@ -44,7 +46,7 @@
813 #
814 #self.core.slider.window.present()
815 self.core.slider.window.set_position(gtk.WIN_POS_MOUSE)
816- screen, rect, orin = self.get_geometry()
817+ screen, rect, orin = self.get_geometry()
818 x, y = self.core.slider.window.get_position()
819 if rect.y - rect.height/2 > y - rect.height:
820 self.core.slider.window.move(x, y + rect.height)
821@@ -58,24 +60,24 @@
822
823 def on_about(self, data):
824 dialog = gtk.AboutDialog()
825- dialog.set_name('Ear Candy')
826- dialog.set_version('0.4')
827- dialog.set_comments('A pulse audio ear candy manager')
828- dialog.set_website('http://edge.launchpad.net/eyecandy')
829+ dialog.set_name(EarCandyInfo.NAME)
830+ dialog.set_version(EarCandyInfo.VERSION)
831+ dialog.set_comments(EarCandyInfo.DESC)
832+ dialog.set_website(EarCandyInfo.WEBSITE)
833 dialog.run()
834 dialog.destroy()
835-
836+
837
838 def set_icon(self):
839 if self.core.active:
840- icon_path = os.path.join(os.path.dirname(__file__),"earsLabel.png")
841+ icon_path = get_data_path("earsLabel.png")
842 else:
843- icon_path = os.path.join(os.path.dirname(__file__),"earsLabelOff.png")
844+ icon_path = get_data_path("earsLabelOff.png")
845 self.set_from_file( icon_path )
846
847 def stop(self, data=None):
848 gtk.main_quit()
849-
850+
851 if __name__ == '__main__':
852 ts = TrackerStatusIcon()
853 ts.run()
854
855=== renamed file 'VolumeSlider.py' => 'earcandylib/VolumeSlider.py'
856--- VolumeSlider.py 2009-05-19 04:28:48 +0000
857+++ earcandylib/VolumeSlider.py 2009-05-22 15:58:21 +0000
858@@ -16,13 +16,14 @@
859 from glade_window import GladeWindow
860 from window.WindowWatcher import WindowWatcher
861 from Client import Client
862+from Paths import get_data_path
863
864 class EarCandyVolumeSlider(GladeWindow):
865 def __init__(self, core):
866 self.core = core
867-
868- GladeWindow.__init__(self, "pulseoptions.glade", "popup_volume_control")
869-
870+
871+ GladeWindow.__init__(self, get_data_path("pulseoptions.glade"), "popup_volume_control")
872+
873 self.vscale_volume = self.wtree.get_widget("vscale_volume")
874 self.image_status = self.wtree.get_widget("image_status")
875
876@@ -48,7 +49,7 @@
877 self.image_status.set_from_stock("gtk-media-pause", 4)
878 else:
879 self.image_status.set_from_stock("gtk-media-play", 4)
880-
881+
882 self.core.tray.set_icon()
883 self.stop()
884
885
886=== added file 'earcandylib/__init__.py'
887=== renamed file 'glade_window.py' => 'earcandylib/glade_window.py'
888--- glade_window.py 2008-10-09 20:37:12 +0000
889+++ earcandylib/glade_window.py 2009-05-22 15:58:21 +0000
890@@ -18,9 +18,9 @@
891 self.wtree = gtk.glade.XML(glade_file)
892 self.window = self.wtree.get_widget(window_name)
893 self.window.connect("destroy", self.on_destroy)
894-
895+
896 def run(self):
897- self.window.show()
898+ self.window.show()
899 gtk.main()
900 return self.return_value
901
902
903=== renamed directory 'pulseaudio' => 'earcandylib/pulseaudio'
904=== renamed directory 'window' => 'earcandylib/window'
905=== modified file 'earcandylib/window/WindowWatcher.py'
906--- window/WindowWatcher.py 2009-05-11 22:27:41 +0000
907+++ earcandylib/window/WindowWatcher.py 2009-05-22 14:14:10 +0000
908@@ -6,7 +6,7 @@
909 import wnck
910 import gobject
911 import os
912-from DesktopFiles import DesktopFiles
913+from earcandylib.DesktopFiles import DesktopFiles
914
915 class Application():
916 def __init__(self, command, name, desktop_files, icon):
917
918=== added file 'setup.py'
919--- setup.py 1970-01-01 00:00:00 +0000
920+++ setup.py 2009-05-23 12:26:12 +0000
921@@ -0,0 +1,21 @@
922+#!/usr/bin/env python
923+
924+from distutils.core import setup
925+from glob import glob
926+
927+from earcandylib.EarCandyInfo import *
928+
929+DATA_FILES = [('share/earcandy', glob('data/*'))]
930+DATA_FILES += [('share/applications', ['earcandy.desktop'])]
931+DATA_FILES += [('share/pixmaps', ['data/earsLabel.png'])]
932+
933+setup(name = APPNAME,
934+ version = VERSION,
935+ description = DESC,
936+ author = AUTHOR,
937+ author_email = AUTHOR_EMAIL,
938+ url = WEBSITE,
939+ license = LICENSE,
940+ packages = ['earcandylib', 'earcandylib.pulseaudio', 'earcandylib.window'],
941+ data_files = DATA_FILES,
942+ scripts = ['earcandy'])

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: