root/trunk/libffado/support/mixer-qt4/ffado/widgets/matrixmixer.py

Revision 2411, 45.7 kB (checked in by philippe, 9 years ago)

MatrixMixer?: introduce refreshValues at the level of MatrixControlView? and SliderControlView? so as they become as autonomous as possible. In MatrixMixer?, only the call to the one or the other one is required (here refresh from matrix control).

  • Property svn:mergeinfo set to
Line 
1 # coding=utf8
2 #
3 # Copyright (C) 2009 by Arnold Krille
4 # Copyright (C) 2013 by Philippe Carriere
5 #
6 # This file is part of FFADO
7 # FFADO = Free Firewire (pro-)audio drivers for linux
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 2 of the License, or
12 # (at your option) version 3 of the License.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 from PyQt4 import QtGui, QtCore, Qt
24 import dbus, math, decimal
25
26 import logging
27 log = logging.getLogger("matrixmixer")
28
29 def toDBvalue(value):
30     n = int(value)
31     c2p14 = 16384.0
32     if n > 164:
33         return round(20.0*math.log10(float(n)/c2p14), 2)
34     else:
35         return -40.0
36
37 def fromDBvalue(value):
38     v = float(value)
39     c2p14 = 16384.0
40     if (v > -40):
41         return int(round(math.pow(10.0, (value/20.0))*c2p14, 0))
42     else:
43         return 0
44
45 # v, vl, vr in linear scale
46 # b range in [-1:1]
47 def getVolumeLeft(v, b):
48     return int(round(0.5*v*(1.0-b),0))
49 def getVolumeRight(v, b):
50     return v-int(round(0.5*v*(1.0-b),0))
51 def getStereoVolume(vl, vr):
52     return int(round(vl+vr,0))
53 def getStereoBalance(vl, vr):
54     if ((vl+vr) == 0):
55         return 0
56     else:
57         return round(float(vr-vl)/float(vr+vl),2)
58
59 class ColorForNumber:
60     def __init__(self):
61         self.colors = dict()
62
63     def addColor(self, n, color):
64         self.colors[n] = color
65
66     def getColor(self, n):
67         #print "ColorForNumber.getColor( %g )" % (n)
68         keys = self.colors.keys()
69         keys.sort()
70         low = keys[-1]
71         high = keys[-1]
72         for i in range(len(keys)-1):
73             if keys[i] <= n and keys[i+1] > n:
74                 low = keys[i]
75                 high = keys[i+1]
76         #print "%g is between %g and %g" % (n, low, high)
77         f = 0
78         if high != low:
79             f = (n-low) / (high-low)
80         lc = self.colors[low]
81         hc = self.colors[high]
82         return QtGui.QColor(
83                 (1-f)*lc.red()   + f*hc.red(),
84                 (1-f)*lc.green() + f*hc.green(),
85                 (1-f)*lc.blue()  + f*hc.blue() )
86
87 class BckgrdColorForNumber(ColorForNumber):
88     def __init__(self):
89         ColorForNumber.__init__(self)
90         self.addColor(             0.0, QtGui.QColor(  0,   0,   0))
91         self.addColor(             1.0, QtGui.QColor(  0,   0, 128))
92         self.addColor(   math.pow(2,6), QtGui.QColor(  0, 255,   0))
93         self.addColor(  math.pow(2,14), QtGui.QColor(255, 255,   0))
94         self.addColor(math.pow(2,16)-1, QtGui.QColor(255,   0,   0))
95
96     def getFrgdColor(self, color):
97         if color.valueF() < 0.6:
98             return QtGui.QColor(255, 255, 255)
99         else:
100             return QtGui.QColor(0, 0, 0)
101    
102 class MixerNode(QtGui.QAbstractSlider):
103     def __init__(self, input, output, value, max, muted, inverted, parent, matrix_obj):
104         QtGui.QAbstractSlider.__init__(self, parent)
105         #log.debug("MixerNode.__init__( %i, %i, %i, %i, %s )" % (input, output, value, max, str(parent)) )
106
107         # Store a direct link back to the underlying matrix object so the mute
108         # and invert interfaces can be easily found.  By the time the matrix
109         # has been set into the full widget hierarchy, its parent is unlikely
110         # to still be the top-level matrix object.
111         self.matrix_obj = matrix_obj;
112
113         self.pos = QtCore.QPointF(0, 0)
114         self.input = input
115         self.output = output
116         self.setOrientation(Qt.Qt.Vertical)
117         if max == -1:
118             max = pow(2, 16)-1
119         self.setRange(0, max)
120         self.setValue(value)
121         self.connect(self, QtCore.SIGNAL("valueChanged(int)"), self.internalValueChanged)
122
123         self.setSmall(False)
124
125         self.bgcolors = BckgrdColorForNumber()
126
127         self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu)
128         self.mapper = QtCore.QSignalMapper(self)
129         self.connect(self.mapper, QtCore.SIGNAL("mapped(const QString&)"), self.directValues)
130
131         self.spinbox = QtGui.QDoubleSpinBox(self)
132         self.spinbox.setRange(-40, 12)
133         self.spinbox.setSuffix(" dB")
134         if value != 0:
135             self.spinbox.setValue(toDBvalue(value))           
136
137         self.connect(self.spinbox, QtCore.SIGNAL("valueChanged(const QString&)"), self.directValues)
138         action = QtGui.QWidgetAction(self)
139         action.setDefaultWidget(self.spinbox)
140         self.addAction(action)
141
142         for text in ["3 dB", "0 dB", "-3 dB", "-20 dB", "-inf dB"]:
143             action = QtGui.QAction(text, self)
144             self.connect(action, QtCore.SIGNAL("triggered()"), self.mapper, QtCore.SLOT("map()"))
145             self.mapper.setMapping(action, text)
146             self.addAction(action)
147
148         # Only show the mute menu item if a value has been supplied
149         self.mute_action = None
150         if (muted != None):
151             action = QtGui.QAction(text, self)
152             action.setSeparator(True)
153             self.addAction(action)
154             self.mute_action = QtGui.QAction("Mute", self)
155             self.mute_action.setCheckable(True)
156             self.mute_action.setChecked(muted)
157             self.connect(self.mute_action, QtCore.SIGNAL("triggered()"), self.mapper, QtCore.SLOT("map()"))
158             self.mapper.setMapping(self.mute_action, "Mute")
159             self.addAction(self.mute_action)
160
161         # Similarly, only show a phase inversion menu item if in use
162         self.inv_action = None
163         if (inverted != None):
164             if (muted == None):
165                 action = QtGui.QAction(text, self)
166                 action.setSeparator(True)
167                 self.addAction(action)
168             self.inv_action = QtGui.QAction("Invert", self)
169             self.inv_action.setCheckable(True)
170             self.inv_action.setChecked(inverted)
171             self.connect(self.inv_action, QtCore.SIGNAL("triggered()"), self.mapper, QtCore.SLOT("map()"))
172             self.mapper.setMapping(self.inv_action, "Invert")
173             self.addAction(self.inv_action)
174
175     def directValues(self,text):
176         #log.debug("MixerNode.directValues( '%s' )" % text)
177         if text == "Mute":
178             #log.debug("Mute %d" % self.mute_action.isChecked())
179             self.update()
180             self.matrix_obj.mutes_interface.setValue(self.output, self.input, self.mute_action.isChecked())
181         elif text == "Invert":
182             #log.debug("Invert %d" % self.inv_action.isChecked())
183             self.update()
184             self.matrix_obj.inverts_interface.setValue(self.output, self.input, self.inv_action.isChecked())
185         else:
186             text = text.split(" ")[0].replace(",",".")
187             n = fromDBvalue(float(text))
188             #log.debug("  linear value: %g" % n)
189             self.setValue(n)
190
191     def mousePressEvent(self, ev):
192         if ev.buttons() & Qt.Qt.LeftButton:
193             self.pos = ev.posF()
194             self.tmpvalue = self.value()
195             ev.accept()
196             #log.debug("MixerNode.mousePressEvent() %s" % str(self.pos))
197
198     def mouseMoveEvent(self, ev):
199         if hasattr(self, "tmpvalue") and self.pos is not QtCore.QPointF(0, 0):
200             newpos = ev.posF()
201             change = newpos.y() - self.pos.y()
202             #log.debug("MixerNode.mouseReleaseEvent() change %s" % (str(change)))
203             self.setValue( self.tmpvalue - math.copysign(pow(abs(change), 2), change) )
204             ev.accept()
205
206     def mouseReleaseEvent(self, ev):
207         if hasattr(self, "tmpvalue") and self.pos is not QtCore.QPointF(0, 0):
208             newpos = ev.posF()
209             change = newpos.y() - self.pos.y()
210             #log.debug("MixerNode.mouseReleaseEvent() change %s" % (str(change)))
211             self.setValue( self.tmpvalue - math.copysign(pow(abs(change), 2), change) )
212             self.pos = QtCore.QPointF(0, 0)
213             del self.tmpvalue
214             ev.accept()
215
216     def paintEvent(self, ev):
217         p = QtGui.QPainter(self)
218         rect = self.rect()
219         v = self.value()
220         if (self.mute_action!=None and self.mute_action.isChecked()):
221             color = QtGui.QColor(64, 64, 64)
222         else:
223             color = self.bgcolors.getColor(v)
224         p.fillRect(rect, color)
225
226         if self.small:
227             return
228
229         p.setPen(self.bgcolors.getFrgdColor(color))
230
231         lv=decimal.Decimal('-Infinity')
232         if v != 0:
233             lv = toDBvalue(v)
234             #log.debug("new value is %g dB" % lv)
235         text = "%.2g dB" % lv
236         if v == 0:
237             symb_inf = u"\u221E"
238             text = "-" + symb_inf + " dB"
239         p.drawText(rect, Qt.Qt.AlignCenter, QtCore.QString.fromUtf8(text))
240         if (self.inv_action!=None and self.inv_action.isChecked()):
241             p.drawText(rect, Qt.Qt.AlignLeft|Qt.Qt.AlignTop, QtCore.QString.fromUtf8(" ϕ"))
242
243     def internalValueChanged(self, value):
244         #log.debug("MixerNode.internalValueChanged( %i )" % value)
245         if value != 0:
246             dB = toDBvalue(value)
247             if self.spinbox.value() is not dB:
248                 self.spinbox.setValue(dB)
249         self.emit(QtCore.SIGNAL("valueChanged"), (self.input, self.output, value) )
250         self.update()
251
252     def setSmall(self, small):
253         self.small = small
254         if small:
255             self.setMinimumSize(10, 10)
256         else:
257             fontmetrics = self.fontMetrics()
258             self.setMinimumSize(fontmetrics.boundingRect("-0.0 dB").size()*1.1)
259         self.update()
260
261 class MixerChannel(QtGui.QWidget):
262     def __init__(self, number, parent=None, name="", smallFont=False):
263         QtGui.QWidget.__init__(self, parent)
264         layout = QtGui.QGridLayout(self)
265         self.number = number
266         self.name = name
267         self.lbl = QtGui.QLabel(self)
268         self.lbl.setAlignment(Qt.Qt.AlignCenter)
269         if (smallFont):
270             font = self.lbl.font()
271             font.setPointSize(font.pointSize()/1.5)
272             self.lbl.setFont(font)
273         layout.addWidget(self.lbl, 0, 0, 1, 2)
274         self.hideChannel(False)
275
276         self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu)
277
278         action = QtGui.QAction("Make this channel small", self)
279         action.setCheckable(True)
280         self.connect(action, QtCore.SIGNAL("triggered(bool)"), self.hideChannel)
281         self.addAction(action)
282
283     def hideChannel(self, hide):
284         if hide:
285             self.lbl.setText("%i" % (self.number+1));
286         else:
287             self.lbl.setText(self.name)
288         self.emit(QtCore.SIGNAL("hide"), self.number, hide)
289         self.update()
290
291 # Matrix view widget
292 class MatrixControlView(QtGui.QWidget):
293     def __init__(self, servername, basepath, parent=None, sliderMaxValue=-1, mutespath=None, invertspath=None, smallFont=False, shortname=False, shortcolname="Ch", shortrowname="Ch", transpose=False):
294         QtGui.QWidget.__init__(self, parent)
295
296         self.bus = dbus.SessionBus()
297         self.dev = self.bus.get_object(servername, basepath)
298         self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.MatrixMixer")
299
300         self.transpose = transpose
301         if (transpose):
302             self.shortcolname = shortrowname
303             self.shortrowname = shortcolname
304             self.cols = self.interface.getRowCount()
305             self.rows = self.interface.getColCount()
306         else:
307             self.shortcolname = shortcolname
308             self.shortrowname = shortrowname
309             self.cols = self.interface.getColCount()
310             self.rows = self.interface.getRowCount()
311
312         log.debug("Mixer has %i rows and %i columns" % (self.rows, self.cols))
313
314         self.mutes_dev = None
315         self.mutes_interface = None
316         if (mutespath != None):
317             self.mutes_dev = self.bus.get_object(servername, mutespath)
318             self.mutes_interface = dbus.Interface(self.mutes_dev, dbus_interface="org.ffado.Control.Element.MatrixMixer")
319
320         self.inverts_dev = None
321         self.inverts_interface = None
322         if (invertspath != None):
323             self.inverts_dev = self.bus.get_object(servername, invertspath)
324             self.inverts_interface = dbus.Interface(self.inverts_dev, dbus_interface="org.ffado.Control.Element.MatrixMixer")
325
326         layout = QtGui.QGridLayout(self)
327         layout.setSizeConstraint(QtGui.QLayout.SetNoConstraint);
328         self.setLayout(layout)
329
330         self.rowHeaders = []
331         self.columnHeaders = []
332         self.items = []
333         self.shortname = shortname
334
335         # Add row/column headers, but only if there's more than one
336         # row/column
337         if (self.cols > 1):
338             for i in range(self.cols):
339                 ch = MixerChannel(i, self, self.getColName(i, self.shortname), smallFont)
340                 self.connect(ch, QtCore.SIGNAL("hide"), self.hideColumn)
341                 layout.addWidget(ch, 0, i+1)
342                 self.columnHeaders.append( ch )
343             layout.setRowStretch(0, 0)
344             layout.setRowStretch(1, 10)
345         if (self.rows > 1):
346             for i in range(self.rows):
347                 ch = MixerChannel(i, self, self.getRowName(i, self.shortname), smallFont)
348                 self.connect(ch, QtCore.SIGNAL("hide"), self.hideRow)
349                 layout.addWidget(ch, i+1, 0)
350                 self.rowHeaders.append( ch )
351
352         # Add node-widgets
353         for i in range(self.rows):
354             self.items.append([])
355             for j in range(self.cols):
356                 if (transpose):
357                     mute_value = None
358                     if (self.mutes_interface != None):
359                         mute_value = self.mutes_interface.getValue(j,i)
360                     inv_value = None
361                     if (self.inverts_interface != None):
362                         inv_value = self.inverts_interface.getValue(j,i)
363                     node = MixerNode(i, j, self.interface.getValue(j,i), sliderMaxValue, mute_value, inv_value, self, self)
364                 else:
365                     mute_value = None
366                     if (self.mutes_interface != None):
367                         mute_value = self.mutes_interface.getValue(i,j)
368                     inv_value = None
369                     if (self.inverts_interface != None):
370                         inv_value = self.inverts_interface.getValue(i,j)
371                     node = MixerNode(j, i, self.interface.getValue(i,j), sliderMaxValue, mute_value, inv_value, self, self)
372                 if (smallFont):
373                     font = node.font()
374                     font.setPointSize(font.pointSize()/1.5)
375                     node.setFont(font)
376                 self.nodeConnect(node)
377                 layout.addWidget(node, i+1, j+1)
378                 self.items[i].append(node)
379
380         self.hiddenRows = []
381         self.hiddenCols = []
382
383     def nodeConnect(self, node):
384         self.connect(node, QtCore.SIGNAL("valueChanged"), self.valueChanged)
385
386     def nodeDisconnect(self, node):
387         self.disconnect(node, QtCore.SIGNAL("valueChanged"), self.valueChanged)
388
389     def checkVisibilities(self):
390         for x in range(len(self.items)):
391             for y in range(len(self.items[x])):
392                 self.items[x][y].setSmall(
393                         (x in self.hiddenRows)
394                         | (y in self.hiddenCols)
395                         )
396
397     def hideColumn(self, column, hide):
398         if hide:
399             self.hiddenCols.append(column)
400         else:
401             self.hiddenCols.remove(column)
402         self.checkVisibilities()
403
404     def hideRow(self, row, hide):
405         if hide:
406             self.hiddenRows.append(row)
407         else:
408             self.hiddenRows.remove(row)
409         self.checkVisibilities()
410
411     # Columns and rows
412     def getColName(self, i, shortname):
413         if (self.transpose):
414             name = self.interface.getRowName(i)
415         else:
416             name = self.interface.getColName(i)
417         self.shortname = shortname
418         if (shortname or (name == '')):
419             number = " %d" % (i+1)
420             name = self.shortcolname + number
421         return name
422
423     def getRowName(self, j, shortname):
424         if (self.transpose):
425             name = self.interface.getColName(j)
426         else:
427             name = self.interface.getRowName(j)
428         self.shortname = shortname
429         if (shortname or (name == '')):
430             number = " %d" % (j+1)
431             name = self.shortrowname + number
432         return name
433
434     def valueChanged(self, n):
435         #log.debug("MatrixNode.valueChanged( %s )" % str(n))
436         self.interface.setValue(n[1], n[0], n[2])
437         self.emit(QtCore.SIGNAL("valueChanged"), n)
438        
439     # Update when routing is modified
440     def updateRouting(self):
441         if (self.cols > 1):
442             for i in range(self.cols):
443                 last_name = self.columnHeaders[i].lbl.text()
444                 col_name = self.getColName(i, self.shortname)
445                 if last_name != col_name:
446                     #log.debug("MatrixControlView.updateRouting( %s )" % str(col_name))
447                     self.columnHeaders[i].name = col_name
448                     self.columnHeaders[i].lbl.setText(col_name)
449      
450         if (self.rows > 1):
451             for j in range(self.rows):
452                 last_name = self.rowHeaders[j].lbl.text()
453                 row_name = self.getRowName(j, self.shortname)
454                 if last_name != row_name:
455                     #log.debug("MatrixControlView.updateRouting( %s )" % str(row_name))
456                     self.rowHeaders[j].name = row_name
457                     self.rowHeaders[j].lbl.setText(row_name)
458
459     def updateValues(self, n):
460         nbitems = len(n)/3
461         for i in range(nbitems):
462             n_0 = n[3*i]   
463             n_1 = n[3*i+1]   
464             n_2 = n[3*i+2]
465             self.nodeDisconnect(self.items[n_0][n_1])
466             self.items[n_0][n_1].setValue(n_2)
467             self.nodeConnect(self.items[n_0][n_1])
468
469     def refreshValues(self):
470         for x in range(len(self.items)):
471             for y in range(len(self.items[x])):
472                 val = self.interface.getValue(x,y)
473                 if (self.transpose):
474                     self.items[y][x].setValue(val)
475                     self.items[y][x].internalValueChanged(val)
476                 else:
477                     self.items[x][y].setValue(val)
478                     self.items[x][y].internalValueChanged(val)
479
480 class VolumeSlider(QtGui.QSlider):
481     def __init__(self, In, Out, value, parent):
482         QtGui.QSlider.__init__(self, QtCore.Qt.Vertical, parent)
483
484         self.setTickPosition(QtGui.QSlider.TicksBothSides)
485         v_min = 10.0*toDBvalue(0)
486         v_max = 10.0*toDBvalue(65536)
487         self.setTickInterval((v_max-v_min)/10)
488         self.setMinimum(v_min)
489         self.setMaximum(v_max)
490         self.setSingleStep(1)
491         self.sliderSetValue(value)
492         self.In = In
493         self.Out = Out
494         self.connect(self, QtCore.SIGNAL("valueChanged(int)"), self.sliderValueChanged)
495
496     def sliderSetValue(self, value):
497         #log.debug("Volume slider value changed( %i )" % value)
498         v = 10.0*toDBvalue(value)
499         #log.debug("Volume slider value changed(dB: %g )" % (0.1*v))
500         self.setValue(v)
501
502     def sliderReadValue(self, value):
503         return fromDBvalue(0.1*value)
504
505     # Restore absolute value from DB
506     # Emit signal for further use, especially for matrix view
507     def sliderValueChanged(self, value):
508         value = fromDBvalue(0.1*value)
509         self.emit(QtCore.SIGNAL("valueChanged"), (self.In, self.Out, value))
510         self.update()
511
512
513 class VolumeSliderValueInfo(QtGui.QLineEdit):
514     def __init__(self, In, Out, value, parent):
515         QtGui.QLineEdit.__init__(self, parent)
516
517         self.setReadOnly(True)
518         self.setAlignment(Qt.Qt.AlignCenter)
519         self.setAutoFillBackground(True)
520         self.setFrame(False)
521
522         self.sliderSetMinimalDim()
523
524         self.bgcolors = BckgrdColorForNumber()
525
526         self.sliderSetValue(value)
527
528     def sliderSetMinimalDim(self):
529         fontmetrics = self.fontMetrics()
530         self.setMinimumSize(fontmetrics.boundingRect("-00.0 dB").size()*1.1)
531        
532     def sliderSetValue(self, value):
533         color = self.bgcolors.getColor(value)
534         palette = self.palette()
535         palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, color)
536         palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, self.bgcolors.getFrgdColor(color))
537         self.setPalette(palette)
538
539         v = round(toDBvalue(value),1)
540         if (v > -40):
541             text = "%.1f dB" % v
542         else:
543             symb_inf = u"\u221E"
544             text = "-" + symb_inf + " dB"
545
546         self.setText(text)
547        
548 class BalanceSlider(QtGui.QSlider):
549     def __init__(self, In, Out, value, parent):
550         QtGui.QSlider.__init__(self, QtCore.Qt.Horizontal, parent)
551
552         v_min = -50
553         v_max = 50
554         self.setTickPosition(QtGui.QSlider.TicksBothSides)
555         self.setTickInterval((v_max-v_min)/2)
556         self.setMinimum(v_min)
557         self.setMaximum(v_max)
558         self.setSingleStep(1)
559         self.In = In
560         self.Out = Out
561         self.sliderSetValue(value)
562         self.connect(self, QtCore.SIGNAL("valueChanged(int)"), self.sliderValueChanged)
563
564     def sliderSetValue(self, value):
565         #log.debug("Balance fader value set( %d, %d, %f )" % (self.In, self.Out, value))
566         v = int(round(50.0*value, 2))
567         self.setValue(v)
568
569     def sliderReadValue(self):
570         return float(round(self.value()/50.0, 2))
571
572     def sliderValueChanged(self, value):
573         value = float(round(self.value()/50.0, 2))
574         #log.debug("Balance fader value changed( %d, %d, %f )" % (self.In, self.Out, value))
575         self.emit(QtCore.SIGNAL("valueChanged"), (self.In, self.Out, value))
576
577 # Slider view widget
578 class SliderControlView(QtGui.QWidget):
579     def __init__(self, parent, servername, basepath, rule="Columns_are_inputs", shortname=False, shortinname="Ch", shortoutname="Ch", stereochannels = []):
580         QtGui.QWidget.__init__(self, parent)
581
582         self.bus = dbus.SessionBus()
583         self.dev = self.bus.get_object(servername, basepath)
584         self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.MatrixMixer")
585
586         self.rule = rule
587         self.shortname = shortname
588         self.shortinname = shortinname
589         self.shortoutname = shortoutname
590
591         self.stereochannels = stereochannels
592
593         self.out = []
594         self.nbIn = self.getNbIn()
595         self.nbOut = self.getNbOut()
596         self.outmatrix = []
597
598         k = 0
599         for i in range(self.nbOut):
600             widget = QtGui.QWidget(parent)
601             v_layout = QtGui.QVBoxLayout(widget)
602             v_layout.setAlignment(Qt.Qt.AlignCenter)
603             widget.setLayout(v_layout)
604             self.out.append(widget)
605
606             self.out[i].is_stereo = False
607             self.out[i].out_1 = k
608             self.outmatrix.append(i)
609             self.out[i].outname = "Out %d" % (k+1)
610             if k in self.stereochannels:
611                 self.out[i].is_stereo = True
612                 self.out[i].outname += "+%d" % (k+2)
613                 self.outmatrix.append(i)
614
615             self.out[i].lbl = []
616
617             # Mixer/Out info label
618             if (self.nbOut > 1):
619                 lbl = QtGui.QLabel(widget)
620                 lbl.setText(self.getOutName(i, self.shortname))
621                 lbl.setAlignment(Qt.Qt.AlignCenter)
622                 v_layout.addWidget(lbl)
623                 self.out[i].lbl.append(lbl)
624
625             h_layout_wid = QtGui.QWidget(widget)
626             h_layout = QtGui.QHBoxLayout(h_layout_wid)
627             h_layout.setAlignment(Qt.Qt.AlignCenter)
628             h_layout_wid.setLayout(h_layout)
629             v_layout.addWidget(h_layout_wid)
630             self.out[i].volume = []
631             self.out[i].svl = []
632             self.out[i].balance = []
633
634             for j in range(self.nbIn):
635                 h_v_layout_wid = QtGui.QWidget(h_layout_wid)
636                 h_v_layout = QtGui.QVBoxLayout(h_v_layout_wid)
637                 h_v_layout.setAlignment(Qt.Qt.AlignCenter)
638                 h_v_layout_wid.setLayout(h_v_layout)
639                 h_layout.addWidget(h_v_layout_wid)
640
641                 # Mixer/In info label
642                 if (self.nbIn > 1):
643                     lbl = QtGui.QLabel(h_v_layout_wid)
644                     lbl.setText(self.getInName(j, self.shortname))
645                     lbl.setAlignment(Qt.Qt.AlignCenter)
646                     h_v_layout.addWidget(lbl)
647                     self.out[i].lbl.append(lbl)
648
649                 h_v_h_layout_wid = QtGui.QWidget(h_v_layout_wid)
650                 h_v_h_layout = QtGui.QHBoxLayout(h_v_h_layout_wid)
651                 h_v_h_layout.setAlignment(Qt.Qt.AlignCenter)
652                 h_v_h_layout_wid.setLayout(h_v_h_layout)
653                 h_v_layout.addWidget(h_v_h_layout_wid)
654
655                 volume = VolumeSlider(j, i, self.getVolumeValue(j,i), h_v_h_layout_wid)
656                 h_v_h_layout.addWidget(volume)
657                 self.out[i].volume.append(volume)
658                 self.volumeConnect(volume)
659
660                 # Volume slider info
661                 svl = VolumeSliderValueInfo(j, i, self.getVolumeValue(j,i), h_v_layout_wid)
662                 h_v_layout.addWidget(svl)
663                 self.out[i].svl.append(svl)
664
665                 # Balance fader
666                 if self.out[i].is_stereo:
667                     balance = BalanceSlider(j, i, self.getBalanceValue(j,i), h_v_layout_wid)
668                     h_v_layout.addWidget(balance)
669                     self.out[i].balance.append(balance)
670                     self.balanceConnect(balance)
671             k += 1
672             if self.out[i].is_stereo:
673                 k += 1
674
675     def volumeConnect(self, volume):
676         self.connect(volume, QtCore.SIGNAL("valueChanged"), self.valueChangedVolume)
677
678     def volumeDisconnect(self, volume):
679         self.disconnect(volume, QtCore.SIGNAL("valueChanged"), self.valueChangedVolume)
680
681     def balanceConnect(self, balance):
682         self.connect(balance, QtCore.SIGNAL("valueChanged"), self.valueChangedBalance)
683
684     def balanceDisconnect(self, balance):
685         self.disconnect(balance, QtCore.SIGNAL("valueChanged"), self.valueChangedBalance)
686
687     def getNbIn(self):
688         if (self.rule == "Columns_are_inputs"):
689             return self.interface.getColCount()
690         else:
691             return self.interface.getRowCount()
692        
693     def getNbOut(self):
694         if (self.rule == "Columns_are_inputs"):
695             nbout = self.interface.getRowCount()
696         else:
697             nbout = self.interface.getColCount()
698         return nbout-len(self.stereochannels)
699        
700     def getVolumeValue(self, In, i):
701         Out = self.out[i].out_1
702         if (self.rule == "Columns_are_inputs"):
703             vl = self.interface.getValue(Out, In)           
704         else:
705             vl = self.interface.getValue(In, Out)
706         if (self.out[i].is_stereo):
707             if (self.rule == "Columns_are_inputs"):
708                 vr = self.interface.getValue(Out+1, In)           
709             else:
710                 vr = self.interface.getValue(In, Out+1)
711             return getStereoVolume(vl, vr)
712         else:
713             return vl;     
714
715     def getBalanceValue(self, In, i):
716         Out = self.out[i].out_1
717         if (self.rule == "Columns_are_inputs"):
718             vl = self.interface.getValue(Out, In)           
719             vr = self.interface.getValue(Out+1, In)           
720         else:
721             vl = self.interface.getValue(In, Out)
722             vr = self.interface.getValue(In, Out+1)
723         return getStereoBalance(vl, vr)
724
725     def setValue(self, In, Out, val):
726         if (self.rule == "Columns_are_inputs"):
727             return self.interface.setValue(Out, In, val)           
728         else:
729             return self.interface.setValue(In, Out, val)           
730
731     def updateValues(self, n):
732         nbitems = len(n)/3
733         for j in range(nbitems):
734             n_0 = n[3*j]   
735             n_1 = n[3*j+1]   
736             n_2 = n[3*j+2]
737             i = self.outmatrix[n_1]
738             if (self.out[i].is_stereo):
739                 v = self.getVolumeValue(n_0, i)
740                 self.volumeDisconnect(self.out[i].volume[n_0])
741                 self.balanceDisconnect(self.out[i].balance[n_0])
742                 self.out[i].volume[n_0].sliderSetValue(v)
743                 self.out[i].svl[n_0].sliderSetValue(v)
744                 b = self.getBalanceValue(n_0, i)       
745                 # log.debug("update Value (%d %d %d %f)" % (n_0, i, v, b))
746                 self.out[i].balance[n_0].sliderSetValue(b)
747                 self.volumeConnect(self.out[i].volume[n_0])
748                 self.balanceConnect(self.out[i].balance[n_0])
749             else:
750                 v = n_2
751                 # log.debug("update Value (%d %d %d)" % (n_0, i, v))
752                 self.volumeDisconnect(self.out[i].volume[n_0])
753                 self.out[i].volume[n_0].sliderSetValue(v)
754                 self.out[i].svl[n_0].sliderSetValue(v)
755                 self.volumeConnect(self.out[i].volume[n_0])
756        
757     def valueChangedVolume(self, n):
758         #log.debug("VolumeSlider.valueChanged( %s )" % str(n))
759         v = n[2]
760         n1 = self.out[n[1]].out_1
761         if (self.out[n[1]].is_stereo):
762             b = self.out[n[1]].balance[n[0]].value()/50.0
763             vl = int(getVolumeLeft(v, b))
764             self.setValue(n[0], n1, vl)
765             n2 = n1+1
766             vr = int(getVolumeRight(v, b))
767             self.setValue(n[0], n2, vr)
768             n_t = (n[0], n1, vl, n[0], n2, vr)
769             self.emit(QtCore.SIGNAL("valueChanged"), n_t)
770         else:
771             self.setValue(n[0], n1, v)
772             n_t = (n[0], n1, v)
773             self.emit(QtCore.SIGNAL("valueChanged"), n_t)
774         self.out[n[1]].svl[n[0]].sliderSetValue(v)
775
776     def valueChangedBalance(self, n):
777         #log.debug("BalanceSlider.valueChanged( %s )" % str(n))
778         n1 = self.out[n[1]].out_1
779         v = fromDBvalue(0.1*self.out[n[1]].volume[n[0]].value())
780         b = n[2]
781         vl = int(getVolumeLeft(v, b))
782         self.setValue(n[0], n1, vl)
783         n2 = n1+1
784         vr = int(getVolumeRight(v, b))
785         self.setValue(n[0], n2, vr)
786         n_t = (n[0], n1, vl, n[0], n2, vr)
787         self.emit(QtCore.SIGNAL("valueChanged"), n_t)
788
789     def getOutName(self, i, shortname):
790         self.shortname = shortname
791         k = self.out[i].out_1
792         if (shortname):
793             if (self.out[i].is_stereo):
794                 number = " %d+%d" % (k+1, k+2)
795             else:
796                 number = " %d" % (k+1)
797             name = self.shortoutname + number
798             return name
799         else:
800             if (self.rule == "Columns_are_inputs"):               
801                 if (self.out[i].is_stereo):
802                     name = self.interface.getRowName(k).replace('\n','')+" + "+self.interface.getRowName(k+1).replace('\n','')
803                 else:
804                     name = self.interface.getRowName(k).replace('\n','')
805                 return name
806             else:
807                 if (self.out[i].is_stereo):
808                     name = self.interface.getColName(k).replace('\n','')+" + "+self.interface.getColName(k+1).replace('\n','')
809                 else:
810                     name = self.interface.getColName(k).replace('\n','')
811                 return name
812
813     def getInName(self, j, shortname):
814         self.shortname = shortname
815         if (shortname):
816             number = " %d" % (j+1)
817             name = self.shortinname + number
818             return name
819         else:
820             if (self.rule == "Columns_are_inputs"):
821                 return self.interface.getColName(j)           
822             else:
823                 return self.interface.getRowName(j)           
824
825     # Update when routing is modified
826     def updateRouting(self):
827         for i in range(self.nbOut):
828             if (self.nbOut > 1):
829                 last_name = self.out[i].lbl[0].text()
830                 out_name = self.getOutName(i, self.shortname)
831                 if last_name != out_name:
832                     #log.debug("SliderControlView.updateRouting( %s )" % str(out_name))
833                     self.out[i].lbl[0].setText(out_name)
834             if (self.nbIn > 1):
835                 for j in range(self.nbIn):
836                     last_name = self.out[i].lbl[j+1].text()
837                     in_name = self.getInName(j, self.shortname)
838                     if last_name != in_name:
839                         #log.debug("SliderControlView.updateRouting( %s )" % str(in_name))
840                         self.out[i].lbl[j+1].setText(in_name)
841
842     def refreshValues(self):
843         for n_out in range(self.nbOut):
844             for n_in in range(self.nbIn):
845                 i = self.outmatrix[n_out]
846                 v = self.getVolumeValue(n_in, i)
847                 if (self.out[i].is_stereo):
848                     self.volumeDisconnect(self.out[i].volume[n_in])
849                     self.out[i].volume[n_in].sliderSetValue(v)
850                     self.out[i].svl[n_in].sliderSetValue(v)
851                     b = self.getBalanceValue(n_in, i)       
852                     # log.debug("update Value (%d %d %d %f)" % (n_0, i, v, b))
853                     self.out[i].balance[n_in].sliderSetValue(b)
854                     self.volumeConnect(self.out[i].volume[n_in])
855                 else:
856                     # log.debug("update Value (%d %d %d)" % (n_0, i, v))
857                     self.out[i].volume[n_in].sliderSetValue(v)
858                     self.out[i].svl[n_in].sliderSetValue(v)
859
860 from functools import partial
861
862 class MatrixMixer(QtGui.QWidget):
863     def __init__(self, servername, basepath, parent=None, rule="Columns_are_inputs", sliderMaxValue=-1, mutespath=None, invertspath=None, smallFont=False, taborientation=QtGui.QTabWidget.West, tabshape=QtGui.QTabWidget.Triangular):
864         QtGui.QWidget.__init__(self, parent)
865         self.servername = servername
866         self.basepath = basepath
867         self.sliderMaxValue = sliderMaxValue
868         self.mutespath = mutespath
869         self.invertspath = invertspath
870         self.smallFont = smallFont
871
872         self.layout = QtGui.QVBoxLayout(self)
873         self.setLayout(self.layout)
874
875         # Mixer view Tool bar
876         mxv_set = QtGui.QToolBar("View settings", self)
877
878         # Here is a hack; the first action button appears to behaves strangely,
879         # possibly a PyQt bug (or an unsufficient fair implementation of it)
880         # Feel free to remove the next three lines at a time in the future
881         hack = QtGui.QAction(" ", mxv_set)
882         hack.setDisabled(True)
883         mxv_set.addAction(hack)
884
885         transpose_matrix = QtGui.QAction("Transpose", mxv_set)
886         self.transpose = False
887         transpose_matrix.setShortcut('Ctrl+T')
888         transpose_matrix.setToolTip("Invert rows and columns in Matrix view")
889         mxv_set.addAction(transpose_matrix)
890         transpose_matrix.triggered.connect(self.transposeMatrixView)
891         mxv_set.addSeparator()
892
893         self.hide_matrix = QtGui.QAction("Hide matrix", mxv_set)
894         self.hide_matrix_bool = False
895         mxv_set.addAction(self.hide_matrix)
896         self.hide_matrix.triggered.connect(self.hideMatrixView)
897         mxv_set.addSeparator()
898
899         self.hide_per_output = QtGui.QAction("Hide per Output", mxv_set)
900         self.hide_per_output_bool = False
901         mxv_set.addAction(self.hide_per_output)
902         self.hide_per_output.triggered.connect(self.hidePerOutputView)
903         mxv_set.addSeparator()
904
905         self.use_short_names = QtGui.QAction("Short names", mxv_set)
906         self.short_names_bool = False
907         mxv_set.addAction(self.use_short_names)
908         self.use_short_names.setToolTip("Use short or full names for input and output channels")
909         self.use_short_names.triggered.connect(self.shortChannelNames)
910         mxv_set.addSeparator()
911
912         font_switch_lbl = QtGui.QLabel(mxv_set)
913         font_switch_lbl.setText("Font size ")
914         mxv_set.addWidget(font_switch_lbl)
915         font_switch = QtGui.QComboBox(mxv_set)
916         font_switch.setToolTip("Labels font size")
917         font = font_switch.font()
918         for i in range(10):
919             font_switch.addItem(" %d " % (font.pointSize()+4-i))
920         font_switch.setCurrentIndex(font_switch.findText(" %d " % font.pointSize()))
921         mxv_set.addWidget(font_switch)
922         mxv_set.addSeparator()
923         self.connect(font_switch, QtCore.SIGNAL("activated(QString)"), self.changeFontSize)
924
925         self.layout.addWidget(mxv_set)
926         self.mxv_set = mxv_set
927
928         # First tab is for matrix view
929         # Next are for "per Out" view
930         self.tabs = QtGui.QTabWidget(self)
931         self.tabs.setTabPosition(taborientation)
932         self.tabs.setTabShape(tabshape)
933         self.layout.addWidget(self.tabs)
934
935         # Inputs/Outputs versus rows/columns rule
936         self.rule = rule
937
938         # Matrix view tab
939         if (rule == "Columns_are_inputs"):
940             self.matrix = MatrixControlView(servername, basepath, self, sliderMaxValue, mutespath, invertspath, smallFont, self.short_names_bool, "In", "Out", self.transpose)
941         else:
942             self.matrix = MatrixControlView(servername, basepath, self, sliderMaxValue, mutespath, invertspath, smallFont, self.short_names_bool, "Out", "In", self.transpose)
943         self.connect(self.matrix, QtCore.SIGNAL("valueChanged"), self.matrixControlChanged)
944
945         self.scrollarea_matrix = QtGui.QScrollArea(self.tabs)
946         self.scrollarea_matrix.setWidgetResizable(True)
947         self.scrollarea_matrix.setWidget(self.matrix)
948         self.tabs.addTab(self.scrollarea_matrix, " Matrix ")
949
950         # Add stereo/mono output choice in tool bar
951         if (rule == "Columns_are_inputs"):
952             if (self.transpose):
953                 nb_out_mono = self.matrix.cols
954             else:
955                 nb_out_mono = self.matrix.rows
956         else:
957             if (self.transpose):
958                 nb_out_mono = self.matrix.rows
959             else:
960                 nb_out_mono = self.matrix.cols
961
962         stereo_switch_lbl = QtGui.QLabel(mxv_set)
963         stereo_switch_lbl.setText("Stereo: ")
964         mxv_set.addWidget(stereo_switch_lbl)
965
966         self.stereo_channels = []
967
968         self.stereo_switch = []
969         for i in range(nb_out_mono/2):
970             stereo_switch = QtGui.QPushButton("%d+%d" % (2*i+1, 2*i+2), mxv_set)
971             stereo_switch.setToolTip("Set these output channels as stereo")
972             stereo_switch.setCheckable(True)
973             stereo_switch.clicked.connect(partial(self.switchStereoChannel, i))
974             stereo_switch.setMinimumSize(stereo_switch_lbl.fontMetrics().boundingRect("%d+%d" % (nb_out_mono, nb_out_mono)).size()*1.05)
975             stereo_switch.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum))
976             stereo_switch.is_stereo = False
977             mxv_set.addWidget(stereo_switch)
978             self.stereo_switch.append(stereo_switch)
979         mxv_set.addSeparator()
980
981         # Per out view tabs
982         self.perOut = SliderControlView(self, servername, basepath, rule, self.short_names_bool, "In", "Out", self.stereo_channels)
983         self.connect(self.perOut, QtCore.SIGNAL("valueChanged"), self.sliderControlChanged)
984         for i in range(self.perOut.nbOut):
985             self.perOut.out[i].scrollarea = QtGui.QScrollArea(self.tabs)
986             self.perOut.out[i].scrollarea.setWidgetResizable(True)
987             self.perOut.out[i].scrollarea.setWidget(self.perOut.out[i])
988             self.tabs.addTab(self.perOut.out[i].scrollarea, " %s " % self.perOut.out[i].outname)
989
990     def transposeMatrixView(self):
991         self.transpose = not(self.transpose)
992         self.tabs.removeTab(0)
993         self.scrollarea_matrix.destroy()
994         if (self.rule == "Columns_are_inputs"):
995             self.matrix = MatrixControlView(self.servername, self.basepath, self, self.sliderMaxValue, self.mutespath, self.invertspath, self.smallFont, self.short_names_bool, "In", "Out", self.transpose)
996         else:
997             self.matrix = MatrixControlView(self.servername, self.basepath, self, self.sliderMaxValue, self.mutespath, self.invertspath, self.smallFont, self.short_names_bool, "Out", "In", self.transpose)
998         self.connect(self.matrix, QtCore.SIGNAL("valueChanged"), self.matrixControlChanged)
999
1000         self.scrollarea_matrix = QtGui.QScrollArea(self.tabs)
1001         self.scrollarea_matrix.setWidgetResizable(True)
1002         self.scrollarea_matrix.setWidget(self.matrix)
1003         self.tabs.insertTab(0, self.scrollarea_matrix, "Matrix")
1004         self.tabs.setCurrentIndex(0)
1005        
1006     def hideMatrixView(self):
1007         self.hide_matrix_bool = not(self.hide_matrix_bool)
1008         if (self.hide_matrix_bool):
1009             self.tabs.removeTab(0)
1010             self.hide_matrix.setText("Show Matrix")
1011         else:
1012             self.tabs.insertTab(0, self.scrollarea_matrix, "Matrix")
1013             self.tabs.setCurrentIndex(0)
1014             self.hide_matrix.setText("Hide Matrix")
1015            
1016     def hidePerOutputView(self):
1017         self.hide_per_output_bool = not(self.hide_per_output_bool)
1018         if (self.hide_per_output_bool):
1019             index_0 = 1
1020             if (self.hide_matrix_bool):
1021                 index_0 = 0
1022             for i in range(self.perOut.nbOut):
1023                 self.tabs.removeTab(index_0)
1024             self.hide_per_output.setText("Show per Output")
1025         else:
1026             for i in range(self.perOut.nbOut):
1027                 self.tabs.insertTab(i+1, self.perOut.out[i].scrollarea, " %s " % self.perOut.out[i].outname)
1028             self.hide_per_output.setText("Hide per Output")
1029
1030     # Font size for channel names
1031     def changeFontSize(self, size):
1032         font = self.mxv_set.font()
1033         font.setPointSize(int(size))
1034         self.mxv_set.setFont(font)
1035
1036         font = self.tabs.font()
1037         font.setPointSize(int(size))
1038         self.tabs.setFont(font)
1039
1040         font = self.matrix.font()
1041         font.setPointSize(int(size))
1042         self.matrix.setFont(font)
1043
1044         font = self.perOut.font()
1045         font.setPointSize(int(size))
1046         self.perOut.setFont(font)
1047
1048         for i in range(self.perOut.nbOut):
1049             for j in range(self.perOut.nbIn):
1050                 self.perOut.out[i].svl[j].sliderSetMinimalDim()
1051
1052     # Allows long name for Mixer/Out and /In to be hidden
1053     def shortChannelNames(self):
1054         checked = not(self.short_names_bool)
1055         if (self.matrix.cols > 1):
1056             for i in range(self.matrix.cols):
1057                 self.matrix.columnHeaders[i].name = self.matrix.getColName(i, checked)
1058                 self.matrix.columnHeaders[i].lbl.setText(self.matrix.columnHeaders[i].name)
1059
1060         if (self.matrix.rows > 1):
1061             for j in range(self.matrix.rows):
1062                 self.matrix.rowHeaders[j].name = self.matrix.getRowName(j, checked)
1063                 self.matrix.rowHeaders[j].lbl.setText(self.matrix.rowHeaders[j].name)       
1064
1065         for i in range(self.perOut.nbOut):
1066             if (self.perOut.nbOut > 1):
1067                 self.perOut.out[i].lbl[0].setText(self.perOut.getOutName(i, checked))
1068             if (self.perOut.nbIn > 1):
1069                 for j in range(self.perOut.nbIn):
1070                     self.perOut.out[i].lbl[j+1].setText(self.perOut.getInName(j, checked))
1071
1072         # Care for hidden columns
1073         if (self.matrix.cols > 1):
1074             for i in self.matrix.hiddenCols:
1075                 self.matrix.columnHeaders[i].lbl.setText("%d" % (i+1))
1076         # Care for hidden rows
1077         if (self.matrix.rows > 1):
1078             for j in self.matrix.hiddenRows:
1079                 self.matrix.rowHeaders[j].lbl.setText("%d" % (j+1))
1080
1081         self.short_names_bool = checked
1082         if (self.short_names_bool):
1083             self.use_short_names.setText("Long names")
1084         else:
1085             self.use_short_names.setText("Short names")
1086
1087     # Sliders value change
1088     #   Care that some recursive process is involved and only stop when exactly same values are involved
1089     # Matrix view
1090     def matrixControlChanged(self, n):
1091         # Update value needed for "per Out" view
1092         #log.debug("Update per Output( %s )" % str(n))
1093         nbitems = len(n)/3
1094         if (self.rule == "Columns_are_inputs"):
1095            n_t = n
1096         else:
1097             n_t = ()
1098             for i in range(nbitems):
1099                 n_t += (n[3*i+1], n[3*i], n[3*i+2])
1100
1101         self.perOut.updateValues(n_t)
1102
1103     # "per Out" view
1104     def sliderControlChanged(self, n):
1105         # Update value needed for matrix view
1106         #log.debug("Update Matrix( %s )" % str(n))
1107         nbitems = len(n)/3
1108         if (((self.rule == "Columns_are_inputs") and not self.transpose) or ((self.rule != "Columns_are_inputs") and self.transpose)):
1109             n_t = ()
1110             for i in range(nbitems):
1111                 n_t += (n[3*i+1], n[3*i], n[3*i+2])
1112         else:
1113             n_t = n
1114
1115         self.matrix.updateValues(n_t)
1116
1117     def refreshValues(self):
1118         # Refreshing matrix coefficient should be sufficient,
1119         #  propagating the changes to perOut view
1120         self.matrix.refreshValues()
1121
1122     def switchStereoChannel(self, channel, is_stereo):
1123         #log.debug(" switching channels %d+%d to stereo/mono" % (2*channel, 2*channel+1))
1124         self.stereo_switch[channel].is_stereo = self.stereo_switch[channel].isChecked();
1125         if (self.stereo_switch[channel].is_stereo):
1126             self.stereo_channels.append(2*channel)
1127         else:
1128             self.stereo_channels.remove(2*channel)
1129
1130         # tab 0 is for matrix except if it is hidden
1131         index_0 = 1
1132         if (self.hide_matrix_bool):
1133             index_0 = 0
1134         for i in range(self.perOut.nbOut):
1135             self.tabs.removeTab(index_0)
1136         self.perOut.destroy()
1137         self.perOut = SliderControlView(self, self.servername, self.basepath, self.rule, self.short_names_bool, "In", "Out", self.stereo_channels)
1138         self.connect(self.perOut, QtCore.SIGNAL("valueChanged"), self.sliderControlChanged)
1139         current = 0
1140         for i in range(self.perOut.nbOut):
1141             self.perOut.out[i].scrollarea = QtGui.QScrollArea(self.tabs)
1142             self.perOut.out[i].scrollarea.setWidgetResizable(True)
1143             self.perOut.out[i].scrollarea.setWidget(self.perOut.out[i])
1144             self.tabs.addTab(self.perOut.out[i].scrollarea, " %s " % self.perOut.out[i].outname)
1145             if self.perOut.out[i].out_1 == 2*channel:
1146                 current = i
1147
1148         self.tabs.setCurrentWidget(self.perOut.out[current].scrollarea)
1149
1150     # Update when routing is modified
1151     def updateRouting(self):
1152         self.matrix.updateRouting()
1153         self.perOut.updateRouting()
1154        
1155 #
1156 # vim: et ts=4 sw=4 fileencoding=utf8
Note: See TracBrowser for help on using the browser.