После перехода на OpenBox я пытался найти нормальный регулятор громкости звука в определённом канале для системного трея. Но искал я такую программу не долго. Вспомнив о команде amixer, я твердо решил создать что то свое.
Программа писалась на одном дыхании и потому содержит не точности, которые со временем я поправлю.
Собственно окно моей программы amixvol |
Инструменты:
- пакет python-qt4 версии не ниже 4.7.3
- пакет python версии не ниже 2.5 (на более низких версиях не тестировалось)
Требования:
- Иконка в трее (две одна на без звук другая при включенном канале)
- Появление окна регулирования при счилчке левой кнавиши мыши по иконке в трее.
- Прятать окно при потере фокуса с объекта полосы прокрутки.
- Слежение за уровнем звука на канале (индикатор включение выключения звука)
Описание работы программы amixvol
Иконки для программы я брал из темы Faenza (audio-volume-high.svg, audio-volume-muted.svg)
Для определения места нахождения иконок использовал полные пути (без заморочек с текущим каталогом)
pathsvg = '/home/cryptspirit/Sha-bang/audio/'
pic_high = pathsvg + 'audio-volume-high.svg'
pic_muted = pathsvg + 'audio-volume-muted.svg'
Для обработки события потери фокуса я подправил стандартный класс QtGui.QSlider создав свой slider_Event. В составе которого я изменил только процедуры потери фокуса, инициализации объекта и получения фокуса. Проблема заключается в том что в окне программы у нас находиться два объекта self.slider(сам регулятор громкости) и self.mCheckBox(переключатель режима без звука). Дело в том что пока у меня возникают заморочки со скрытием окна программы (писал программу стихийно, как только заработала более менее нормально то изменять код я перестал и отложил это до лучших времен). Когда один объект теряет фокус, а второй его еще не получил окно должно закрыться. Но объект mCheckBox у меня почему то упорно сбрасывает с себя фокус и у меня еще не было времени отработать все варианты с потерей и передачей фокуса. Для первого варианта я использовал флажок flag, если его значение находиться в положении False то окно отображаться не будет.
Далее идет описание канала с каким будем работатьchannel = 'Front'
Я специально не стал заострять внимание на выборе канала(возможно это будет реализовано в графическом интерфейсе). Пока что канал можно определить как текстовую переменную channel. Для отображение текущего канала который использует amixvol использую подсказки
self.tray.setToolTip('[' + channel + ']')
Расмотрим подробнее работу функции получения информации о состоянии звукового канала
def getval(self, chl):
s = subprocess.Popen(['amixer', '-c', '0', 'get', channel],
stdout=subprocess.PIPE).communicate()[0]
try: re.search(r"\[off\]", s).group()
except:
self.mCheckBox.setChecked(False)
else:
self.tray.setIcon(QtGui.QIcon(pic_muted))
self.mCheckBox.setChecked(True)
s1 = re.search(r"" + chl + " Left: Playback.* \[",
s).group()[21:-1]
val = re.search(r".*\d \[", s1).group()[:-2]
s2 = re.search(r"Limits: Playback .*", s).group()[17:]
minn = re.search(r".* -", s2).group()[:-2]
return int(s2[len(minn) + 2:]), int(minn), int(val)
Как раньше и описывалось для манипуляции со звуком и получение информации о состоянии звукового канала будем использовать утилиту amixer. Вызываем amixer для получения данных о состоянии нашего указанного канала.
s = subprocess.Popen(['amixer', '-c', '0', 'get', channel], stdout=subprocess.PIPE).communicate()[0]
далее используя регулярные выражения определяем режим (звук или без звука). Это делается просто. Выражение "amixer -c 0 get Front" возвращает приблезительно следующее:
Simple mixer control 'Front',0
Capabilities: pvolume pswitch
Playback channels: Front Left - Front Right
Limits: Playback 0 - 64
Mono:
Front Left: Playback 39 [61%] [-25.00dB] [off]
Front Right: Playback 39 [61%] [-25.00dB] [off]
Где "[off]" указывает на беззвучный режим канала. Используя исключения я определяю наличие текста "[off]" в полученной строке.
Если звук выключен есть смысл поставить иконку безвучного режима и установить значение объекта mCheckBox в True(поставить флажок). После чего снова используя регулярные выражения получаем уровень громкости звукового канала.
s1 = re.search(r"" + chl + " Left: Playback.* \[",
s).group()[21:-1]
Определяем текущее показание громкости
val = re.search(r".*\d \[", s1).group()[:-2]
Определяем границы громкости
minn = re.search(r".* -", s2).group()[:-2]
return int(s2[len(minn) + 2:]), int(minn), int(val)
И возвращаем их при выходе из функции
Далее осталось описать процедуры контроля громкости
def Sound_Control(self):
self.indk()
subprocess.Popen(['amixer', '-c', '0', 'set', channel,
str(self.slider.value())],
stdout=subprocess.PIPE)
def S_Control(self):
if self.mCheckBox.isChecked() == True:
subprocess.Popen(['amixer', '-c', '0', 'set', channel,
'mute'], stdout=subprocess.PIPE)
else:
subprocess.Popen(['amixer', '-c', '0', 'set', channel,
'unmute'], stdout=subprocess.PIPE)
self.indk()
Главным индикатором здесь выступает состояние объекта mCheckBox. В процедуре Sound_Control, которая вызывается при извинении полосы прокрутки берется текущее положение полосы и через вызов утилиты amixer устанавливается определенный уровень громкости. Процедура S_Control вызывается по извинению значения флажка mCheckBox. А потом опираясь на его состояние устанавливается беззвучный режим или нет.
Это краткое описание работы программы amixvol. Оно как и сам код программы в этой статье еще будет меняться в ходи улучшения самой программы.
Скачать файл программы можно здесь
Листиг всей программы:
import subprocess
from PyQt4 import QtCore, QtGui
import sys
import time
import re
flag = False
pathsvg = '/home/cryptspirit/Sha-bang/audio/'
pic_high = pathsvg + 'audio-volume-high.svg'
pic_muted = pathsvg + 'audio-volume-muted.svg'
channel = 'Front'
class slider_Event(QtGui.QSlider):
def __init__(self, parent=None):
QtGui.QSlider.__init__(self)
self.flag = False
def focusOutEvent(self, event):
self.flag = False
def setFocus(self):
self.flag = True
class WidgetLayer(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, None, QtCore.Qt.Tool |
QtCore.Qt.FramelessWindowHint |
QtCore.Qt.WindowStaysOnTopHint)
self.tray = QtGui.QSystemTrayIcon(QtGui.QIcon(pic_high))
self.tray.activated.connect(self.iconActivated)
self.tray.setToolTip('[' + channel + ']')
self.tray.show()
self.val = 0
self.ck = False
self.createActions()
mainLayout = QtGui.QVBoxLayout()
self.slider = slider_Event(QtCore.Qt.Vertical)
self.slider.valueChanged.connect(self.Sound_Control)
self.mCheckBox = QtGui.QCheckBox('')
self.mCheckBox.toggled.connect(self.S_Control)
mainLayout.addWidget(self.slider)
mainLayout.addWidget(self.mCheckBox)
self.setLayout(mainLayout)
self.getval(channel)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.lostfocus)
self.timer.start(3000)
def lostfocus(self):
if self.slider.flag == False:
self.setVisible(False)
def getval(self, chl):
s = subprocess.Popen(['amixer', '-c', '0', 'get', channel],
stdout=subprocess.PIPE).communicate()[0]
try: re.search(r"\[off\]", s).group()
except:
self.mCheckBox.setChecked(False)
else:
self.tray.setIcon(QtGui.QIcon(pic_muted))
self.mCheckBox.setChecked(True)
s1 = re.search(r"" + chl + " Left: Playback.* \[",
s).group()[21:-1]
val = re.search(r".*\d \[", s1).group()[:-2]
s2 = re.search(r"Limits: Playback .*", s).group()[17:]
minn = re.search(r".* -", s2).group()[:-2]
return int(s2[len(minn) + 2:]), int(minn), int(val)
def sw(self):
self.slider.setFocus()
max, min, val = self.getval(channel)
self.slider.setMaximum(max)
self.slider.setMinimum(min)
self.slider.setValue(val)
self.setGeometry(QtGui.QCursor.pos().x(),
QtGui.QCursor.pos().y(), 10, 150)
self.setVisible(True)
def createActions(self):
quitAction = QtGui.QAction("&Quit", QtGui.qApp,
triggered=self.qui)
self.trayIconMenu = QtGui.QMenu()
self.trayIconMenu.addAction(quitAction)
self.tray.setContextMenu(self.trayIconMenu)
def iconActivated(self, reason):
if reason == QtGui.QSystemTrayIcon.Trigger:
self.sw()
def qui(self):
self.hide()
sys.exit(1)
def indk(self):
if self.mCheckBox.isChecked() != True:
self.tray.setIcon(QtGui.QIcon(pic_high))
else:
self.tray.setIcon(QtGui.QIcon(pic_muted))
def Sound_Control(self):
self.indk()
subprocess.Popen(['amixer', '-c', '0', 'set', channel,
str(self.slider.value())],
stdout=subprocess.PIPE)
def S_Control(self):
if self.mCheckBox.isChecked() == True:
subprocess.Popen(['amixer', '-c', '0', 'set', channel,
'mute'], stdout=subprocess.PIPE)
else:
subprocess.Popen(['amixer', '-c', '0', 'set', channel,
'unmute'], stdout=subprocess.PIPE)
self.indk()
def main():
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setQuitOnLastWindowClosed(False)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
volcontr = WidgetLayer()
sys.exit(app.exec_())
return 0
if __name__ == '__main__':
main()
Предуприждение:
Программа находиться в стадии разработки и если вы увидели не точности либо у вас есть предложения по изменению прицепа работы программы пишите в комментарии. Возможно автор тоже давно заметил свои недочеты но за недостатком свободного времени решил использовать хотя бы такой (рабочий) вариант. Это предупреждение исчезнет после выхода окончательной версии.
0 коммент. on "Регулятор громкости на Python amixvol"
Отправить комментарий