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) |