diff --git a/main.py b/main.py deleted file mode 100644 index 70d856c..0000000 --- a/main.py +++ /dev/null @@ -1,522 +0,0 @@ -import os -import sys -import json -from time import time -import threading -import soundfile as sf -import sounddevice as sd -from pynput.keyboard import Listener -from PyQt5 import QtWidgets, QtGui, QtCore -from PyQt5.QtCore import QFile, QTextStream -from PyQt5.QtWidgets import QApplication -import ui_preferences -import ui_hotkeys -import ui_sundpood -import ui_overlay -import themes -import keys - - -###! UI !### -class OverlayUi(QtWidgets.QMainWindow, ui_overlay.Ui_MainWindow): - def __init__(self, parent=None): - super().__init__(parent) - self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) - self.setGeometry(QtCore.QRect(0, 0, 250, 20)) - self.setupUi(self) - - def keyPressEvent(self, e): - if e.key() == QtCore.Qt.Key_F1: - self.hide() - win.show() - -class PreferencesUi(QtWidgets.QMainWindow, ui_preferences.Ui_MainWindow): - def __init__(self, parent=None): - super().__init__(parent) - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - self.setupUi(self) - - def mousePressEvent(self, event): - self.offset = event.pos() - - def mouseMoveEvent(self, event): - x = event.globalX() - y = event.globalY() - x_w = self.offset.x() - y_w = self.offset.y() - self.move(x-x_w, y-y_w) - -class HotkeysUi(QtWidgets.QMainWindow, ui_hotkeys.Ui_MainWindow): - def __init__(self, parent=None): - super().__init__(parent) - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - self.setupUi(self) - - def mousePressEvent(self, event): - self.offset = event.pos() - - def mouseMoveEvent(self, event): - x = event.globalX() - y = event.globalY() - x_w = self.offset.x() - y_w = self.offset.y() - self.move(x-x_w, y-y_w) - -class MainUi(QtWidgets.QMainWindow, ui_sundpood.Ui_MainWindow): - def __init__(self, parent=None): - super().__init__(parent) - self.setWindowFlags(QtCore.Qt.FramelessWindowHint) - self.setupUi(self) - - def mousePressEvent(self, event): - self.offset = event.pos() - - def mouseMoveEvent(self, event): - x = event.globalX() - y = event.globalY() - x_w = self.offset.x() - y_w = self.offset.y() - self.move(x-x_w, y-y_w) - - def keyPressEvent(self, e): - if e.key() == QtCore.Qt.Key_F1: - pref.close() - self.hide() - over.show() - - def closeEvent(self, event): - hotk.close() - pref.close() - - -###! JSON !### -def jsonread(file): - ''' - Чтение JSON файла file - file - имя JSON файла, может содержать полный или относительный путь - ''' - with open(file, "r", encoding='utf-8') as read_file: - data = json.load(read_file) - return data - -def jsonwrite(file, data): - ''' - Запись в JSON файл file данные data - file - имя JSON файла, может содержать полный или относительный путь - data - данные, может быть словарем/списком/кортежем/строкой/числом - ''' - with open(file, 'w', encoding='utf-8') as write_file: - write_file.write(json.dumps(data)) - - -###! FUNCTIONS !### -def toggle_stylesheet(path): - ''' - Toggle the stylesheet to use the desired path in the Qt resource - system (prefixed by `:/`) or generically (a path to a file on - system). - - :path: A full path to a resource or file on system - ''' - - # get the QApplication instance, or crash if not set - app = QApplication.instance() - if app is None: - raise RuntimeError("No Qt Application found.") - - path = os.path.join('themes', path) - file = QFile(path) - file.open(QFile.ReadOnly | QFile.Text) - stream = QTextStream(file) - theme = stream.readAll() - - pref.setStyleSheet(theme) - win.setStyleSheet(theme) - hotk.setStyleSheet(theme) - save() - -def find_device(): - ''' - Ищем устройство device и возвращаем его индекс - ''' - - list_ = list(sd.query_devices()) # Список устройств вывода звука - device = 'VoiceMeeter Input' # Имя искомого устройства - - msg = QtWidgets.QMessageBox() # Окно ошибки (Устройство не найдено) - msg.setIcon(QtWidgets.QMessageBox.Critical) - msg.setText("You don't install VoiceMeeter") - msg.setInformativeText('install VoiceMetter from "redist" folder or download it from \nvb-audio.com/Voicemeeter') - msg.setWindowTitle('Error') - - for i in list_: - if device in i['name']: - return i['name'] - - msg.exec_() - exit() - -def sound_convert(dir_, format_): - ''' - Конвертация файла name в папке path в формат format_ - по средством ffmpeg.exe - - path - папка с файлом, может содержать полный или относительный путь - name - название файла, может только название файла - format_ - формат в который нужно конвертировать - ''' - if os.path.exists('ffmpeg.exe'): - path, name = os.path.split(dir_) - old_name = name - if os.path.splitext(dir_)[1] in ['.mp3', '.m4a']: - name = os.path.splitext(name)[0] + format_ - os.system(f'ffmpeg.exe -i "{os.path.join(path, old_name)}" "play.wav" -hide_banner -y') - return "play.wav" - else: - return dir_ - -def get_files(dir_, config): - ''' - Сбор всех аудифайлов - dir_ - Путь к папке со звуками - config - Имя файла настроек - ''' - msg = QtWidgets.QMessageBox() # Окно ошибки (Не найдены файлы в папке 'sound') - msg.setIcon(QtWidgets.QMessageBox.Critical) - msg.setText("You don't have any sounds in 'sound' folder") - msg.setInformativeText('download sound in .wav / .mp3 / .m4a format') - msg.setWindowTitle('Error') - - ffmsg = QtWidgets.QMessageBox() # Окно ошибки (Не найден ffmpeg.exe) - ffmsg.setIcon(QtWidgets.QMessageBox.Critical) - ffmsg.setText("You don't have ffmpeg.exe in the program root folder") - ffmsg.setInformativeText('download ffmpeg from ffmpeg.org for converting sound files') - ffmsg.setWindowTitle('Error') - - if os.path.exists(dir_): - if len(os.listdir(dir_)) == 0: - msg.exec_() - - sounds = [] # Все аудио файлы - sounds_list = [f'{dir_}\\'] # Начальная категория - - if not os.path.exists('ffmpeg.exe'): - ffmsg.exec_() - exit() - - for i in os.listdir(dir_): - if os.path.isfile(os.path.join(dir_, i)): - name = os.path.join(dir_, i) - if name == None: - sounds_list.append(i) - elif os.path.splitext(i)[1] in ['.mp3', '.m4a', '.wav']: - sounds_list.append(i) - else: - sounds_list_cat = [os.path.join(dir_, i)] - for x in os.listdir(os.path.join(dir_, i)): - if os.path.isfile(os.path.join(dir_, i, x)): - if name == None: - sounds_list_cat.append(x) - elif os.path.splitext(x)[1] in ['.mp3', '.m4a', '.wav']: - sounds_list_cat.append(x) - sounds.append(sounds_list_cat) - sounds.append(sounds_list) - - if os.path.exists(config): - hotkeys = jsonread(config)['hotkeys'] - KEYS_CMD = jsonread(config)['KEYS_CMD'] - sounds_list = { 'hotkeys':hotkeys, - 'sounds':sounds, - 'Theme':'None', - 'KEYS_CMD':KEYS_CMD} - else: - sounds_list = { 'hotkeys':{}, - 'sounds':sounds, - 'Theme':'None', - 'KEYS_CMD':{ - 'select_move_up' :' ',# вверх - 'select_move_down' :' ',# вниз - 'select_move_left' :' ',# влево - 'select_move_right' :' ',# вправо - 'play_sound' :' ',# Играть - 'stop_sound' :' ',# Остановить - }} - - jsonwrite(config, sounds_list) - else: - sounds_list = { 'hotkeys':{}, - 'sounds':'', - 'Theme':'None', - 'KEYS_CMD':{ - 'select_move_up' :' ',# вверх - 'select_move_down' :' ',# вниз - 'select_move_left' :' ',# влево - 'select_move_right' :' ',# вправо - 'play_sound' :' ',# Играть - 'stop_sound' :' ',# Остановить - }} - jsonwrite(config, sounds_list) - os.mkdir(dir_) - msg.exec_() - -def play_sound(*argv): - if False in argv: - try: - sound = os.path.join(win.soundList.item(0).text(), - win.soundList.currentItem().text()) - except AttributeError: - return False - else: - sound = argv[0] - try: - sound = sound_convert(sound, '.wav') - data, fs = sf.read(sound, dtype='float32') - sd.play(data, fs) - #os.remove(sound) - except RuntimeError: - pass - -def cat_select(cat): - win.soundList.clear() - for i in menu: - if cat == i[0]: - win.soundList.addItems(i) - -def hotkey_remap(): - ''' - Переназначение хоткея - btn - индекс кнопки хоткея в списке HOTKEYS - ''' - def check(key): - key = str(key).replace("'",'') - try: - sound = os.path.join(win.soundList.item(0).text(), - win.soundList.currentItem().text()) - except AttributeError: - win.hkset.setEnabled(True) - return False - - if key not in keys.forbidden: - hotkeys.update({key:sound}) - elif key == 'Key.backspace': - del hotkeys[find_key(hotkeys, sound)] - - save() - hotk.hotkeyList.clear() - for i in sound_get_dict['sounds']: - for x in i: - x = os.path.join(i[0], x) - if x in hotkeys.values(): - hotk.hotkeyList.addItem(f'{find_key(hotkeys, x)}\t:{x}') - - win.hkset.setEnabled(True) - return False - - win.hkset.setEnabled(False) - - hotkey_remap_Listener = Listener( - on_release=check) - hotkey_remap_Listener.start() - -def hotkey_delete(): - key = hotk.hotkeyList.currentItem().text().split(':')[0].replace('\t', '') - hotkeys.pop(key) - - save() - hotk.hotkeyList.clear() - for i in sound_get_dict['sounds']: - for x in i: - x = os.path.join(i[0], x) - if x in hotkeys.values(): - hotk.hotkeyList.addItem(f'{find_key(hotkeys, x)}\t:{x}') - -def pref_remap(btn, func_): - ''' - Переназначение клавиши в окно Preference - btn - PyQt5 кнопка - func_ - строковое значени функции из словаря KEYS_CMD - ''' - def check(key): - ''' - Проверка кнопки btn на не участие - в списке запрещенных клавиш keys.forbidden - если это клавиша 'Key.backspace' то стираем значение - key - pynput код клавиши - ''' - key = str(key).replace("'",'') - if key not in keys.forbidden: - KEYS_CMD.update({find_key(COMMAND_DICT, func_) : key}) - btn.setText(keys.dict_[key]) - elif key == 'Key.backspace': - KEYS_CMD.update({find_key(COMMAND_DICT, func_) : ' '}) - btn.setText(' ') - for i in PREF_BTN: - i.setEnabled(True) - save() - return False - - for i in PREF_BTN: - i.setEnabled(False) - - hotkey_remap_Listener = Listener( - on_release=check) - hotkey_remap_Listener.start() - -def find_key(dict, val): - ''' - Поиск ключа в словаре dict по значению val - dict - словарь - val - значение - ''' - return next((key for key, value in dict.items() if value == val), None) - -def save(): - ''' - Сохранение настроек оверлея и глобальных хоткеев - ''' - sounds = jsonread(config)['sounds']# Все аудиофайлы - - KEYS_JSON = {} # Настроенные клавиши - for i in KEYS_CMD.keys(): - KEYS_JSON.setdefault(COMMAND_DICT[i], KEYS_CMD[i]) - - theme = pref.themesList.currentItem().text() - - sounds_list = {'sounds':sounds, 'hotkeys':hotkeys, 'Theme':theme, 'KEYS_CMD':KEYS_JSON} - jsonwrite(config, sounds_list) - -###! CONTROL !### -def select_move(mode): - ''' - Перемешение по оверлейному меню - mode - кортеж из двух цифр - (смещение по категории, смещение по списку) - ''' - select[0] += mode[0] # Категории - select[1] += mode[1] # Файлы в категории - if select[0] > len(menu)-1 or select[0] < -len(menu)+1: - select[0] = 0 - if select[1] > len(menu[select[0]])-1 or select[1] < -len(menu[select[0]])+1: - select[1] = 0 - if mode[0] != 0: - select[1] = 0 - over.label.setText(menu[select[0]][select[1]]) - win.select_label.setText(menu[select[0]][select[1]]) - -def key_check(key): - key = str(key).replace("'",'') # Преобразование кода в строку - key_n = '' # Переведенное значение клавиши - try: - key_n = keys.dict_[key] - except KeyError: - pass - if key in hotkeys.keys(): - play_sound(hotkeys[key]) - elif key in KEYS_CMD.values(): - find_key(KEYS_CMD, key)() - -def main(): - key_check_Listener = Listener( - on_release=key_check) - key_check_Listener.start() - - win.exit_button.clicked.connect(win.close) - win.min_button.clicked.connect(win.showMinimized) - pref.exit_button.clicked.connect(pref.close) - pref.min_button.clicked.connect(pref.showMinimized) - hotk.exit_button.clicked.connect(hotk.close) - hotk.min_button.clicked.connect(hotk.showMinimized) - - win.hkset.clicked.connect(hotkey_remap) - win.pref_button.clicked.connect(pref.show) - win.hotkeys_button.clicked.connect(hotk.show) - win.catList.currentTextChanged.connect(cat_select) - win.stop_button.clicked.connect(lambda: sd.stop()) - win.play_button.clicked.connect(play_sound) - - pref.play_sound.clicked.connect( - lambda: pref_remap(pref.play_sound, 'play_sound')) - pref.stop_sound.clicked.connect( - lambda: pref_remap(pref.stop_sound, 'stop_sound')) - pref.select_move_up.clicked.connect( - lambda: pref_remap(pref.select_move_up, 'select_move_up')) - pref.select_move_down.clicked.connect( - lambda: pref_remap(pref.select_move_down, 'select_move_down')) - pref.select_move_left.clicked.connect( - lambda: pref_remap(pref.select_move_left, 'select_move_left')) - pref.select_move_right.clicked.connect( - lambda: pref_remap(pref.select_move_right, 'select_move_right')) - pref.themesList.currentTextChanged.connect(toggle_stylesheet) - - hotk.delete_button.clicked.connect(hotkey_delete) - - win.show() - - -if __name__ == '__main__': - ### Создание окна ### - app = QApplication([]) # Приложение - over = OverlayUi() # Окно оверлея - pref = PreferencesUi() # Окно настроек - hotk = HotkeysUi() # Окно хоткеев - win = MainUi() # Основное окно - - PREF_BTN = [ # Список кнопок настроек клавиш в pref - pref.select_move_up, - pref.select_move_down, - pref.select_move_left, - pref.select_move_right, - pref.play_sound, - pref.stop_sound,] - - ### Поиск устроства ввода ### - sd.default.device = find_device() # Установка устройства вывода по умолчанию - - ### Глобальные переменные ### - dir_ = 'sound' - config = 'settings.json' - get_files(dir_, config) # Сбор всех аудиофайлов - sound_get_dict = jsonread(config) # Загрузка данных - hotkeys = sound_get_dict['hotkeys'] # Загрузка словаря хоткеев - theme = sound_get_dict['Theme'] # Загрузка темы - menu = sound_get_dict['sounds'] # Загрузка оверлейного меню - select = [0, 0] # Установка курсора оверлея в нулевую позицию - - if theme != 'None': - toggle_stylesheet(theme) - pref.themesList.addItems(os.listdir('themes')) - - for i in sound_get_dict['sounds']: - win.catList.addItem(i[0]) - - for x in i: - x = os.path.join(i[0], x) - if x in hotkeys.values(): - hotk.hotkeyList.addItem(f'{find_key(hotkeys, x)}\t: {x}') - - - COMMAND_DICT = { # Словарь функций к строковому значению - lambda: select_move((0, -1)):'select_move_up', # вверх - lambda: select_move((0, 1)) :'select_move_down', # вниз - lambda: select_move((-1, 0)):'select_move_left', # влево - lambda: select_move((1, 0)) :'select_move_right', # вправо - lambda: play_sound(os.path.join(menu[select[0]][0], - menu[select[0]][select[1]])):'play_sound', # Играть - lambda: sd.stop() :'stop_sound', # Остановить - } - KEYS_JSON = sound_get_dict['KEYS_CMD']# Загрузка настроенных клавиш - KEYS_CMD = COMMAND_DICT.copy() # Настроенные клавиши - - ### Установка настроенных клавиш ### - for i in KEYS_CMD.keys(): - KEYS_CMD.update({i:KEYS_JSON[COMMAND_DICT[i]]}) - KEYS_JSON = None - - combo = 0 - for i in KEYS_CMD.values(): - PREF_BTN[combo].setText(keys.dict_[i]) - combo += 1 - - main() - - sys.exit(app.exec()) \ No newline at end of file