Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Converted config to yaml; packaging clean up; nearing beta release |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk | master |
| Files: | files | file ages | folders |
| SHA3-256: |
93665ca85c31b3859bef5b63551f3d81 |
| User & Date: | jmcclure 2017-08-26 23:51:00 |
Context
|
2017-08-27
| ||
| 00:48 | Post packaging-test patch-up check-in: 401afa1988 user: jmcclure tags: trunk, master | |
|
2017-08-26
| ||
| 23:51 | Converted config to yaml; packaging clean up; nearing beta release check-in: 93665ca85c user: jmcclure tags: trunk, master | |
|
2017-08-18
| ||
| 10:50 | mainwin.py: handle divide-by-zero if/when fex line is zero length spectogram.py: initial eraser functionality added check-in: 5db78221ea user: jmcclure tags: trunk, master | |
Changes
Added LICENSE.txt.
> > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Copyright 2017 Jesse McClure Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Changes to MANIFEST.in.
|
| < | | 1 | include fexqt/*.yaml |
Deleted README.md.
|
| < < < < |
Added README.rst.
> > > > | 1 2 3 4 | Fex === Coming soon ... Development being moved here from github |
Deleted bin/fex.
|
| < < < < < < < < < < < < < < < < < < |
Changes to fexqt/__init__.py.
1 |
| | < < | < < < < < < | < | > > > > > > > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import os, sys, time
from PyQt5.QtWidgets import QApplication
from fexqt.mainwin import MainWin
def main():
app = QApplication(sys.argv)
args = sys.argv[1:]
files = [arg for arg in args if os.path.isfile(arg)]
flags = [arg for arg in args if arg[0] == '-']
other = [arg for arg in args if arg not in files + flags]
print(' INFO: Fex starting on %s' %(time.asctime(time.localtime())))
# TODO create logger
if flags:
print(' WARN: ignoring unrecognized flags: ', flags)
if other:
print(' WARN: ignoring unrecognized arguments: ', other)
win = MainWin(files)
return app.exec_()
|
Added fexqt/actions.yaml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
actions:
- name: quit
menu: '&Quit'
icon: *icon-quit
shortcut: Ctrl+Q
statustip: Quit Fex
- name: mode_normal
menu: '&Normal'
icon: *icon-quit # FIXME
shortcut: Ctrl+X
statustip: Set Normal Mode
- name: mode_cursor
menu: 'Cursor'
icon: *icon-cursor
shortcut: Ctrl+C
statustip: Set Cursor Mode
- name: mode_eraser
menu: 'Eraser'
icon: *icon-eraser
shortcut: Ctrl+E
statustip: Set Eraser Mode
- name: prev_song
menu: 'Previous Song'
icon: *icon-back
shortcut: Ctrl+H
statustip: Go back to the previous song
- name: next_song
menu: 'Next Song'
icon: *icon-forward
shortcut: Ctrl+L
statustip: Move on to the next song
- name: add_songs
menu: 'Add Songs'
icon: *icon-fileadd
shortcut: Ctrl+O
statustip: Add songs to the queue
- name: fit
menu: 'Fit'
icon: *icon-shape
shortcut: Ctrl+R
statustip: Fit to original aspect ratio
|
Changes to fexqt/config.py.
1 |
| | | < | < < | | | | | < > | | | | > > | > > < < < < | | | | | | | | | | | > > | | | | | | | | > < | | | | | | | | | | | > > < < | | | | | | | | | > > > > > | < < | 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 |
import os, site, tempfile, yaml
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPalette, QColor
def configure():
_files = []
for fname in [ 'icons.yaml', 'actions.yaml', 'menu.yaml', 'config.yaml' ]:
_files += [os.path.join(p, fname) for p in site.getsitepackages()]
if 'APPDATA' in os.environ:
_files += os.path.join(os.environ['APPDATA'], 'fex', fname)
elif 'XDG_CONFIG_HOME' in os.environ:
_files += [os.path.join(os.environ['XDG_CONFIG_HOME'], 'fex', fname)]
elif 'HOME' in os.environ:
_files += [os.path.join(os.environ['HOME'], '.config', 'fex', fname)]
_files += [os.path.join(os.getcwd(), 'fexqt', fname)] # FIXME DELME
_files += [os.path.join(os.getcwd(), fname)]
_yaml = """
constants:
Window: &Window "%(Window)s"
Background: &Background "%(Background)s"
WindowText: &WindowText "%(WindowText)s"
Foreground: &Foreground "%(Foreground)s"
Base: &Base "%(Base)s"
AlternateBase: &AlternateBase "%(AlternateBase)s"
ToolTipBase: &ToolTipBase "%(ToolTipBase)s"
ToolTipText: &ToolTipText "%(ToolTipText)s"
Text: &Text "%(Text)s"
Button: &Button "%(Button)s"
ButtonText: &ButtonText "%(ButtonText)s"
BrightText: &BrightText "%(BrightText)s"
Highlight: &Highlight "%(Highlight)s"
IconOnly: &IconOnly %(IconOnly)d
TextOnly: &TextOnly %(TextOnly)d
TextBesideIcon: &TextBesideIcon %(TextBesideIcon)d
TextUnderIcon: &TextUnderIcon %(TextUnderIcon)d
FollowStyle: &FollowStyle %(FollowStyle)d
TempFile: &TempFile "%(TempFile)s"
""" % {
'Window': QColor(QPalette.Window).name(),
'Background': QColor(QPalette.Background).name(),
'WindowText': QColor(QPalette.WindowText).name(),
'Foreground': QColor(QPalette.Foreground).name(),
'Base': QColor(QPalette.Base).name(),
'AlternateBase': QColor(QPalette.AlternateBase).name(),
'ToolTipBase': QColor(QPalette.ToolTipBase).name(),
'ToolTipText': QColor(QPalette.ToolTipText).name(),
'Text': QColor(QPalette.Text).name(),
'Button': QColor(QPalette.Button).name(),
'ButtonText': QColor(QPalette.ButtonText).name(),
'BrightText': QColor(QPalette.BrightText).name(),
'Highlight': QColor(QPalette.Highlight).name(),
'IconOnly': Qt.ToolButtonIconOnly,
'TextOnly': Qt.ToolButtonTextOnly,
'TextBesideIcon': Qt.ToolButtonTextBesideIcon,
'TextUnderIcon': Qt.ToolButtonTextUnderIcon,
'FollowStyle': Qt.ToolButtonFollowStyle,
'TempFile': os.path.join(tempfile.gettempdir(), 'fex.log'),
}
for fname in _files:
if (os.path.isfile(fname)):
with open(fname, 'r') as fin:
print(' INFO: reading config from %s' % fname)
_yaml += fin.read()
return yaml.load(_yaml)
|
Added fexqt/config.yaml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
spectrogram:
fft:
window: 256
overlap: 192
window-function: hamming
# boxcar triang blackman hamming hann bartlett flattop parzen bohman
# blackmanharris nuttall barthann
calculations:
high-pass: 1250
low-pass: 10000
threshold: 14
log10-freq: false
colors:
invert: false
border: paleblue
line: deepskyblue
eraser: yellow
cursor: red
scale-ratio: 8
main-window:
maximized: false
menu: *main-menu
toolbars:
- name: modes
items: [ quit, bar, mode_normal, mode_cursor, mode_eraser ]
position: top
visible: true
style: *TextBesideIcon
- name: songs
items: [ prev_song, next_song, add_songs ]
position: top
visible: true
style: *TextBesideIcon
- name: zoom
items: [ fit ]
position: top
visible: true
style: *TextBesideIcon
statusbar:
visible: true
|
Deleted fexqt/default.cfg.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added fexqt/icons.yaml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
icons:
back: &icon-back !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOjIxQTY2NUIxNEJGNjExRTI5MDlCODA3QTA5NTQzODRGIiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOjIxQTY2NUIyNEJGNjExRTI5MDlCODA3QTA5NTQzODRGIj4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjFBNjY1QUY0QkY2MTFFMjkwOUI4
MDdBMDk1NDM4NEYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MjFBNjY1QjA0QkY2MTFFMjkw
OUI4MDdBMDk1NDM4NEYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz4TI7b/AAABF0lEQVR42uza0QrCMAyF4VX73EqevOKFMGTO
ZiVpmnMCu9X9HxvOrqW1tiHPbQMfAhCAAAQgAAEIgDt19ANKKVMDRp9keQtM+t5HGIH3JTRyXIxv
Yc7fGeATDwmwj4cD+I6HAjiKhwH4FQ8BcBafHuBffGqAnvi0AL3xKQE08ekAtPHmhydAuHg3gKjx
LgCR480BosebAqwQbwawSrwJwErxGgDNmuA95aqo8hYQ2FtgNQTrn0GBBlgBwetRWKABIiN4/x0W
dIArCCmXxAQdQIOQellc0AF6ECBejQk6wBkC1OtxQQc4QoDcIiPoAHuEEAB1859npBWxMrrTcvWd
onX2CcwebpUlAAEIQAACEIAABECdlwADAAxIS0j1HP7kAAAAAElFTkSuQmCC
eraser: &icon-eraser !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOkVDNjQyQjY4NEJGNjExRTI5QTI3REVCRkIzNUMxNUY1IiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOkVDNjQyQjY5NEJGNjExRTI5QTI3REVCRkIzNUMxNUY1Ij4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RUM2NDJCNjY0QkY2MTFFMjlBMjdE
RUJGQjM1QzE1RjUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RUM2NDJCNjc0QkY2MTFFMjlB
MjdERUJGQjM1QzE1RjUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz6n1IgWAAADA0lEQVR42uybv0scQRTHXU+EA8EqVSCtIAgB
0whp0wYSSCMcXKsIgVT5A2yFQFrhwDYQuCp/QNIkBFIdpBIEQ0QbQRAMwuY7l13ZXWfu5sebN+9y
O/AtBLmb+fD9vp03s5fleb4wz2NxYc5HC2DeASyFfkCWZUkXEFrD2gjM0FwfQB+g79Be8TeNhULE
NLahSzXdim6hIfQc6njPXziAR9CnxsJ1OocOoI3/CYCy+ZXF4pu6i8isAliDPnssPPeJiCQAHegt
dE2weOuISAGwAf2IsPCqFNi+NADL0H5h15iL/wmtS4vAVjGxPLKOoBVJNWAFesewcK3lUwN4Bh0z
LP6e5TG60CAVgFX15QwL11q+eLSO/m18+QG8gH6nsjxGr7qh4gRQNi95Sss3/5cLQE/TvMTSYJLl
uQHYNi9slucE4Nu8eFu+2s6bLM8BgKp5cbJ840jMaPloACI3L0bLa+pN38V5JACYmhet5Suj67O3
CALA2LwYLV85Mxg5fs4NtOMNgLF5IbV8IbURe+rVCzA2L1EsD32FHno1Q4zNSwzLKx2q2Dp3g8zN
SwzLa/NuBYCxeYlleWPeXQCczqjlJ+bdJQJl4budEctb5d2nCG5G2vBQWt46776PQbXlfUPY6FBa
3invtjXgoy5HRas7FGR557zbAsiLCb3WXSl5FklKy9/lneR22wCgetH4OLBIUlp+nHfS6/0pAMpL
xgPd5cKUIklt+XHeyd9vsABQ6kTdtFoWSWrLj/Me5QUPBwClakWycR44JLZ8Le9SANSKpMW9gK/l
7+VdEoBakTTMLcTy2rxLBFArkkSWN+ZdKoBakQyw/NS8SwcQIqu8xwAQ/KoswTiDXkFfpLwp+hL6
xfT936AnqRZvtBDTeYBz3llqAMN5gHfe2QFEOA+wfr6LAUB4HuD0fBcHIPA8gCTvIgA4FknSvIsB
YFkkyfMuDsCEIhkl7zEAZKHv+1Z+NKWK5HvoAtqF/nABCJo/IYBkG7mQsZR6AqlH+7O5FkALYL7H
XwEGAP5qkxJidMCUAAAAAElFTkSuQmCC
fileadd: &icon-fileadd !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOjBENDI1OERDNEJGNzExRTJCQjRCRDlGNDk0Q0Y2MERDIiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOjBENDI1OERENEJGNzExRTJCQjRCRDlGNDk0Q0Y2MERDIj4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MEQ0MjU4REE0QkY3MTFFMkJCNEJE
OUY0OTRDRjYwREMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MEQ0MjU4REI0QkY3MTFFMkJC
NEJEOUY0OTRDRjYwREMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz5B5WJeAAACoklEQVR42uxbwUrDQBBNrBSEQkEoKIInT0LF
U09eC4Ve/QBBEAT/wFO/QegHCN6EIngQPAmepEVJT2KhF0EvlYAgBCpxCiPUdZPsJtns0tmBB02T
nSQv82Z3pqkbhqFD2ZYc4mYJcKxZI20u57sJYFXzNWjNAc/Uk+CIOgFjSgQsGxABRS5F3wHrlCUw
oJ4EhQj4QJAlYJGj4EGUgEXMA+8IsgQMZKrBMXUCRhT0H1eIrGJRtEi2zssB1mw/wHE8QJ3o/Q9n
SbBHOAB6Lj59jygBO78fPCxLKcGbXwdQlMGfe64TjIB/id+jFv7sUrhHMfxdRgYmzwbfgFvEPW4/
4b5dQAmwB2giSgnZf8jbYaIM3gBHgKoEWVUc8xYX/jzrGHTjX4BTQCVD1FTQx9ec307cgLpBT72R
o3wac9GQuOzXLYNHwGbEtdUAB4ArQB9L9gl+vsJ9axFjNwDnImx1ND/5jYj+RBcQCPgI8NjUP/Dq
kkEQEfYtfMqy/iY4NpXpkMEp5zoOAdMMPqfoQ9qKlsErJ9u3Em6etTgSmqbL4IST7PyEMaIEhOir
ZqoMPgErzLnPBMbJEBCiTyNlcMHJ+IECAgLezBD3mtxlQWv8O2Z7H1BWcJ4y+hYmYBhVMOTdmGS2
2wrP1ZYhoKgSecRJwGznmgcnxXHS3e8iZgM23P0ErcsaOxtIRUBRMtBmIu8Kq5YBW+erfDvlIw0B
qmeDrYSkmHUajPMtRIBqGbCJ6UbhuVL7Vrko0roQMmE24C2FuwoI6GYNH5W1wTFzrjUTiqEiZaC6
HG7llazINER0yGDWut6OaIn5KXNLK+9pRHWJ/BKh1VqKpmhNxTxaRG3Qj+gM/ybH2S8+13icj+jj
d0cxbXFr1qxZs/avjUT97/M/AgwAfyghRmCejjgAAAAASUVORK5CYII=
forward: &icon-forward !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOjIyQzI4ODNBNEJGNjExRTJBQTkwRUQyQ0ZFMjNFMEM1IiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOjIyQzI4ODNCNEJGNjExRTJBQTkwRUQyQ0ZFMjNFMEM1Ij4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjJDMjg4Mzg0QkY2MTFFMkFBOTBF
RDJDRkUyM0UwQzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MjJDMjg4Mzk0QkY2MTFFMkFB
OTBFRDJDRkUyM0UwQzUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/VnRHAAABCklEQVR42uza2w6EIAwEULvrd5vMl7PJvm32
RqkRnZkmPGLKURQL0VpblOO2iIcBDGAAAxjAAAbQjbV6gYiYOoDqStZT4ES5bNMeoUrbM5URhHL+
JwNIIzACpBBYAboRmAG6ENgB/iIoAPxEUAH4iqAE8BFBDeANQRHgBeEwgGSCR7RNHeCJoA5QRmAA
KCGwAAwjMAEMIbABpBEYAVIIrEXR++4lpQvdfShPASi/BKH8GYTyQgjKS2Eo/wxBuSAC5YoQlEti
UC6KQrksDuWNEShvjUF5cxRdnUgB0N2JEACpTmQASHciAsCQWjH/qA7i6idF19kJ+KSoAQxgAAMY
wAAGMIAB5sRDgAEAdMaq5u7GptkAAAAASUVORK5CYII=
quit: &icon-quit !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOjM0NUVEOEJENEJGNjExRTI5NEQ3QzQ0MTk4OUYwMURFIiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOjM0NUVEOEJFNEJGNjExRTI5NEQ3QzQ0MTk4OUYwMURFIj4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzQ1RUQ4QkI0QkY2MTFFMjk0RDdD
NDQxOTg5RjAxREUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzQ1RUQ4QkM0QkY2MTFFMjk0
RDdDNDQxOTg5RjAxREUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz57l3dUAAACLElEQVR42uybfWuDQAyH6yz7coNCwY83EAZC
YZ9wUHA6dBxFvSS/JOfpCfm3d89DfUkuqfq+v5z5eruc/Dq9gCv6A1VVJQVAb2GNf0AzRDdE7chd
T2s2KgalMW3gZ/yZIb6dJNTTWv20dgMxKMH3ThJC+D6QcHcVsAJvLWEJHpagDW8lYQsekmABry2B
Ai+WYAWvJYEDL5JgCT/HQyhBAs+WYA0vlYDAhxJukAAleK4EDXiyBC94qgRNeJIET/iYBAv4qARv
+DUJlvChhA+KgM54I68SPODnaCkC3h039HBc6y9jpT4DPCUkg4+9BY4iYRWe8h2Qu4RNeOqXYK4S
ovCcXCA3CSR4bjZYZyKBDC+pB+xdAgteWhHaqwQ2PFIT3JsEETxaFd6LBDE8JGAnEiB4WEBiCTC8
loAUEv6P4tD9n/50uNwC5SFYXoPlQ6h8CpdkqKTDh4HXL4hkCK9XEssYHi+KHgB+ji92WfxA8FEJ
qeG76XgsmYQlAQ/nlLZ2XPOTejz+9Mrng3qCtYTFHoGtBomnE/xr/2/aBgnjRolYU7WFBH6LjJEE
ake5pgR5k5SyBG47vYYEvE1OSYJ0lgCRQILnZoN3gQR0kEIigQwvqQdwJGhNkXAksOClFSGKBO0R
GooENjxSE9ySYDU/tCVBBI9WhW8LEqyHp5YkiOE1zgVCCV6TY6EECF7rbHCU0CYYm2tR+DEqdPAw
98HJa+oNpL7K8PTZBfwKMAAC4rSsna895QAAAABJRU5ErkJggg==
cursor: &icon-cursor !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOkRDQkUwQ0VGNEJGNjExRTJCMkY1ODJCRDM1RkE4N0MyIiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOkRDQkUwQ0YwNEJGNjExRTJCMkY1ODJCRDM1RkE4N0MyIj4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RENCRTBDRUQ0QkY2MTFFMkIyRjU4
MkJEMzVGQTg3QzIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RENCRTBDRUU0QkY2MTFFMkIy
RjU4MkJEMzVGQTg3QzIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz5gt7lOAAAAlElEQVR42uzbCwqAIBBAwd3w/lfeLhBFH0Nx
3gEih0UxKOO6intlTNQWiwcAAAAAAFaunZzzb8/zGmSNaQIAAAAAAAAAAAAAADi8C/T6htfruWUC
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBhbcJ3ThPws2bZAwAAAAAAAAAAAAAAkBa8DY7Wp3+y2gQB
AACwdLsAAwAvSAaB2BA6qAAAAABJRU5ErkJggg==
shape: &icon-shape !!binary |
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6
eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEz
NDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJo
dHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlw
dGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAv
IiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RS
ZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpD
cmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlE
PSJ4bXAuaWlkOkJDQjRCRUM5NEJGNjExRTJCODc4ODQ2MkE5NkYyRjQ3IiB4bXBNTTpEb2N1bWVu
dElEPSJ4bXAuZGlkOkJDQjRCRUNBNEJGNjExRTJCODc4ODQ2MkE5NkYyRjQ3Ij4gPHhtcE1NOkRl
cml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkNCNEJFQzc0QkY2MTFFMkI4Nzg4
NDYyQTk2RjJGNDciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkNCNEJFQzg0QkY2MTFFMkI4
Nzg4NDYyQTk2RjJGNDciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1l
dGE+IDw/eHBhY2tldCBlbmQ9InIiPz7DJHK0AAABJElEQVR42uzbUQ6CMAyA4Y5T6Gl85eochMxL
mLkQSCAIViphhb9JjT4Y10/TzaQTEXnkbHKmH7Pp33t0mNcfN7x5yFgAgGn9VX64GT78VQCAZQ1d
7ZZvvy4AoLb8CkL/ZBxBzh2Teiu5eAAAwH777NGpOqdommA0bpVHxjPnfa0JagCS8195YBegCQKw
CtCOXrcXqHlW73CWXjrbJ+e59N+hq1dz7j/bLkAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA4IoAwxxgKfP/e8es3vGk9af5/7NNiEzqZU6QHgAAAN/C8+ygau3auTpv3V8198id
IXoAAABsPfp6vTv817O+97vDqTLu897vDreWfd773eFu/W8BBgBJzJr91bypowAAAABJRU5ErkJg
gg==
|
Changes to fexqt/mainwin.py.
1 |
| | | | | < | > | | | | > > > > > | | | > > > > > | > > | > > > > > > > > > > | | | | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
import os
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QIcon, QPalette, QPixmap
from PyQt5.QtWidgets import qApp, QAction, QFileDialog, QLabel, QMainWindow, QWidget
from fexqt.spectrogram import Spectrogram
from fexqt.config import configure
class MainWin(QMainWindow):
def __init__(self, files):
super().__init__()
self.files = files
self.config = configure()
self.setGeometry(0, 0, 800, 600)
self.setWindowTitle("Fex: Frequency Excursion Calculator")
self.setWindowIcon(QIcon('web.png')) # FIXME
if self.config['main-window']['maximized']:
self.setWindowState(Qt.WindowMaximized)
self.init_calls()
self.init_actions()
self.init_menubar()
self.init_toolbar()
self.init_statusbar()
self.init_body()
self.show()
self.spect = None
# Spectrograms are sized relative to parent window. The timer allows
# the window manager to adjust the parent window prior to this sizing.
if self.files:
QTimer.singleShot(20,self.next_spectrogram)
# TODO handle drag & drop of sound files to add to queue
# and update window title
# TODO file dialog to add files to queue
def action_unknown(self):
print(' WARN: unknown action binding')
def action_add_songs(self):
fnames, kind = QFileDialog.getOpenFileNames(self, 'Select Audio Files', os.getcwd(), "Audio (*.wav)")
if not fnames:
return
self.files += fnames
if self.spect:
self.setWindowTitle(self.basename + ' [+%d more]' % len(self.files))
else:
self.next_spectrogram()
def init_calls(self):
self.calls = {
'add_songs': self.action_add_songs,
'fit': lambda: self.spect.fit() if self.spect else None,
'mode_normal': lambda: self.spect.mode_normal() if self.spect else None,
'mode_cursor': lambda: self.spect.mode_cursor() if self.spect else None,
'mode_eraser': lambda: self.spect.mode_eraser() if self.spect else None,
'quit': qApp.quit,
'unknown': self.action_unknown,
}
def init_body(self):
self.body = QWidget()
self.body.setBackgroundRole(QPalette.AlternateBase)
self.body.setAutoFillBackground(True)
self.body.setContentsMargins(10,10,10,10)
self.setCentralWidget(self.body)
def init_actions(self):
self.actions = dict()
for act in self.config['actions']:
pix = QPixmap()
pix.loadFromData(act['icon'])
a = QAction(QIcon(pix), act['menu'], self)
a.setShortcut(act['shortcut'])
a.setStatusTip(act['statustip'])
if act['name'] in self.calls:
a.triggered.connect(self.calls[act['name']])
else:
a.triggered.connect(self.calls['unknown'])
self.actions[act['name']] = a
def init_menubar(self):
self.menubar = self.menuBar()
fileMenu = self.menubar.addMenu('&File')
fileMenu.addAction(self.actions['quit'])
fileTools = self.menubar.addMenu('&Tools')
fileTools.addAction(self.actions['mode_normal'])
fileTools.addAction(self.actions['mode_cursor'])
fileTools.addAction(self.actions['mode_eraser'])
def init_statusbar(self):
self.statusbar = self.statusBar()
self.status_text = QLabel()
self.statusBar().addPermanentWidget(self.status_text)
def init_toolbar(self):
for bar in self.config['main-window']['toolbars']:
toolbar = self.addToolBar(bar['name'])
toolbar.setVisible(bar['visible'])
toolbar.setToolButtonStyle(bar['style'])
for item in bar['items']:
if item == 'bar':
toolbar.addSeparator()
else: # if act[''] exists
toolbar.addAction(self.actions[item])
def next_spectrogram(self):
if self.spect:
pass # TODO save data and print(' DATA: ...')
if self.files:
fname = self.files.pop(0)
self.basename = os.path.basename(fname)
title = self.basename
if self.files:
title += ' [+%d more]' % len(self.files)
self.setWindowTitle(title)
self.spect = Spectrogram(fname, self.body, self.config['spectrogram'])
self.spect.fexChanged.connect(self.update_status_fex)
self.spect.mouseOver.connect(self.update_status_text)
else:
self.fname = None
self.spect = None
def update_status_fex(self, pex, tex):
self.pex = pex
self.tex = tex
try:
self.fex = pex / tex
except ZeroDivisionError:
self.fex = 0.0
def update_status_text(self, x, y):
self.status_text.setText("FEX: %.3f %0.3fs %0.1fKHz" %(self.fex,x,y))
|
Added fexqt/menu.yaml.
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
menu-items: &main-menu
- name: &file-menu "&File"
items:
- quit
- bar
- name: &file-sub-menu "Submenu"
items:
- quit
- name: &tools-menu "Tools"
items:
- mode_normal
- mode_cursor
- mode_eraser
|
Changes to fexqt/spectrogram.py.
1 2 3 4 | import wave from numpy import * from scipy.signal import spectrogram | | | | > | < | | < < < | < < < < < | < < < < < | < < < < < < < < < < < < | | | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > | < < < < < < < < < < < < < > | < | | < < < | | | | | | < | < < < < < < < < < | | | > | 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 |
import wave
from numpy import *
from scipy.signal import spectrogram
from PyQt5.QtCore import pyqtSignal, Qt, QPoint, QRect
from PyQt5.QtGui import QColor, QImage, QPainter, QPainterPath
from PyQt5.QtWidgets import QGraphicsOpacityEffect, QWidget
class Spectrogram(QWidget):
fexChanged = pyqtSignal(float, float)
mouseOver = pyqtSignal(float, float)
def __init__(self, fname, parent, config):
super().__init__(parent)
print(' INFO: processing %s' % fname)
self.conf = config
self.fname = fname
self.setMouseTracking(True)
self.setGeometry(parent.contentsRect())
self.init_fft()
if self.amp is None:
# TODO complete / load next
return
self.init_calculations()
self.init_image()
self.fit()
self.eraser = self.init_eraser(10, 200)
self.eraser.maxheight = 200
self.cursorX = self.init_cursor(1, self.height() * 4)
self.cursorY = self.init_cursor(self.width() * 4, 1)
self.show()
def init_fft(self):
fft = self.conf['fft']
try:
with wave.open(self.fname, 'rb') as song:
channels, bs, samprate, nsamp, comp, compnam = song.getparams()
data = fromstring(song.readframes(nsamp), dtype=uint16)
except wave.Error:
print(' WARN: skipping \"%s\": not a wave file.' % self.fname)
return None
f, t, amp = spectrogram(data, samprate, fft['window-function'],
fft['window'], fft['overlap'], detrend=False, mode='psd' )
self.freq = f
self.time = t
self.amp = amp
def init_calculations(self):
calc = self.conf['calculations']
bot = argmax(self.freq > calc['high-pass'])
top = argmin(self.freq < calc['low-pass'])
self.freq = self.freq[bot:top]
self.amp = self.amp[bot:top,:]
thresh = self.amp.max() / 10**(calc['threshold'] / 10.0)
thresh = 255.0 - thresh * 255.0 / self.amp.max()
calc['threshold'] = thresh
self.amp = 255.0 - self.amp * 255.0 / self.amp.max()
arr = asarray(self.amp.flat)
per = 100 * (arr <= thresh).sum() / len(arr)
msg = '%.2f%% of signal is above the threshold.' % per
if per > 35.0:
print(' WARN: %s Consider using a lower threshold.' % msg)
elif per < 5.0:
print(' WARN: %s Consider using a higher threshold.' % msg)
else:
print(' INFO: %s' % msg)
def init_image(self):
colors = self.conf['colors']
self.amp = flipud(self.amp)
self.amp_back = empty_like(self.amp)
self.freq = flipud(self.freq)
self.imgdata = array(self.amp, uint8)
h, w = self.imgdata.shape
self.freq /= 1000.0
self.image = QImage(self.imgdata.tobytes(), w, h, w, QImage.Format_Grayscale8)
if colors['invert']:
self.image.invertPixels()
def init_cursor(self, w, h):
cursor = QWidget(self)
cursor.setAttribute(Qt.WA_TransparentForMouseEvents)
effect = QGraphicsOpacityEffect(cursor)
effect.setOpacity(0.4)
cursor.setGraphicsEffect(effect)
cursor.setGeometry(0, 0, w, h)
cursor.setAutoFillBackground(True)
p = cursor.palette()
p.setColor(cursor.backgroundRole(), QColor(self.conf['colors']['cursor']))
cursor.setPalette(p)
cursor.hide()
return cursor
def init_eraser(self, w, h):
eraser = QWidget(self)
eraser.setAttribute(Qt.WA_TransparentForMouseEvents)
effect = QGraphicsOpacityEffect(eraser)
effect.setOpacity(0.4)
eraser.setGraphicsEffect(effect)
eraser.setGeometry(0, 0, w, h)
eraser.setAutoFillBackground(True)
p = eraser.palette()
p.setColor(eraser.backgroundRole(), QColor(self.conf['colors']['eraser']))
eraser.setPalette(p)
eraser.hide()
return eraser
def fit(self):
s = self.image.size()
asp = s.width() / (s.height() * self.conf['scale-ratio'])
if self.size().height() * asp > self.size().width():
self.resize(self.width(), self.width() / asp)
else:
self.resize(self.height() * asp, self.height())
def mode_normal(self):
self.unsetCursor()
self.eraser.hide()
self.cursorX.hide()
self.cursorY.hide()
def mode_cursor(self):
self.setCursor(Qt.BlankCursor)
self.eraser.hide()
self.cursorX.show()
self.cursorY.show()
def mode_eraser(self):
self.setCursor(Qt.BlankCursor)
self.cursorX.hide()
self.cursorY.hide()
self.eraser.show()
def paintEvent(self, e):
# TODO refactor this rats nest
qp = QPainter(self)
qp.setRenderHints(QPainter.SmoothPixmapTransform | QPainter.Antialiasing)
qp.drawImage(self.contentsRect(), self.image, self.image.rect())
r = QRect(0, 0, self.width() - 1, self.height() - 1)
qp.setPen(QColor(self.conf['colors']['border']))
qp.drawRect(r)
path = QPainterPath()
pfreq, ptime, pex, tex = 0.0, 0.0, 0.0, 0.0
qp.setPen(QColor(self.conf['colors']['line']))
for x, y in enumerate(argmin(self.amp, 0)):
if self.amp[y,x] > self.conf['calculations']['threshold']:
continue
t, f = self.time[x], self.freq[y]
# TODO log10f?
if pfreq == 0.0:
path.moveTo(x * self.sx, y * self.sy)
else:
path.lineTo(x * self.sx, y * self.sy)
pex += sqrt((f - pfreq)**2 + (t - ptime)**2)
tex += t - ptime
ptime, pfreq = t, f
qp.drawPath(path)
self.fexChanged.emit(pex, tex)
def resizeEvent(self, e):
self.sx = self.width() / self.image.width()
self.sy = self.height() / self.image.height()
def mousePressEvent(self, e):
if self.cursorX.isVisible():
pass
elif self.eraser.isVisible():
copyto(self.amp_back, self.amp)
pass
elif e.button() == Qt.MiddleButton:
self.fit()
else:
self.__pos = self.pos()
self.__size = self.size()
self.__epos = self.mapToParent(e.pos())
def mouseMoveEvent(self, e):
# TODO refactor this rats nest
# TODO add eraser size changes
y = e.pos().y()
if y < self.eraser.maxheight / 2.0:
fixed = y * 2.0
elif y > self.height() - self.eraser.maxheight / 2.0:
fixed = (self.height() - y) * 2.0
else:
fixed = self.eraser.maxheight
|
| ︙ | ︙ | |||
202 203 204 205 206 207 208 | def mouseEraseEvent(self, e): w = self.eraser.width() h = self.eraser.height() x = e.pos().x() - w / 2.0 y = e.pos().y() - h / 2.0 x1, y1 = self.mapToImgdata(x, y) x2, y2 = self.mapToImgdata(x + w, y + h) | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | def mouseEraseEvent(self, e): w = self.eraser.width() h = self.eraser.height() x = e.pos().x() - w / 2.0 y = e.pos().y() - h / 2.0 x1, y1 = self.mapToImgdata(x, y) x2, y2 = self.mapToImgdata(x + w, y + h) self.amp[y1:y2,x1:x2] = 255.0 self.update(x, 0, w, self.height()) def mouseNormalEvent(self, e): if e.buttons() == Qt.LeftButton: diff = self.mapToParent(e.pos()) - self.__epos + self.__pos self.move(diff) elif e.buttons() == Qt.RightButton: |
| ︙ | ︙ |
Changes to setup.py.
1 2 3 4 | from setuptools import setup def readme(): | | > | | > | > | | 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 |
from setuptools import setup
def readme():
with open('README.rst') as f:
return f.read()
setup(
name='fexqt',
version='2.0.0a1',
description='Frequency Excursion Calculator',
long_description=readme(),
classifiers=[
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: MIT License',
'Intended Audience :: Science/Research',
'Programming Language :: Python :: 3',
'Topic :: Multimedia :: Sound/Audio :: Analysis',
'Topic :: Scientific/Engineering :: Visualization'
],
url='http://jessemcclure.org',
author='Jesse McClure',
author_email='jmcclure@broadinstitute.org',
license='MIT',
packages=['fexqt'],
package_data={'fexqt': ['*.yaml']},
install_requires=['pyaml', 'PyQt5', 'scipy'],
python_requires='>=3',
entry_points={'console_scripts': ['fex=fexqt:main']},
include_package_data=True,
zip_safe=False)
|