-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsample_tools_UI.py
253 lines (203 loc) · 8.33 KB
/
sample_tools_UI.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# coding:utf-8
"""
:module: sample_tools_UI.pyw
:description: Launcher Hub for Sample Tools
:author: Michel 'Mitch' Pecqueur
:date: 2024.10
"""
# import atexit
import ctypes
import importlib
import platform
import sys
from functools import partial
from pathlib import Path
import qdarkstyle
from PyQt5 import QtWidgets, QtCore, QtGui, Qt
from __init__ import __version__ # noqa
# from tools.simple_logger import SimpleLogger
if getattr(sys, 'frozen', False):
import pyi_splash # noqa
pyi_splash.close()
def resource_path(relative_path, as_str=True):
"""
Get absolute path to resource, works for dev and for PyInstaller
Modified from :
https://stackoverflow.com/questions/31836104/pyinstaller-and-onefile-how-to-include-an-image-in-the-exe-file
:param str or WindowsPath relative_path:
:param bool as_str: Return result as a string
:return:
:rtype: str or WindowsPath
"""
if hasattr(sys, '_MEIPASS'):
base_path = Path(sys._MEIPASS)
else:
base_path = Path().resolve()
result = base_path / relative_path
return (result, str(result))[bool(as_str)]
class SampleToolsUi(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setObjectName('sample_tools_ui')
self.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
self.setWindowTitle(f'Sample Tools v{__version__}')
self.running = {}
self.current_dir = Path(__file__).parent
self.tools_path = self.current_dir / 'tools'
if self.tools_path.exists():
print(f'tools sub-directory successfully found: {str(self.tools_path)}')
sys.path.append(str(self.tools_path))
# Check append
if str(self.tools_path) in sys.path:
print('tools sub-directory successfully added to sys.path')
else:
print('tools sub-directory failed to be added to sys.path')
else:
print(f'tools sub-directory not found: {str(self.tools_path)}')
self.tools = {
'SMP2ds': 'smp2ds_UI.py',
'Split Audio Tool': 'split_tool_UI.py',
'Rename Sample Tool': 'rename_tool_UI.py',
'Loop Tool': 'loop_tool_UI.py',
'Stereo Tool': 'st_tool_UI.py',
'Upsample Tool': 'upsample_tool_UI.py',
}
self.tool_modules = {}
self.import_tools()
self.icons_path = 'tools/UI/icons/'
self.icons = {
'SMP2ds': 'smp2ds_64.png',
'Split Audio Tool': 'split_tool_64.png',
'Rename Sample Tool': 'rename_tool_64.png',
'Loop Tool': 'loop_tool_64.png',
'Stereo Tool': 'st_tool_64.png',
'Upsample Tool': 'upsample_tool_64.png',
}
self.status_tips = {
'SMP2ds': 'Create Decent Sampler presets from samples',
'Split Audio Tool': 'Split and trim audio file(s) by detecting silences',
'Rename Sample Tool': "Rename audio files and update their 'smpl' chunk/metadata",
'Loop Tool': 'Detect loop points or modify audio files to make them loop',
'Stereo Tool': 'Apply pseudo-stereo/stereo imaging effect to audio file(s)',
'Upsample Tool': 'Up-sample audio file(s) using spectral band replication',
}
self.setupUi()
def import_tools(self):
for name in self.tools:
module_name = self.tools[name].split('.')[0]
try:
self.tool_modules[name] = importlib.import_module(module_name)
print(f'{module_name}: loaded')
except Exception as e:
print(f'Failed to import {module_name}: {e}')
def setupUi(self):
centralwidget = QtWidgets.QWidget(self)
centralwidget.setObjectName('centralwidget')
lyt = QtWidgets.QHBoxLayout(centralwidget)
lyt.setObjectName('tool_btn_lyt')
self.setCentralWidget(centralwidget)
status_bar = QtWidgets.QStatusBar()
self.setStatusBar(status_bar)
for tool, cmd in self.tools.items():
img_file = resource_path(Path(self.icons_path).joinpath(self.icons[tool]))
btn = IconButton(image=img_file, parent=self)
name = Path(cmd).stem.removesuffix('_UI')
btn.setObjectName(name)
btn.setToolTip(tool)
btn.setStatusTip(self.status_tips[tool])
btn.clicked.connect(partial(self.launch_tool, name=tool))
lyt.addWidget(btn)
img_file = resource_path(Path(self.icons_path).joinpath('quit_64.png'))
close_btn = IconButton(image=img_file, parent=self)
close_btn.clicked.connect(self.close)
close_btn.setToolTip('Quit')
close_btn.setStatusTip('Close all tools and quit')
lyt.addWidget(close_btn)
self.setLayout(lyt)
app_icon = QtGui.QIcon()
img_file = resource_path(Path(self.icons_path).joinpath('sample_tools_64.png'))
app_icon.addFile(img_file, QtCore.QSize(64, 64))
self.setWindowIcon(app_icon)
self.setFixedSize(576, 112)
def launch_tool(self, name):
"""
Launch given tool if it is not already running
:param str name:
:return:
"""
# QtWidgets.QMainWindow()
if name in self.running:
print(f'{name} already running')
try:
self.running[name].show()
self.running[name].showNormal()
self.running[name].raise_()
self.running[name].activateWindow()
except Exception as e:
print(e)
return
mod = self.tool_modules[name]
tool = mod.run(parent=self)
tool.destroyed.connect(partial(self.running.pop, name))
self.running[name] = tool
print(f'{name} launched')
def closeEvent(self, event):
confirm_dlg = QtWidgets.QMessageBox.question(self, 'Confirmation', 'Are you sure you want to quit?',
Qt.QMessageBox.Yes | Qt.QMessageBox.No, Qt.QMessageBox.No)
if confirm_dlg == Qt.QMessageBox.Yes:
print(f'{self.objectName()} closed')
event.accept()
else:
event.ignore()
class IconButton(QtWidgets.QPushButton):
def __init__(self, parent=None, size=64, image=''):
super().__init__()
self.setParent(parent)
self.setGeometry(0, 0, size, size)
icon = QtGui.QIcon()
icon.addFile(image)
self.setIcon(icon)
self.setIconSize(QtCore.QSize(size, size))
size_policy = Qt.QSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
self.setSizePolicy(size_policy)
self.setFlat(True)
# def global_exception_handler(exctype, value, tb):
# logger.log_exception(value)
# logger = SimpleLogger('sample_tools_log.txt')
# sys.excepthook = global_exception_handler
# atexit.register(lambda: logger.logger.info("Application is shutting down."))
if __name__ == '__main__':
app_id = f'mitch.sampleTools.{__version__}'
if platform.system() == 'Windows':
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5'))
if platform.system() == "Darwin":
macos_style = """
QComboBox{combobox-popup: 0;}
QComboBox QAbstractItemView {min-width: 64px;}
"""
app.setStyleSheet(app.styleSheet() + macos_style)
font = app.font()
font.setPointSize(11)
app.setFont(font)
current_screen = app.primaryScreen()
for screen in QtWidgets.QApplication.screens():
if screen.geometry().contains(Qt.QCursor.pos()):
current_screen = screen
break
screen_geo = current_screen.geometry()
try:
window = SampleToolsUi()
# Move window up in the screen
x = screen_geo.x() + (screen_geo.width() - window.width()) // 2
y = screen_geo.y() + int(screen_geo.height() * .1 - window.height() / 2)
window.move(x, y)
window.show()
sys.exit(app.exec_())
except Exception as e:
print(e)
# logger.log_exception(e)