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