audio-tuner-gui 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audio_tuner_gui/__init__.py +39 -0
- audio_tuner_gui/analysis.py +717 -0
- audio_tuner_gui/common.py +350 -0
- audio_tuner_gui/display.py +605 -0
- audio_tuner_gui/export.py +563 -0
- audio_tuner_gui/file_selector.py +435 -0
- audio_tuner_gui/icons/audio_tuner_icon.ico +0 -0
- audio_tuner_gui/icons/audio_tuner_icon.svg +215 -0
- audio_tuner_gui/icons/audio_tuner_icon_hires.svg +215 -0
- audio_tuner_gui/icons/audio_tuner_logo_dark.svg +563 -0
- audio_tuner_gui/icons/audio_tuner_logo_light.svg +563 -0
- audio_tuner_gui/icons/black-logo.svg +24 -0
- audio_tuner_gui/icons/folder.png +0 -0
- audio_tuner_gui/icons/gpl-v3-logo_red.svg +223 -0
- audio_tuner_gui/icons/gpl-v3-logo_white.svg +224 -0
- audio_tuner_gui/icons/preferences-other.png +0 -0
- audio_tuner_gui/icons/process-stop.png +0 -0
- audio_tuner_gui/icons/white-logo.svg +24 -0
- audio_tuner_gui/log_viewer.py +154 -0
- audio_tuner_gui/option_panel.py +683 -0
- audio_tuner_gui/player.py +757 -0
- audio_tuner_gui/scripts/__init__.py +0 -0
- audio_tuner_gui/scripts/tuner_gui.py +863 -0
- audio_tuner_gui-0.9.1.dist-info/METADATA +93 -0
- audio_tuner_gui-0.9.1.dist-info/RECORD +28 -0
- audio_tuner_gui-0.9.1.dist-info/WHEEL +4 -0
- audio_tuner_gui-0.9.1.dist-info/entry_points.txt +2 -0
- audio_tuner_gui-0.9.1.dist-info/licenses/COPYING +674 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
# This file is part of Audio Tuner.
|
|
4
|
+
#
|
|
5
|
+
# Copyright 2025, 2026 Jessie Blue Cassell <bluesloth600@gmail.com>
|
|
6
|
+
#
|
|
7
|
+
# This program is free software: you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU General Public License
|
|
18
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
"""File selector for the GUI."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__author__ = 'Jessie Blue Cassell'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
'FileSelector',
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import errno
|
|
34
|
+
from collections import deque
|
|
35
|
+
|
|
36
|
+
from PyQt6.QtCore import (
|
|
37
|
+
Qt,
|
|
38
|
+
QDir,
|
|
39
|
+
pyqtSignal,
|
|
40
|
+
QModelIndex,
|
|
41
|
+
)
|
|
42
|
+
from PyQt6.QtWidgets import (
|
|
43
|
+
QWidget,
|
|
44
|
+
QVBoxLayout,
|
|
45
|
+
QHBoxLayout,
|
|
46
|
+
QLineEdit,
|
|
47
|
+
QTreeView,
|
|
48
|
+
QAbstractItemView,
|
|
49
|
+
QCompleter,
|
|
50
|
+
QToolButton,
|
|
51
|
+
)
|
|
52
|
+
from PyQt6.QtGui import (
|
|
53
|
+
QIcon,
|
|
54
|
+
QFileSystemModel,
|
|
55
|
+
QKeySequence,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
import audio_tuner_gui.common as gcom
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class _PathHistory(deque):
|
|
62
|
+
def __init__(self, path):
|
|
63
|
+
super().__init__()
|
|
64
|
+
self.n = 0
|
|
65
|
+
self.append(self._norm(path))
|
|
66
|
+
|
|
67
|
+
def _norm(self, path):
|
|
68
|
+
return QDir.fromNativeSeparators(path).rstrip('/') + '/'
|
|
69
|
+
|
|
70
|
+
def cd(self, path):
|
|
71
|
+
path = self._norm(path)
|
|
72
|
+
while not self.at_end():
|
|
73
|
+
self.pop()
|
|
74
|
+
if path != self[-1]:
|
|
75
|
+
self.append(path)
|
|
76
|
+
self.n += 1
|
|
77
|
+
return path
|
|
78
|
+
|
|
79
|
+
def at_end(self):
|
|
80
|
+
return len(self) == self.n + 1
|
|
81
|
+
|
|
82
|
+
def at_beginning(self):
|
|
83
|
+
return self.n == 0
|
|
84
|
+
|
|
85
|
+
def current(self):
|
|
86
|
+
return self[self.n]
|
|
87
|
+
|
|
88
|
+
def next(self, native=False):
|
|
89
|
+
ret = self[self.n+1]
|
|
90
|
+
if native:
|
|
91
|
+
ret = QDir.toNativeSeparators(ret)
|
|
92
|
+
return ret
|
|
93
|
+
|
|
94
|
+
def previous(self, native=False):
|
|
95
|
+
ret = self[self.n-1]
|
|
96
|
+
if native:
|
|
97
|
+
ret = QDir.toNativeSeparators(ret)
|
|
98
|
+
return ret
|
|
99
|
+
|
|
100
|
+
def forward(self):
|
|
101
|
+
if not self.at_end():
|
|
102
|
+
self.n += 1
|
|
103
|
+
else:
|
|
104
|
+
raise IndexError
|
|
105
|
+
|
|
106
|
+
def back(self):
|
|
107
|
+
if not self.at_beginning():
|
|
108
|
+
self.n -= 1
|
|
109
|
+
else:
|
|
110
|
+
raise IndexError
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class _PathEdit(QLineEdit):
|
|
114
|
+
def setText(self, text):
|
|
115
|
+
if QDir(text).exists():
|
|
116
|
+
text = text.rstrip('/\\') + '/'
|
|
117
|
+
text = QDir.toNativeSeparators(text)
|
|
118
|
+
super().setText(text)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class _FileView(QTreeView):
|
|
122
|
+
EnterKeyPress = pyqtSignal()
|
|
123
|
+
|
|
124
|
+
def __init__(self):
|
|
125
|
+
super().__init__()
|
|
126
|
+
|
|
127
|
+
self.setExpandsOnDoubleClick(False)
|
|
128
|
+
self.setAllColumnsShowFocus(True)
|
|
129
|
+
self.setItemsExpandable(False)
|
|
130
|
+
self.setRootIsDecorated(False)
|
|
131
|
+
self.setSelectionMode(
|
|
132
|
+
QAbstractItemView.SelectionMode.ExtendedSelection)
|
|
133
|
+
|
|
134
|
+
def keyPressEvent(self, event):
|
|
135
|
+
if event.key() == Qt.Key.Key_Return:
|
|
136
|
+
self.EnterKeyPress.emit()
|
|
137
|
+
else:
|
|
138
|
+
super().keyPressEvent(event)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class FileSelector(QWidget):
|
|
142
|
+
"""A widget for selecting files to analyze. Inherits from QWidget.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
option_panel : audio_tuner_gui.option_panel.OptionPanel
|
|
147
|
+
The option panel widget to get analysis options from.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
SelectForAnalysis = pyqtSignal(str, gcom.Options)
|
|
151
|
+
"""Signal emitted when a file is selected.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
audio_tuner_gui.common.Options
|
|
156
|
+
The options to use when the file is analyzed.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
UpdateStatusbar = pyqtSignal(str)
|
|
160
|
+
"""Signal emitted to request a string be displayed on the status bar.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
str
|
|
165
|
+
The string.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def __init__(self, option_panel):
|
|
170
|
+
super().__init__()
|
|
171
|
+
|
|
172
|
+
self.option_panel = option_panel
|
|
173
|
+
|
|
174
|
+
vbox = QVBoxLayout(self)
|
|
175
|
+
vbox.setSpacing(5)
|
|
176
|
+
vbox.setContentsMargins(3, 3, 3, 3)
|
|
177
|
+
|
|
178
|
+
self.path_history = _PathHistory(QDir.currentPath())
|
|
179
|
+
|
|
180
|
+
self._init_file_view()
|
|
181
|
+
self._init_control_panel()
|
|
182
|
+
self._cd(self.path_history.current())
|
|
183
|
+
|
|
184
|
+
vbox.addWidget(self.panel)
|
|
185
|
+
vbox.addWidget(self.view)
|
|
186
|
+
|
|
187
|
+
def _init_file_view(self):
|
|
188
|
+
model = QFileSystemModel(self)
|
|
189
|
+
|
|
190
|
+
self.original_filter = model.filter()
|
|
191
|
+
self.showhidden_filter = self.original_filter | QDir.Filter.Hidden
|
|
192
|
+
|
|
193
|
+
view = _FileView()
|
|
194
|
+
view.setModel(model)
|
|
195
|
+
view.setColumnWidth(0, 250)
|
|
196
|
+
view.header().moveSection(2, 1)
|
|
197
|
+
view.setSortingEnabled(True)
|
|
198
|
+
view.sortByColumn(0, Qt.SortOrder.AscendingOrder)
|
|
199
|
+
|
|
200
|
+
view.doubleClicked.connect(self._file_selector_doubleclick)
|
|
201
|
+
view.EnterKeyPress.connect(self._file_selector_enter)
|
|
202
|
+
|
|
203
|
+
self.model = model
|
|
204
|
+
self.view = view
|
|
205
|
+
|
|
206
|
+
def _set_show_hidden(self, checked):
|
|
207
|
+
if checked:
|
|
208
|
+
self.model.setFilter(self.showhidden_filter)
|
|
209
|
+
else:
|
|
210
|
+
self.model.setFilter(self.original_filter)
|
|
211
|
+
|
|
212
|
+
def _init_control_panel(self):
|
|
213
|
+
panel = QWidget()
|
|
214
|
+
hbox = QHBoxLayout(panel)
|
|
215
|
+
hbox.setContentsMargins(0, 0, 0, 0)
|
|
216
|
+
|
|
217
|
+
back_act = gcom.SplitAction('&Back', self)
|
|
218
|
+
back_act.setIcon(QIcon.fromTheme(gcom.ICON_BACK))
|
|
219
|
+
back_act.setShortcut(QKeySequence('Alt+Left'))
|
|
220
|
+
back_act.triggered_connect(self._go_back)
|
|
221
|
+
|
|
222
|
+
back_button = QToolButton()
|
|
223
|
+
back_button.setDefaultAction(back_act.button())
|
|
224
|
+
hbox.addWidget(back_button)
|
|
225
|
+
|
|
226
|
+
forward_act = gcom.SplitAction('&Forward', self)
|
|
227
|
+
forward_act.setIcon(QIcon.fromTheme(gcom.ICON_FORWARD))
|
|
228
|
+
forward_act.setShortcut(QKeySequence('Alt+Right'))
|
|
229
|
+
forward_act.triggered_connect(self._go_forward)
|
|
230
|
+
|
|
231
|
+
forward_button = QToolButton()
|
|
232
|
+
forward_button.setDefaultAction(forward_act.button())
|
|
233
|
+
hbox.addWidget(forward_button)
|
|
234
|
+
|
|
235
|
+
up_act = gcom.SplitAction('&Up', self)
|
|
236
|
+
up_act.setIcon(QIcon.fromTheme(gcom.ICON_UP))
|
|
237
|
+
up_act.setShortcut(QKeySequence('Alt+Up'))
|
|
238
|
+
up_act.triggered_connect(self._updir)
|
|
239
|
+
|
|
240
|
+
up_button = QToolButton()
|
|
241
|
+
up_button.setDefaultAction(up_act.button())
|
|
242
|
+
hbox.addWidget(up_button)
|
|
243
|
+
|
|
244
|
+
home_act = gcom.SplitAction('&Home', self)
|
|
245
|
+
home_act.setIcon(QIcon.fromTheme(gcom.ICON_HOME))
|
|
246
|
+
home_act.setShortcut(QKeySequence('Alt+Home'))
|
|
247
|
+
home_act.setStatusTip('Go to home directory')
|
|
248
|
+
home_act.triggered_connect(self._go_home)
|
|
249
|
+
|
|
250
|
+
home_button = QToolButton()
|
|
251
|
+
home_button.setDefaultAction(home_act.button())
|
|
252
|
+
hbox.addWidget(home_button)
|
|
253
|
+
|
|
254
|
+
completer = QCompleter(self.model, self)
|
|
255
|
+
|
|
256
|
+
pathedit = _PathEdit()
|
|
257
|
+
pathedit.setCompleter(completer)
|
|
258
|
+
hbox.addWidget(pathedit)
|
|
259
|
+
pathedit.returnPressed.connect(self._pathedit_enter,
|
|
260
|
+
Qt.ConnectionType.QueuedConnection)
|
|
261
|
+
|
|
262
|
+
self.pathedit = pathedit
|
|
263
|
+
self.panel = panel
|
|
264
|
+
self.back_button = back_button
|
|
265
|
+
self.forward_button = forward_button
|
|
266
|
+
self.up_button = up_button
|
|
267
|
+
self.back_act = back_act
|
|
268
|
+
self.forward_act = forward_act
|
|
269
|
+
self.up_act = up_act
|
|
270
|
+
self.home_act = home_act
|
|
271
|
+
|
|
272
|
+
def _pathedit_enter(self):
|
|
273
|
+
try:
|
|
274
|
+
self._cd(QDir.fromNativeSeparators(self.pathedit.text()))
|
|
275
|
+
except NotADirectoryError:
|
|
276
|
+
path = self.pathedit.text()
|
|
277
|
+
options = self.option_panel.get_options()
|
|
278
|
+
if options is None:
|
|
279
|
+
self.option_panel.ensure_visible()
|
|
280
|
+
else:
|
|
281
|
+
self.SelectForAnalysis.emit(path, options)
|
|
282
|
+
self._select_filename()
|
|
283
|
+
|
|
284
|
+
def handle_command_line_arg(self, arg):
|
|
285
|
+
"""If `arg` is a file, act like it's been selected and send it
|
|
286
|
+
out for analysis. If `arg` is a directory, change to that
|
|
287
|
+
directory.
|
|
288
|
+
|
|
289
|
+
Note that this does the same thing as the `handle_drop` method.
|
|
290
|
+
They're separate methods to leave open the possibility of
|
|
291
|
+
different behavior for command line arguments and drops in the
|
|
292
|
+
future.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
arg : str
|
|
297
|
+
File or directory path.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
self._cd(QDir.fromNativeSeparators(arg))
|
|
302
|
+
except NotADirectoryError:
|
|
303
|
+
options = self.option_panel.get_options()
|
|
304
|
+
if options is None:
|
|
305
|
+
self.option_panel.ensure_visible()
|
|
306
|
+
else:
|
|
307
|
+
self.SelectForAnalysis.emit(arg, options)
|
|
308
|
+
|
|
309
|
+
def handle_drop(self, path):
|
|
310
|
+
"""If `path` is a file, act like it's been selected and send it
|
|
311
|
+
out for analysis. If `path` is a directory, change to that
|
|
312
|
+
directory.
|
|
313
|
+
|
|
314
|
+
Note that this does the same thing as the
|
|
315
|
+
`handle_command_line_arg` method. They're separate methods to
|
|
316
|
+
leave open the possibility of different behavior for command
|
|
317
|
+
line arguments and drops in the future.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
path : str
|
|
322
|
+
File or directory path.
|
|
323
|
+
"""
|
|
324
|
+
self.handle_command_line_arg(path)
|
|
325
|
+
|
|
326
|
+
def _select_filename(self):
|
|
327
|
+
path = self.pathedit.text()
|
|
328
|
+
if not QDir(path).exists():
|
|
329
|
+
length = len(os.path.basename(path))
|
|
330
|
+
pos = len(os.path.dirname(path)) + 1
|
|
331
|
+
self.pathedit.setSelection(pos, length)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _cd(self, dest=None, hist='cd'):
|
|
335
|
+
if hist == 'previous':
|
|
336
|
+
dest = self.path_history.previous()
|
|
337
|
+
elif hist == 'next':
|
|
338
|
+
dest = self.path_history.next()
|
|
339
|
+
if isinstance(dest, QModelIndex):
|
|
340
|
+
path = self.model.filePath(dest)
|
|
341
|
+
index = dest
|
|
342
|
+
elif isinstance(dest, str):
|
|
343
|
+
if not QDir(dest).exists():
|
|
344
|
+
raise NotADirectoryError(errno.ENOTDIR,
|
|
345
|
+
os.strerror(errno.ENOTDIR),
|
|
346
|
+
dest)
|
|
347
|
+
path = dest
|
|
348
|
+
index = self.model.index(dest)
|
|
349
|
+
self.model.setRootPath(path)
|
|
350
|
+
self.view.setRootIndex(index)
|
|
351
|
+
self.pathedit.setText(QDir(path).absolutePath())
|
|
352
|
+
if hist == 'previous':
|
|
353
|
+
self.path_history.back()
|
|
354
|
+
elif hist == 'next':
|
|
355
|
+
self.path_history.forward()
|
|
356
|
+
elif hist == 'cd':
|
|
357
|
+
self.path_history.cd(path)
|
|
358
|
+
if self.path_history.at_beginning():
|
|
359
|
+
self.back_act.setEnabled(False)
|
|
360
|
+
self.back_button.setAutoRaise(True)
|
|
361
|
+
status = 'No previous directory'
|
|
362
|
+
self.back_act.setStatusTip(status)
|
|
363
|
+
if self.back_button.underMouse():
|
|
364
|
+
self.UpdateStatusbar.emit(status)
|
|
365
|
+
else:
|
|
366
|
+
self.back_act.setEnabled(True)
|
|
367
|
+
self.back_button.setAutoRaise(False)
|
|
368
|
+
status = ('Go to previous directory: '
|
|
369
|
+
+ self.path_history.previous(native=True))
|
|
370
|
+
self.back_act.setStatusTip(status)
|
|
371
|
+
if self.back_button.underMouse():
|
|
372
|
+
self.UpdateStatusbar.emit(status)
|
|
373
|
+
if self.path_history.at_end():
|
|
374
|
+
self.forward_act.setEnabled(False)
|
|
375
|
+
self.forward_button.setAutoRaise(True)
|
|
376
|
+
status = 'No next directory'
|
|
377
|
+
self.forward_act.setStatusTip(status)
|
|
378
|
+
if self.forward_button.underMouse():
|
|
379
|
+
self.UpdateStatusbar.emit(status)
|
|
380
|
+
else:
|
|
381
|
+
self.forward_act.setEnabled(True)
|
|
382
|
+
self.forward_button.setAutoRaise(False)
|
|
383
|
+
status = ('Go to next directory: '
|
|
384
|
+
+ self.path_history.next(native=True))
|
|
385
|
+
self.forward_act.setStatusTip(status)
|
|
386
|
+
if self.forward_button.underMouse():
|
|
387
|
+
self.UpdateStatusbar.emit(status)
|
|
388
|
+
if QDir(path).isRoot():
|
|
389
|
+
self.up_act.setEnabled(False)
|
|
390
|
+
self.up_button.setAutoRaise(True)
|
|
391
|
+
status = 'No parent directory'
|
|
392
|
+
self.up_act.setStatusTip(status)
|
|
393
|
+
if self.up_button.underMouse():
|
|
394
|
+
self.UpdateStatusbar.emit(status)
|
|
395
|
+
else:
|
|
396
|
+
self.up_act.setEnabled(True)
|
|
397
|
+
self.up_button.setAutoRaise(False)
|
|
398
|
+
status = 'Go to parent directory'
|
|
399
|
+
self.up_act.setStatusTip(status)
|
|
400
|
+
if self.up_button.underMouse():
|
|
401
|
+
self.UpdateStatusbar.emit(status)
|
|
402
|
+
|
|
403
|
+
def _file_selector_doubleclick(self, index):
|
|
404
|
+
if self.model.isDir(index):
|
|
405
|
+
self._cd(index)
|
|
406
|
+
else:
|
|
407
|
+
self._file_selector_enter()
|
|
408
|
+
|
|
409
|
+
def _file_selector_enter(self):
|
|
410
|
+
selected = [x for x in self.view.selectedIndexes() if x.column() == 0]
|
|
411
|
+
if len(selected) == 1 and self.model.isDir(selected[0]):
|
|
412
|
+
self._cd(selected[0])
|
|
413
|
+
else:
|
|
414
|
+
options = self.option_panel.get_options()
|
|
415
|
+
if options is None:
|
|
416
|
+
self.option_panel.ensure_visible()
|
|
417
|
+
else:
|
|
418
|
+
for index in selected:
|
|
419
|
+
self.SelectForAnalysis.emit(self.model.filePath(index),
|
|
420
|
+
options)
|
|
421
|
+
|
|
422
|
+
def _updir(self):
|
|
423
|
+
directory = self.model.rootDirectory()
|
|
424
|
+
if directory.cdUp():
|
|
425
|
+
newpath = directory.absolutePath()
|
|
426
|
+
self._cd(newpath)
|
|
427
|
+
|
|
428
|
+
def _go_home(self):
|
|
429
|
+
self._cd(QDir.homePath())
|
|
430
|
+
|
|
431
|
+
def _go_back(self):
|
|
432
|
+
self._cd(hist='previous')
|
|
433
|
+
|
|
434
|
+
def _go_forward(self):
|
|
435
|
+
self._cd(hist='next')
|
|
Binary file
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="64"
|
|
6
|
+
height="64"
|
|
7
|
+
viewBox="0 0 16.933334 16.933334"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg5"
|
|
10
|
+
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
|
11
|
+
sodipodi:docname="audio_tuner_icon.svg"
|
|
12
|
+
inkscape:export-filename="audio_tuner_icon_32.png"
|
|
13
|
+
inkscape:export-xdpi="48"
|
|
14
|
+
inkscape:export-ydpi="48"
|
|
15
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
16
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
17
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
18
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
19
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
20
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
|
21
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
22
|
+
<sodipodi:namedview
|
|
23
|
+
id="namedview7"
|
|
24
|
+
pagecolor="#000000"
|
|
25
|
+
bordercolor="#000000"
|
|
26
|
+
borderopacity="0.25"
|
|
27
|
+
inkscape:showpageshadow="2"
|
|
28
|
+
inkscape:pageopacity="0"
|
|
29
|
+
inkscape:pagecheckerboard="0"
|
|
30
|
+
inkscape:deskcolor="#d1d1d1"
|
|
31
|
+
inkscape:document-units="mm"
|
|
32
|
+
showgrid="true"
|
|
33
|
+
inkscape:zoom="12.500449"
|
|
34
|
+
inkscape:cx="30.038921"
|
|
35
|
+
inkscape:cy="32.598829"
|
|
36
|
+
inkscape:window-width="1447"
|
|
37
|
+
inkscape:window-height="1080"
|
|
38
|
+
inkscape:window-x="0"
|
|
39
|
+
inkscape:window-y="0"
|
|
40
|
+
inkscape:window-maximized="0"
|
|
41
|
+
inkscape:current-layer="layer1">
|
|
42
|
+
<inkscape:grid
|
|
43
|
+
type="xygrid"
|
|
44
|
+
id="grid9"
|
|
45
|
+
dotted="true"
|
|
46
|
+
originx="0"
|
|
47
|
+
originy="0"
|
|
48
|
+
spacingx="0.52916669"
|
|
49
|
+
spacingy="0.52916669"
|
|
50
|
+
empspacing="2" />
|
|
51
|
+
</sodipodi:namedview>
|
|
52
|
+
<defs
|
|
53
|
+
id="defs2" />
|
|
54
|
+
<g
|
|
55
|
+
inkscape:label="Layer 1"
|
|
56
|
+
inkscape:groupmode="layer"
|
|
57
|
+
id="layer1">
|
|
58
|
+
<rect
|
|
59
|
+
style="fill:#000000;stroke-width:0.264583"
|
|
60
|
+
id="rect649"
|
|
61
|
+
width="16.933332"
|
|
62
|
+
height="16.933332"
|
|
63
|
+
x="0"
|
|
64
|
+
y="0"
|
|
65
|
+
rx="2.6458318"
|
|
66
|
+
ry="2.6458335" />
|
|
67
|
+
<path
|
|
68
|
+
style="fill:#00ff00;stroke-width:0.264583"
|
|
69
|
+
d="M 4.7625002,1.5875001 H 6.3500003 L 5.8208336,2.6458333 V 3.1750002 L 6.3500003,4.2333335 H 4.7625002 L 5.2916669,3.1750001 V 2.6458335 Z"
|
|
70
|
+
id="path441"
|
|
71
|
+
sodipodi:nodetypes="ccccccccc" />
|
|
72
|
+
<rect
|
|
73
|
+
style="fill:#666666;stroke-width:0.302621"
|
|
74
|
+
id="rect197"
|
|
75
|
+
width="14.816668"
|
|
76
|
+
height="0.52916676"
|
|
77
|
+
x="1.0583334"
|
|
78
|
+
y="2.6458335" />
|
|
79
|
+
<path
|
|
80
|
+
style="fill:#00ff00;stroke-width:0.264583"
|
|
81
|
+
d="m 11.112501,8.9958339 h 1.587501 l -0.529167,1.0583331 v 0.529167 l 0.529167,1.058334 h -1.587501 l 0.529167,-1.058334 v -0.529167 z"
|
|
82
|
+
id="path541"
|
|
83
|
+
sodipodi:nodetypes="ccccccccc" />
|
|
84
|
+
<path
|
|
85
|
+
style="fill:#00ff00;stroke-width:0.264583"
|
|
86
|
+
d="m 10.054167,5.291667 h 1.587501 l -0.529167,1.0583332 v 0.5291669 l 0.529167,1.0583333 H 10.054167 L 10.583334,6.879167 V 6.3500004 Z"
|
|
87
|
+
id="path539"
|
|
88
|
+
sodipodi:nodetypes="ccccccccc" />
|
|
89
|
+
<rect
|
|
90
|
+
style="fill:#666666;stroke-width:0.302621"
|
|
91
|
+
id="rect253"
|
|
92
|
+
width="14.816668"
|
|
93
|
+
height="0.5291667"
|
|
94
|
+
x="1.0583334"
|
|
95
|
+
y="6.3500004" />
|
|
96
|
+
<rect
|
|
97
|
+
style="fill:#666666;stroke-width:0.302621"
|
|
98
|
+
id="rect309"
|
|
99
|
+
width="14.816668"
|
|
100
|
+
height="0.52916718"
|
|
101
|
+
x="1.0583334"
|
|
102
|
+
y="10.054168" />
|
|
103
|
+
<path
|
|
104
|
+
style="fill:#00ff00;stroke-width:0.264583"
|
|
105
|
+
d="m 11.641668,12.700002 h 1.587501 l -0.529167,1.058334 v 0.529167 l 0.529167,1.058334 h -1.587501 l 0.529167,-1.058334 v -0.529167 z"
|
|
106
|
+
id="path543"
|
|
107
|
+
sodipodi:nodetypes="ccccccccc" />
|
|
108
|
+
<rect
|
|
109
|
+
style="fill:#666666;stroke-width:0.302621"
|
|
110
|
+
id="rect311"
|
|
111
|
+
width="14.816668"
|
|
112
|
+
height="0.5291667"
|
|
113
|
+
x="1.0583334"
|
|
114
|
+
y="13.758335" />
|
|
115
|
+
<rect
|
|
116
|
+
style="fill:#ffffff;stroke-width:0.264583"
|
|
117
|
+
id="rect365"
|
|
118
|
+
width="0.52916658"
|
|
119
|
+
height="1.5875001"
|
|
120
|
+
x="7.9375005"
|
|
121
|
+
y="2.1166668" />
|
|
122
|
+
<rect
|
|
123
|
+
style="fill:#ffffff;stroke-width:0.264583"
|
|
124
|
+
id="rect367"
|
|
125
|
+
width="0.52916658"
|
|
126
|
+
height="1.5875"
|
|
127
|
+
x="7.9375005"
|
|
128
|
+
y="5.8208337" />
|
|
129
|
+
<rect
|
|
130
|
+
style="fill:#ffffff;stroke-width:0.264583"
|
|
131
|
+
id="rect369"
|
|
132
|
+
width="0.52916658"
|
|
133
|
+
height="1.5875"
|
|
134
|
+
x="7.9375005"
|
|
135
|
+
y="9.5250006" />
|
|
136
|
+
<rect
|
|
137
|
+
style="fill:#ffffff;stroke-width:0.264583"
|
|
138
|
+
id="rect371"
|
|
139
|
+
width="0.52916658"
|
|
140
|
+
height="1.5875006"
|
|
141
|
+
x="7.9375005"
|
|
142
|
+
y="13.229167" />
|
|
143
|
+
<rect
|
|
144
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
145
|
+
id="rect581"
|
|
146
|
+
width="0.52916658"
|
|
147
|
+
height="1.0583334"
|
|
148
|
+
x="14.287497"
|
|
149
|
+
y="2.1166668" />
|
|
150
|
+
<rect
|
|
151
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
152
|
+
id="rect583"
|
|
153
|
+
width="0.52916658"
|
|
154
|
+
height="1.0583334"
|
|
155
|
+
x="14.287497"
|
|
156
|
+
y="5.8208337" />
|
|
157
|
+
<rect
|
|
158
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
159
|
+
id="rect585"
|
|
160
|
+
width="0.52916658"
|
|
161
|
+
height="1.0583334"
|
|
162
|
+
x="14.287497"
|
|
163
|
+
y="9.5249996" />
|
|
164
|
+
<rect
|
|
165
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
166
|
+
id="rect587"
|
|
167
|
+
width="0.52916658"
|
|
168
|
+
height="1.0583334"
|
|
169
|
+
x="14.287497"
|
|
170
|
+
y="13.229164" />
|
|
171
|
+
<rect
|
|
172
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
173
|
+
id="rect589"
|
|
174
|
+
width="0.52916658"
|
|
175
|
+
height="1.0583334"
|
|
176
|
+
x="2.1166677"
|
|
177
|
+
y="13.229164" />
|
|
178
|
+
<rect
|
|
179
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
180
|
+
id="rect591"
|
|
181
|
+
width="0.52916658"
|
|
182
|
+
height="1.0583334"
|
|
183
|
+
x="2.1166677"
|
|
184
|
+
y="9.5250006" />
|
|
185
|
+
<rect
|
|
186
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
187
|
+
id="rect593"
|
|
188
|
+
width="0.52916658"
|
|
189
|
+
height="1.0583334"
|
|
190
|
+
x="2.1166677"
|
|
191
|
+
y="5.8208346" />
|
|
192
|
+
<rect
|
|
193
|
+
style="fill:#666666;stroke-width:0.216031"
|
|
194
|
+
id="rect595"
|
|
195
|
+
width="0.52916658"
|
|
196
|
+
height="1.0583334"
|
|
197
|
+
x="2.1166677"
|
|
198
|
+
y="2.1166677" />
|
|
199
|
+
</g>
|
|
200
|
+
<metadata
|
|
201
|
+
id="metadata11">
|
|
202
|
+
<rdf:RDF>
|
|
203
|
+
<cc:Work
|
|
204
|
+
rdf:about="">
|
|
205
|
+
<cc:license
|
|
206
|
+
rdf:resource="https://gnu.org/licenses/gpl.html" />
|
|
207
|
+
<dc:creator>
|
|
208
|
+
<cc:Agent>
|
|
209
|
+
<dc:title>Jessie Blue Cassell</dc:title>
|
|
210
|
+
</cc:Agent>
|
|
211
|
+
</dc:creator>
|
|
212
|
+
</cc:Work>
|
|
213
|
+
</rdf:RDF>
|
|
214
|
+
</metadata>
|
|
215
|
+
</svg>
|