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

Revision 2109, 13.4 kB (checked in by jwoithe, 12 years ago)

Patch from Philippe Carriere. This is just a simple re-writing of updatePanels, intended to have no effects on the behaviour of ffado-mixer. Sub-functions will be used in a future, additional, 'refresh currents' panels function.

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