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

Revision 1707, 13.5 kB (checked in by arnonym, 14 years ago)

Use QSettings to store the state of the VU-switch.

And do some playing with deleting panels when the devices are removed from the bus. Otherwise the timer checking the VU-values will run forever and complain about missing dbus-objects.

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