root/trunk/libffado/support/mixer-qt4/ffado/panelmanager.py

Revision 1640, 13.8 kB (checked in by arnonym, 15 years ago)

Make it more pythonic.

All ffado stuff is now in packages getting installed to the official python dirs. Ideally this would allow other apps to use the stuff from us.

Maybe the generic widgets (that are used by multiple mixers) should go into ffado/widgets...

  • Property svn:mergeinfo set to
Line 
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2005-2008 by Pieter Palmers
4 #               2007-2008 by Arnold Krille
5 #
6 # This file is part of FFADO
7 # FFADO = Free Firewire (pro-)audio drivers for linux
8 #
9 # FFADO is based upon FreeBoB.
10 #
11 # This program is free software: you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation, either version 3 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 from ffado.config import * #FFADO_VERSION, FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH
26
27 from PyQt4.QtGui import QFrame, QWidget, QTabWidget, QVBoxLayout, QMainWindow, QIcon, QAction, qApp, QStyleOptionTabWidgetFrame
28 from PyQt4.QtCore import QTimer
29
30 from ffado.dbus_util import *
31 from ffado.registration import *
32
33 from ffado.configuration import *
34
35 from ffado.mixer.phase88 import *
36 from ffado.mixer.phase24 import *
37 from ffado.mixer.saffire import SaffireMixer
38 from ffado.mixer.saffirepro import SaffireProMixer
39 from ffado.mixer.saffire_dice import Saffire_Dice
40 from ffado.mixer.audiofire import AudioFireMixer
41 from ffado.mixer.bcoaudio5 import *
42 from ffado.mixer.edirolfa66 import *
43 from ffado.mixer.edirolfa101 import *
44 from ffado.mixer.mackie_onyxmixer import *
45 from ffado.mixer.quatafire import *
46 from ffado.mixer.motu import *
47 from ffado.mixer.rme import *
48 from ffado.mixer.dummy import *
49 from ffado.mixer.globalmixer import GlobalMixer
50
51 import time
52
53 import logging
54 log = logging.getLogger('panelmanager')
55
56 use_generic = False
57 try:
58     from mixer_generic import *
59     log.info("The generic mixer is found, seems to be a developer using ffadomixer...")
60 except ImportError:
61     pass
62 else:
63     use_generic = True
64
65 # pseudo-guid
66 GUID_GENERIC_MIXER = 0
67
68
69 class HLine( QFrame ):
70     def __init__( self, parent ):
71         QFrame.__init__( self, parent )
72         self.setFrameShape( QFrame.HLine )
73         self.setLineWidth( 2 )
74         self.setMinimumHeight( 10 )
75
76 class PanelManagerStatus(QWidget):
77     def __init__(self, parent):
78         QWidget.__init__(self,parent)
79         uicLoad("ffado/panelmanagerstatus", self)
80
81 class OwnTabWidget(QTabWidget):
82     def __init__(self,parent):
83         QTabWidget.__init__(self,parent)
84
85     def tabInserted(self,index):
86         self.checkTabBar()
87
88     def tabRemoved(self,index):
89         self.checkTabBar()
90
91     def checkTabBar(self):
92         if self.count()<2:
93             self.tabBar().hide()
94         else:
95             self.tabBar().show()
96
97 class PanelManager(QWidget):
98     def __init__(self, parent, devmgr=None):
99         QWidget.__init__(self, parent)
100         self.setObjectName("PanelManager")
101
102         # maps a device GUID to a QT panel
103         self.panels = {}
104
105         # a layout for ourselves
106         self.layout = QVBoxLayout(self)
107
108         # the tabs
109         self.tabs = OwnTabWidget(self)
110         self.tabs.hide()
111         self.layout.addWidget(self.tabs)
112
113         # a dialog that is shown during update
114         self.status = PanelManagerStatus(self)
115         self.layout.addWidget(self.status)
116         self.status.show()
117
118         self.devices = DeviceList( SYSTEM_CONFIG_FILE )
119         self.devices.updateFromFile( USER_CONFIG_FILE )
120
121         if devmgr is not None:
122             self.setManager(devmgr)
123
124     def setManager(self,devmgr):
125         self.devmgr = devmgr
126         self.devmgr.registerPreUpdateCallback(self.devlistPreUpdate)
127         self.devmgr.registerPostUpdateCallback(self.devlistPostUpdate)
128         self.devmgr.registerUpdateCallback(self.devlistUpdate)
129         self.devmgr.registerDestroyedCallback(self.devmgrDestroyed)
130         # create a timer to poll the panels
131         self.polltimer = QTimer()
132         self.connect( self.polltimer, SIGNAL('timeout()'), self.pollPanels )
133         self.polltimer.start( POLL_SLEEP_TIME_MSEC )
134
135         # create a timer to initialize the panel after the main form is shown
136         # since initialization can take a while
137         QTimer.singleShot( POLL_SLEEP_TIME_MSEC, self.updatePanels )
138
139         # live check timer
140         self.alivetimer = QTimer()
141         QObject.connect( self.alivetimer, SIGNAL('timeout()'), self.commCheck )
142         self.alivetimer.start( 2000 )
143
144     def count(self):
145         return self.tabs.count()
146
147     def pollPanels(self):
148         #log.debug("PanelManager::pollPanels()")
149         # only when not modifying the tabs
150         if self.tabs.isEnabled():
151             for guid in self.panels.keys():
152                 w = self.panels[guid]
153                 for child in w.children():
154                     #log.debug("poll child %s,%s" % (guid,child))
155                     if 'polledUpdate' in dir(child):
156                         try:
157                             child.polledUpdate()
158                         except:
159                             log.error("error in polled update")
160                             raise
161
162     def devlistPreUpdate(self):
163         log.debug("devlistPreUpdate")
164         self.tabs.setEnabled(False)
165         self.tabs.hide()
166         self.status.lblMessage.setText("Bus reconfiguration in progress, please wait...")
167         self.status.show()
168         #self.statusBar().showMessage("bus reconfiguration in progress...", 5000)
169
170     def devlistPostUpdate(self):
171         log.debug("devlistPostUpdate")
172         # this can fail if multiple busresets happen in fast succession
173         ntries = 10
174         while ntries > 0:
175             try:
176                 self.updatePanels()
177                 return
178             except:
179                 log.debug("devlistPostUpdate failed (%d)" % ntries)
180                 for guid in self.panels.keys():
181                     w = self.panels[guid]
182                     del self.panels[guid] # remove from the list
183                     idx = self.tabs.indexOf(w)
184                     self.tabs.removeTab(idx)
185                     del w # GC might also take care of that
186
187                 ntries = ntries - 1
188                 time.sleep(2) # sleep a few seconds
189
190         log.debug("devlistPostUpdate failed completely")
191         self.tabs.setEnabled(False)
192         self.tabs.hide()
193         self.status.lblMessage.setText("Error while reconfiguring. Please restart ffadomixer.")
194         self.status.show()
195
196
197     def devlistUpdate(self):
198         log.debug("devlistUpdate")
199
200     def devmgrDestroyed(self):
201         log.debug("devmgrDestroyed")
202         self.alivetimer.stop()
203         self.tabs.setEnabled(False)
204         self.tabs.hide()
205         self.status.lblMessage.setText("DBUS server was shut down, please restart it and restart ffadomixer...")
206         self.status.show()
207
208     def commCheck(self):
209         try:
210             nbDevices = self.devmgr.getNbDevices()
211         except:
212             log.debug("comms lost")
213             self.tabs.setEnabled(False)
214             self.tabs.hide()
215             self.status.lblMessage.setText("Failed to communicate with DBUS server. Please restart it and restart ffadomixer...")
216             self.status.show()
217             self.alivetimer.stop()
218
219     def updatePanels(self):
220         log.debug("PanelManager::updatePanels()")
221         nbDevices = self.devmgr.getNbDevices()
222         #self.statusBar().showMessage("Reconfiguring the mixer panels...")
223
224         # list of panels present
225         guids_with_tabs = self.panels.keys()
226
227         # build list of guids on the bus now
228         guids_present = []
229         guid_indexes = {}
230         for idx in range(nbDevices):
231             path = self.devmgr.getDeviceName(idx)
232             cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path)
233             guid = cfgrom.getGUID()
234             guids_present.append(guid)
235             guid_indexes[guid] = idx
236
237         # figure out what to remove
238         # the special panel (generic)
239         # that has (pseudo-)GUID 0
240         # is also automatically removed
241         to_remove = []
242         for guid in guids_with_tabs:
243             if not guid in guids_present:
244                 to_remove.append(guid)
245                 log.debug("going to remove %s" % str(guid))
246             else:
247                 log.debug("going to keep %s" % str(guid))
248
249         # figure out what to add
250         to_add = []
251         for guid in guids_present:
252             if not guid in guids_with_tabs:
253                 to_add.append(guid)
254                 log.debug("going to add %s" % str(guid))
255
256         # update the widget
257         for guid in to_remove:
258             w = self.panels[guid]
259             del self.panels[guid] # remove from the list
260             idx = self.tabs.indexOf(w)
261             self.tabs.removeTab(idx)
262             del w # GC might also take care of that
263
264         for guid in to_add:
265             # retrieve the device manager index
266             idx = guid_indexes[guid]
267             path = self.devmgr.getDeviceName(idx)
268             log.debug("Adding device %d: %s" % (idx, path))
269
270             cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path)
271             vendorId = cfgrom.getVendorId()
272             modelId = cfgrom.getModelId()
273             unitVersion = cfgrom.getUnitVersion()
274             guid = cfgrom.getGUID()
275             vendorName = cfgrom.getVendorName()
276             modelName = cfgrom.getModelName()
277             log.debug(" Found (%s, %X, %X) %s %s" % (str(guid), vendorId, modelId, vendorName, modelName))
278
279             # check whether this has already been registered at ffado.org
280             reg = ffado_registration(FFADO_VERSION, int(guid, 16),
281                                      vendorId, modelId,
282                                      vendorName, modelName)
283             reg.check_for_registration()
284
285             # The MOTU devices use unitVersion to differentiate models.  For the
286             # moment though we don't need to know precisely which model we're
287             # using.
288             if vendorId == 0x1f2:
289                 modelId = 0x00000000
290
291             # The RME devices use unitVersion to differentiate models.
292             # Therefore in the configuration file we use the config file's
293             # modelid field to store the unit version.  As a result we must
294             # override the modelId with the unit version here so the correct
295             # configuration file entry (and hense mixer widget) is identified.
296             if vendorId == 0xa35:
297                 modelId = unitVersion;
298
299             dev = self.devices.getDeviceById( vendorId, modelId )
300
301             w = QWidget( )
302             l = QVBoxLayout( w )
303
304             # create a control object
305             hw = ControlInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path)
306             clockselect = ClockSelectInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path )
307             samplerateselect = SamplerateSelectInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path )
308             streamingstatus = StreamingStatusInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path )
309             nickname = TextInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path+"/Generic/Nickname" )
310
311             #
312             # Generic elements for all mixers follow here:
313             #
314             globalmixer = GlobalMixer( w )
315             globalmixer.configrom = cfgrom
316             globalmixer.clockselect = clockselect
317             globalmixer.samplerateselect = samplerateselect
318             globalmixer.streamingstatus = streamingstatus
319             globalmixer.nickname = nickname
320             globalmixer.hw = hw
321             globalmixer.initValues()
322             l.addWidget( globalmixer, 1 )
323
324             #
325             # Line to separate
326             #
327             l.addWidget( HLine( w ) )
328
329             #
330             # Specific (or dummy) mixer widgets get loaded in the following
331             #
332             if 'mixer' in dev and dev['mixer'] != None:
333                 mixerapp = dev['mixer']
334                 exec( "mixerwidget = "+mixerapp+"( w )" )
335             else:
336                 mixerwidget = DummyMixer( w )
337                 mixerapp = modelName+" (Dummy)"
338
339             #
340             # The same for all mixers
341             #
342             l.addWidget( mixerwidget, 10 )
343             mixerwidget.configrom = cfgrom
344             mixerwidget.clockselect = clockselect
345             mixerwidget.samplerateselect = samplerateselect
346             mixerwidget.streamingstatus = streamingstatus
347             mixerwidget.nickname = nickname
348             mixerwidget.hw = hw
349             if 'buildMixer' in dir(mixerwidget):
350                 mixerwidget.buildMixer()
351             if 'initValues' in dir(mixerwidget):
352                 mixerwidget.initValues()
353             if 'getDisplayTitle' in dir(mixerwidget):
354                 title = mixerwidget.getDisplayTitle()
355             else:
356                 title = mixerapp
357
358             globalmixer.setName(title)
359             self.tabs.addTab( w, title )
360             self.panels[guid] = w
361
362         # if there is no panel, add the no-device message
363         # else make sure it is not present
364         if self.count() == 0:
365             self.tabs.hide()
366             self.tabs.setEnabled(False)
367             self.status.lblMessage.setText("No supported device found.")
368             self.status.show()
369             #self.statusBar().showMessage("No supported device found.", 5000)
370         else:
371             self.tabs.show()
372             self.tabs.setEnabled(True)
373             self.status.hide()
374             #self.statusBar().showMessage("Configured the mixer for %i devices." % self.tabs.count())
375             if use_generic:
376                 #
377                 # Show the generic (development) mixer if it is available
378                 #
379                 w = GenericMixer( devmgr.bus, FFADO_DBUS_SERVER, mw )
380                 self.tabs.addTab( w, "Generic Mixer" )
381                 self.panels[GUID_GENERIC_MIXER] = w
382
383 # vim: et
Note: See TracBrowser for help on using the browser.