├── .gitignore ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── copyright │ ├── Lancaster_University_GPLv3.xml │ └── profiles_settings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml ├── statistic.xml └── vcs.xml ├── .pre-commit-config.yaml ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── build ├── .resignore ├── linux │ └── build.sh ├── macos │ └── build.sh ├── strip_resources.py └── windows │ └── build.bat ├── dist ├── linux │ └── install.sh └── windows │ └── install.ps1 ├── docs ├── common-issues.md ├── core-knowledge.md ├── developer-guide.md ├── images │ ├── TFPresenter.png │ ├── git_hooks.png │ ├── scheduler.png │ └── update_available.png └── performance.md ├── macos.spec ├── main.spec ├── packages ├── README.md ├── WFT │ ├── for_redistribution │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── WFT │ │ │ ├── WFT.ctf │ │ │ └── __init__.py │ │ └── setup.py │ └── for_testing │ │ ├── WFT │ │ ├── WFT.ctf │ │ └── __init__.py │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ └── setup.py ├── WT │ ├── for_redistribution │ │ ├── MyAppInstaller_web.exe │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── WT │ │ │ ├── WT.ctf │ │ │ └── __init__.py │ │ ├── setup.py │ │ └── wt_test │ │ │ ├── __init__.py │ │ │ └── wt_test.ctf │ └── for_testing │ │ ├── WT │ │ ├── WT.ctf │ │ └── __init__.py │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ ├── setup.py │ │ └── wt_test │ │ ├── __init__.py │ │ └── wt_test.ctf ├── biphaseWavPython │ ├── for_redistribution │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── biphaseWavPython │ │ │ ├── __init__.py │ │ │ └── biphaseWavPython.ctf │ │ └── setup.py │ └── for_testing │ │ ├── biphaseWavPython │ │ ├── __init__.py │ │ └── biphaseWavPython.ctf │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ └── setup.py ├── bispecWavPython │ ├── for_redistribution │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── bispecWavPython │ │ │ ├── __init__.py │ │ │ └── bispecWavPython.ctf │ │ └── setup.py │ └── for_testing │ │ ├── bispecWavPython │ │ ├── __init__.py │ │ └── bispecWavPython.ctf │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ └── setup.py ├── ecurve │ ├── for_redistribution │ │ ├── MyAppInstaller_web.exe │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── ecurve │ │ │ ├── __init__.py │ │ │ └── ecurve.ctf │ │ └── setup.py │ └── for_testing │ │ ├── ecurve │ │ ├── __init__.py │ │ └── ecurve.ctf │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ └── setup.py ├── full_bayesian │ ├── for_redistribution │ │ └── MyAppInstaller_web.exe │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── full_bayesian │ │ │ ├── __init__.py │ │ │ └── full_bayesian.ctf │ │ └── setup.py │ └── for_testing │ │ ├── full_bayesian │ │ ├── __init__.py │ │ └── full_bayesian.ctf │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ └── setup.py ├── install.py ├── rectfr │ ├── for_redistribution │ │ ├── MyAppInstaller_web.exe │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── rectfr │ │ │ ├── __init__.py │ │ │ └── rectfr.ctf │ │ └── setup.py │ └── for_testing │ │ ├── readme.txt │ │ ├── rectfr │ │ ├── __init__.py │ │ └── rectfr.ctf │ │ ├── requiredMCRProducts.txt │ │ └── setup.py ├── ridge_extraction │ ├── for_redistribution │ │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ │ ├── GettingStarted.html │ │ ├── ridge_extraction │ │ │ ├── __init__.py │ │ │ └── ridge_extraction.ctf │ │ └── setup.py │ └── for_testing │ │ ├── readme.txt │ │ ├── requiredMCRProducts.txt │ │ ├── ridge_extraction │ │ ├── __init__.py │ │ └── ridge_extraction.ctf │ │ └── setup.py └── wavsurrogate │ ├── for_redistribution │ └── MyAppInstaller_web.install │ ├── for_redistribution_files_only │ ├── GettingStarted.html │ ├── setup.py │ └── wavsurrogate │ │ ├── __init__.py │ │ └── wavsurrogate.ctf │ └── for_testing │ ├── readme.txt │ ├── requiredMCRProducts.txt │ ├── setup.py │ └── wavsurrogate │ ├── __init__.py │ └── wavsurrogate.ctf ├── publish_update.py ├── requirements.txt ├── res ├── README.md ├── colours │ └── colormap.csv ├── data │ ├── butter.mat │ ├── csv │ │ ├── 32_signals.csv │ │ ├── ECG.csv │ │ ├── dual_signal.csv │ │ ├── ecgtest.csv │ │ ├── heartrate.csv │ │ ├── many_signal.csv │ │ └── single_signal.csv │ ├── mat │ │ ├── 1signal_10Hz.mat │ │ ├── 2signals_10Hz.mat │ │ ├── 6signals_10Hz.mat │ │ ├── IEEEsigs │ │ │ ├── IEEEex_10Hz.mat │ │ │ ├── xp_100Hz.mat │ │ │ └── xp_10Hz.mat │ │ ├── fm_square_200Hz.mat │ │ ├── freq_mod_200Hz.mat │ │ ├── group_coherence │ │ │ ├── group1_Cphd.mat │ │ │ └── group2_phd.mat │ │ ├── load_first.mat │ │ ├── load_second.mat │ │ ├── noisy.mat │ │ ├── noisySIN.mat │ │ └── noisyyy.mat │ └── npy │ │ ├── 1signal_10Hz.npy │ │ ├── 2signals_10Hz.npy │ │ ├── 6signals_10Hz.npy │ │ ├── fm_square_200Hz.npy │ │ ├── freq_mod_200Hz.npy │ │ ├── load_first.npy │ │ ├── load_second.npy │ │ ├── noisy.npy │ │ ├── noisySIN.npy │ │ └── noisyyy.npy ├── img │ ├── frontbanner.svg │ ├── icon.svg │ ├── logo.svg │ ├── physicslogo.png │ └── splashscreen.svg └── layout │ ├── dialog_frequency.ui │ ├── dialog_matlab_runtime.ui │ ├── dialog_pymodalib_cache.ui │ ├── dialog_select_file.ui │ ├── dialog_select_group.ui │ ├── dialog_settings.ui │ ├── window_bispectrum_analysis.ui │ ├── window_dynamical_bayesian.ui │ ├── window_group_coherence.ui │ ├── window_harmonics.ui │ ├── window_launcher.ui │ ├── window_phase_coherence.ui │ ├── window_ridge_extraction.ui │ └── window_time_freq.ui └── src ├── README.md ├── data ├── parsing │ ├── BaseParser.py │ ├── CsvParser.py │ ├── MatParser.py │ ├── NpyParser.py │ ├── groups │ │ ├── GroupMatParser.py │ │ └── GroupNpyParser.py │ └── parsing.py └── resources.py ├── gui ├── Application.py ├── BaseUI.py ├── components │ ├── DualSignalComponent.py │ ├── FreqComponent.py │ ├── PreprocessComponent.py │ ├── SingleSignalComponent.py │ ├── SurrogateComponent.py │ └── VerticalMultiPlotComponent.py ├── dialogs │ ├── ErrorBox.py │ ├── FrequencyDialog.py │ ├── MatlabRuntimeDialog.py │ ├── PyMODAlibCacheDialog.py │ ├── SettingsDialog.py │ ├── UseShortcutComponent.py │ └── files │ │ ├── DragDropLabel.py │ │ └── SelectFileDialog.py ├── plotting │ ├── MatplotlibWidget.py │ ├── NavigationBar.py │ ├── PlotWidget.py │ ├── PyQtGraphWidget.py │ └── plots │ │ ├── AmplitudePlot.py │ │ ├── ColorMeshPlot.py │ │ ├── GroupCoherencePlot.py │ │ ├── PreprocessPlot.py │ │ ├── Rect.py │ │ └── SignalPlot.py └── windows │ ├── BaseWindow.py │ ├── CentredWindow.py │ ├── MaximisedWindow.py │ ├── ViewProperties.py │ ├── bayesian │ ├── DBPlot.py │ ├── DBPlot3d.py │ ├── DBPresenter.py │ ├── DBViewProperties.py │ ├── DBWindow.py │ └── ParamSet.py │ ├── bispectrum │ ├── BAPlot.py │ ├── BAPresenter.py │ ├── BAViewProperties.py │ └── BAWindow.py │ ├── common │ ├── BaseTFPresenter.py │ ├── BaseTFViewProperties.py │ └── BaseTFWindow.py │ ├── groupcoherence │ ├── GCPresenter.py │ ├── GCViewProperties.py │ ├── GCWindow.py │ └── LoadGroupDataDialog.py │ ├── harmonics │ ├── DHPresenter.py │ ├── DHViewProperties.py │ └── DHWindow.py │ ├── launcher │ └── LauncherWindow.py │ ├── phasecoherence │ ├── PCPresenter.py │ ├── PCViewProperties.py │ └── PCWindow.py │ ├── ridgeextraction │ ├── REPlot.py │ ├── REPresenter.py │ ├── REViewProperties.py │ └── REWindow.py │ └── timefrequency │ ├── TFPresenter.py │ ├── TFViewProperties.py │ └── TFWindow.py ├── main.py ├── maths ├── algorithms │ ├── bayesian.py │ ├── matlab_utils.py │ ├── matlabwrappers │ │ ├── bayesian_inference.py │ │ ├── biphase_wav_new.py │ │ ├── bispec_wav_new.py │ │ ├── wav_surrogate.py │ │ └── wft.py │ ├── multiprocessing │ │ ├── bandpass_filter.py │ │ ├── bayesian_inference.py │ │ ├── bispectrum_analysis.py │ │ ├── phase_coherence.py │ │ ├── ridge_extraction.py │ │ └── time_frequency.py │ ├── surrogates.py │ ├── windowed_fourier.py │ └── wpc.py ├── num_utils.py ├── params │ ├── BAParams.py │ ├── DBParams.py │ ├── DHParams.py │ ├── PCParams.py │ ├── REParams.py │ └── TFParams.py └── signals │ ├── SignalGroups.py │ ├── SignalPairs.py │ ├── Signals.py │ ├── TimeSeries.py │ └── data │ ├── BAOutputData.py │ ├── DBOutputData.py │ └── TFOutputData.py ├── processes ├── MPHandler.py └── mp_utils.py ├── updater ├── CleanupThread.py ├── UpdateThread.py ├── Updater.py ├── check.py └── download.py └── utils ├── __init__.py ├── args.py ├── cache.py ├── decorators.py ├── dict_utils.py ├── errorhandling.py ├── file_utils.py ├── launcher.py ├── log_utils.py ├── os_utils.py ├── qutils.py ├── settings.py ├── shortcuts.py └── stdout_redirect.py /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/Lancaster_University_GPLv3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 47 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/statistic.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | 3 | # Doctoc generates the table of contents for each markdown file. 4 | - repo: https://github.com/thlorenz/doctoc 5 | rev: v1.4.0 6 | hooks: 7 | - id: doctoc 8 | args: ["docs", # Apply to everything inside the 'docs' folder. 9 | '--title=## Table of Contents', # Set the title of the table of contents. 10 | "--maxlevel=2", # Only add sections with `#` or `##`. 11 | "--github"] # Use GitHub formatting. 12 | 13 | # Black formats Python files. 14 | - repo: https://github.com/ambv/black 15 | rev: stable 16 | hooks: 17 | - id: black 18 | language_version: python3 19 | files: ^src/ # Include all files within `src/`. 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Only build tagged releases on the master branch. 2 | if: 3 | - tag IS present 4 | - branch = master 5 | 6 | language: generic 7 | cache: 8 | directories: 9 | - /Library/Caches/Homebrew 10 | - $HOME/.cache/pip 11 | 12 | matrix: 13 | include: 14 | - dist: bionic 15 | - os: osx 16 | 17 | install: 18 | - if [ "$TRAVIS_OS_NAME" == "osx" ] ; then sudo chmod +x build/macos/build.sh && build/macos/build.sh ; fi 19 | - if [ "$TRAVIS_OS_NAME" == "linux" ] ; then sudo chmod +x build/linux/build.sh && build/linux/build.sh ; fi 20 | 21 | script: true 22 | 23 | deploy: 24 | provider: releases 25 | api_key: 26 | secure: NzRG8BAjBQCfzCB7DqyEI/KuH/NQGtRnlywgqf1cvaVwPG5itr0Uo9X7sAc2oeIqTpV/7CMQxzqdS+3ujRCyXx9rMQKIMEjEeZU9uLO4QX5AL2JXCac10HB0PXulREa1mMExr9kOGSLs35JQEYCpzdOyesyZGSHTYWjJpuk9/ryyzKSzzJgH99quzGlsguampXX2v8tTpjT49oURFKcF/ra3iWh1ueg+zKgHPaJq5ZNzMFrG2MaAQALAii0AqaXNhsLr3tfCzcHPxxTrCgBxBGk0vSCtGkLOjNCL4GOXTe4wEedeSCjzeJw0eYO7ojM7Zv8bS7whhSeXTgjlKXngcb54+xDpHb/iByesGZwy6M+4Zd+IejjRw5LN5OuBSf/dCSHZ/DYrqdoWFe6cKdWGiRU9VQzCdLFs5vCUZLBjmfaDi1KqKmruV12K9UBDiTXAzXAQG48J2Bv3xXpjrBvBiFXW8O+ddnrlNufsWTmJyV/sLFjnQhWh+FBsByz5es4Zv6whnxSIVLkFJYZZ07RNHKF3AQjphsHU8V/buhuph7FSFsPzejujQOM41QDTpigN1prSyqFEqv+jaM11bEsPujP2JhuWUruSZpDmb/IDrvNXZPT1r7ydtGXiP5qLCEDJy+9xUwrL+guoyCXYDyVxJTD87kzrmTx03aw5e6/o9/M= 27 | file_glob: true 28 | file: 'releases/PyMODA*' 29 | on: 30 | repo: luphysics/PyMODA 31 | tags: true 32 | skip_cleanup: 'true' 33 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | skip_non_tags: true 2 | 3 | image: Visual Studio 2019 4 | 5 | environment: 6 | PYTHON: "C:\\PYTHON37-x64" 7 | 8 | install: 9 | - "%PYTHON%\\python.exe -m pip install -U pip --user" 10 | 11 | build_script: 12 | - cmd: cmd /c build\windows\build.bat 13 | 14 | artifacts: 15 | - path: releases/PyMODA-win64.zip 16 | name: portable 17 | 18 | deploy: 19 | - provider: GitHub 20 | tag: $(APPVEYOR_REPO_TAG_NAME) 21 | description: "Release $(APPVEYOR_REPO_TAG_NAME)" 22 | auth_token: 23 | secure: z8OqFPVcisBMWLDG/O/XsyqQaUgGgi4F4L3sMxQxk80JQm2IKmwQZC+fcbMd+RjZ 24 | draft: false 25 | prerelease: false 26 | force_update: true 27 | artifact: portable 28 | on: 29 | APPVEYOR_REPO_TAG: true 30 | -------------------------------------------------------------------------------- /build/.resignore: -------------------------------------------------------------------------------- 1 | # This file defines files and folders which will be deleted 2 | # when 'strip_resources.py' is executed. This reduces the size 3 | # of the packaged executable. 4 | # 5 | # Paths listed here are relative to the 'res' directory. 6 | # 7 | # Note: This file uses only simplified syntax. 8 | # Each value must be a Unix-style pathname pattern. 9 | 10 | data/npy 11 | 12 | butter.mat 13 | xp_100Hz.mat 14 | freq_mod_200Hz.mat 15 | 16 | load_*.mat 17 | noisyyy.mat 18 | 19 | heartrate.csv 20 | ECG.csv 21 | ecgtest.csv -------------------------------------------------------------------------------- /build/linux/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | sudo add-apt-repository ppa:deadsnakes/ppa -y 5 | 6 | sudo apt-get update 7 | sudo apt-get install python3.7 python3.7-dev -y 8 | sudo apt-get install python3-pip -y 9 | 10 | python3.7 --version 11 | 12 | python3.7 -m pip install -U pip 13 | python3.7 -m pip install -U setuptools wheel 14 | python3.7 -m pip install -U PyInstaller 15 | 16 | python3.7 packages/install.py -y 17 | python3.7 -m pip install -U Pillow 18 | 19 | python3.7 build/strip_resources.py -y 20 | python3.7 -m PyInstaller main.spec --noconfirm 21 | 22 | cd dist 23 | mv main PyMODA 24 | 25 | tar -zcf PyMODA-linux_x86_64.tar.gz PyMODA 26 | cd .. 27 | 28 | mkdir -p releases 29 | mv dist/PyMODA-linux_x86_64.tar.gz releases/ -------------------------------------------------------------------------------- /build/macos/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | python3 --version 5 | 6 | python3 -m pip install -U pip 7 | python3 -m pip install PyInstaller 8 | 9 | python3 packages/install.py -yv 10 | python3 build/strip_resources.py -y 11 | 12 | python3 -m PyInstaller macos.spec --noconfirm 13 | 14 | mv dist/PyMODA.app . 15 | 16 | tar -zcf PyMODA-macOS.tar.gz PyMODA.app 17 | 18 | mkdir -p releases 19 | mv PyMODA-macOS.tar.gz releases/ 20 | -------------------------------------------------------------------------------- /build/strip_resources.py: -------------------------------------------------------------------------------- 1 | """ 2 | Script which deletes unwanted resources in the 'res' folder to reduce the size 3 | of the packaged executable. 4 | """ 5 | import os 6 | from os.path import join 7 | from pathlib import Path 8 | import sys 9 | import shutil 10 | from typing import List 11 | 12 | args = sys.argv[1:] 13 | delete = "-y" in args 14 | 15 | if not delete: 16 | print( 17 | "Please note, files will not actually be " 18 | "deleted unless the '-y' argument is used.", 19 | end="\n\n" 20 | ) 21 | 22 | wd = str(Path(os.path.abspath(os.path.dirname(__file__))).parent) 23 | assert wd == os.getcwd(), "Working directory must be the root of the repository." 24 | 25 | build_dir = os.path.abspath(os.path.dirname(__file__)) 26 | wd = os.getcwd() 27 | res_dir = join(wd, "res") 28 | 29 | 30 | def load_ignores() -> List[str]: 31 | out = [] 32 | 33 | with open(join(build_dir, ".resignore")) as f: 34 | for line in f: 35 | if not line.startswith("#"): 36 | out.append(line.rstrip("\n")) 37 | 38 | return [i for i in out if i] 39 | 40 | 41 | ignores = load_ignores() 42 | 43 | for pattern in ignores: 44 | for f in Path(res_dir).rglob(f"{pattern}"): 45 | print(f"Deleting resource: '{f}'") 46 | 47 | if delete: 48 | try: 49 | os.remove(f) 50 | except: 51 | shutil.rmtree(f) 52 | -------------------------------------------------------------------------------- /build/windows/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | "%PYTHON%\python.exe" -m pip install -U setuptools wheel 4 | "%PYTHON%\python.exe" -m pip install -U PyInstaller 5 | 6 | "%PYTHON%\python.exe" packages\install.py -y 7 | "%PYTHON%\python.exe" build\strip_resources.py -y 8 | 9 | "%PYTHON%\python.exe" -m PyInstaller main.spec --noconfirm 10 | 11 | cd dist 12 | move main PyMODA 13 | 14 | 7z a -r PyMODA-win64.zip PyMODA 15 | cd .. 16 | 17 | mkdir releases 18 | move dist\PyMODA-win64.zip releases\ -------------------------------------------------------------------------------- /dist/linux/install.sh: -------------------------------------------------------------------------------- 1 | echo "Downloading PyMODA. Please wait, this may take over a minute..." 2 | curl -fsSL "https://github.com/luphysics/PyMODA/releases/latest/download/PyMODA-linux_x86_64.tar.gz" -o pymoda.tar.gz 3 | 4 | echo "Extracting PyMODA..." 5 | mkdir -p ~/.pymoda 6 | tar -xf pymoda.tar.gz -C ~/.pymoda/ 7 | 8 | echo "Cleaning up..." 9 | rm pymoda.tar.gz 10 | 11 | echo "Launching PyMODA..." 12 | ~/.pymoda/PyMODA/PyMODA 13 | -------------------------------------------------------------------------------- /dist/windows/install.ps1: -------------------------------------------------------------------------------- 1 | # Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/luphysics/PyMODA/dev/dist/windows/install.ps1')) 2 | 3 | Write-Output "Downloading PyMODA. Please wait, this may take over a minute..." 4 | 5 | $url = "https://github.com/luphysics/PyMODA/releases/latest/download/PyMODA-win64.zip" 6 | 7 | $targetDir = "$env:AppData\PyMODA" 8 | $targetFile = "$targetDir\pymoda.zip" 9 | 10 | ### Download the .zip file ### 11 | 12 | $uri = New-Object "System.Uri" "$url" 13 | 14 | $request = [System.Net.HttpWebRequest]::Create($uri) 15 | $request.set_Timeout(15000) 16 | $response = $request.GetResponse() 17 | 18 | $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024) 19 | $responseStream = $response.GetResponseStream() 20 | $targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $targetFile, Create 21 | 22 | $buffer = new-object byte[] 10KB 23 | $count = $responseStream.Read($buffer,0,$buffer.length) 24 | $downloadedBytes = $count 25 | 26 | while ($count -gt 0) 27 | { 28 | $targetStream.Write($buffer, 0, $count) 29 | $count = $responseStream.Read($buffer,0,$buffer.length) 30 | $downloadedBytes = $downloadedBytes + $count 31 | Write-Progress -activity "Downloading file '$($url.split('/') | Select -Last 1)'" -status "Downloaded ($([System.Math]::Floor($downloadedBytes/1024))K of $($totalLength)K): " -PercentComplete ((([System.Math]::Floor($downloadedBytes/1024)) / $totalLength) * 100) 32 | } 33 | 34 | Write-Progress -activity "Finished downloading file '$($url.split('/') | Select -Last 1)'" 35 | 36 | $targetStream.Flush() 37 | $targetStream.Close() 38 | $targetStream.Dispose() 39 | $responseStream.Dispose() 40 | 41 | ### Extract the .zip file ### 42 | 43 | Write-Output "Extracting PyMODA..." 44 | 45 | mkdir -Force "$targetDir\PyMODA" 46 | rm -r "$targetDir\PyMODA" 47 | 48 | Add-Type -AssemblyName System.IO.Compression.FileSystem 49 | [System.IO.Compression.ZipFile]::ExtractToDirectory($targetFile, $targetDir) 50 | 51 | Write-Output "Cleaning up..." 52 | rm $targetFile 53 | 54 | Write-Output "Launching PyMODA..." 55 | & "$targetDir\PyMODA\PyMODA.exe" --create-shortcut -------------------------------------------------------------------------------- /docs/images/TFPresenter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/docs/images/TFPresenter.png -------------------------------------------------------------------------------- /docs/images/git_hooks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/docs/images/git_hooks.png -------------------------------------------------------------------------------- /docs/images/scheduler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/docs/images/scheduler.png -------------------------------------------------------------------------------- /docs/images/update_available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/docs/images/update_available.png -------------------------------------------------------------------------------- /docs/performance.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Table of Contents 4 | 5 | - [Performance and efficiency](#performance-and-efficiency) 6 | - [Concurrency](#concurrency) 7 | - [Windows vs Linux](#windows-vs-linux) 8 | 9 | 10 | 11 | # Performance and efficiency 12 | 13 | ## Concurrency 14 | 15 | When performing multiple discrete calculations - for example, the wavelet transform of 6 signals - PyMODA uses multiprocessing to greatly increase efficiency by allocating different calculations to different CPU cores. 16 | 17 | Therefore, it is more efficient to transform multiple signals if possible. Efficiency will plateau when the number of signals is higher than the number of CPU cores. 18 | 19 | ### AMD Ryzen 3700X 20 | 21 | The AMD Ryzen 3700X is an 8-core, 16-thread CPU. These tests were run on Manjaro Linux. 22 | 23 | | Operation | Total time: individually ("Transform Single" for all) | Total time: simultaneously ("Transform All") | Performance improvement | 24 | | ------------- | ------------- | ------ | ------ | 25 | | WT on 32 signals | 134s | 19.1s | x7.0 | 26 | 27 | ### Intel i7-6700 28 | 29 | The Intel i7-6700 is a 4-core, 8-thread CPU. These tests were run on KDE neon. 30 | 31 | | Operation | Total time: individually ("Transform Single" for all) | Total time: simultaneously ("Transform All") | Performance improvement | 32 | | ------------- | ------------- | ------ | ------ | 33 | | WT on 2 signals | 10s | 5.4s | x1.9 | 34 | | WT on 6 signals | 30s | 8.4s | x3.6 | 35 | | WT on 32 signals | 160s | 43.1s | x3.7 | 36 | 37 | ## Windows vs Linux 38 | 39 | Linux performs slightly better than Windows with a small number of signals, and significantly better with many signals. 40 | 41 | ### AMD Ryzen 3700X 42 | 43 | | Operating system | Time: WT on 1 signal | Time: WT on 32 signals | 44 | | ---- | ---- | ---- | 45 | | Windows 10 | 4.7s | 33.1s | 46 | | Manjaro Linux | 4.2s | 19.1s | 47 | 48 | ### Intel i7-6700 49 | 50 | | Operating system | Time: WT on 6 signals | Time: WT on 32 signals | 51 | | ---- | ---- | ---- | 52 | | Windows 10 (VM) | 17.5s | 82s | 53 | | Manjaro Linux (VM) | 17.4s | 74s | 54 | -------------------------------------------------------------------------------- /macos.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | block_cipher = None 4 | 5 | import os 6 | import os.path 7 | 8 | def find_packages(): 9 | from pathlib import Path 10 | 11 | out = [] 12 | 13 | for f in Path(".").rglob("**/for_redistribution_files_only/*"): 14 | head, tail = os.path.split(f) 15 | 16 | if os.path.isdir(f) and tail.lower() in head.lower(): 17 | out.append((f, tail,)) 18 | 19 | return out 20 | 21 | 22 | data = [ 23 | ("res", "res"), 24 | ("LICENSE", "."), 25 | *find_packages(), 26 | ] 27 | 28 | a = Analysis( 29 | ["src/main.py"], 30 | pathex=[os.getcwd()], 31 | binaries=[], 32 | datas=data, 33 | hiddenimports=[ 34 | "qasync", 35 | "gui.dialogs.files.DragDropLabel", 36 | ], 37 | hookspath=[], 38 | runtime_hooks=[], 39 | excludes=[], 40 | win_no_prefer_redirects=False, 41 | win_private_assemblies=False, 42 | cipher=block_cipher, 43 | noarchive=False, 44 | ) 45 | 46 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 47 | 48 | exe = EXE( 49 | pyz, 50 | a.scripts, 51 | [], 52 | exclude_binaries=True, 53 | name="PyMODA", 54 | debug=False, 55 | bootloader_ignore_signals=False, 56 | strip=False, 57 | upx=True, 58 | console=False, 59 | ) 60 | 61 | coll = COLLECT( 62 | exe, 63 | a.binaries, 64 | a.zipfiles, 65 | a.datas, 66 | strip=False, 67 | upx=True, 68 | upx_exclude=[], 69 | name="main", 70 | ) 71 | 72 | app = BUNDLE(coll, 73 | name='PyMODA.app', 74 | icon=None, 75 | bundle_identifier="com.luphysics.pymoda") -------------------------------------------------------------------------------- /main.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | block_cipher = None 4 | 5 | import os 6 | import os.path 7 | 8 | def find_packages(): 9 | from pathlib import Path 10 | 11 | out = [] 12 | 13 | for f in Path(".").rglob("**/for_redistribution_files_only/*"): 14 | head, tail = os.path.split(f) 15 | 16 | if os.path.isdir(f) and tail.lower() in head.lower(): 17 | out.append((f, tail,)) 18 | 19 | return out 20 | 21 | 22 | data = [ 23 | ("res", "res"), 24 | ("LICENSE", "."), 25 | *find_packages(), 26 | ] 27 | 28 | a = Analysis( 29 | ["src/main.py"], 30 | pathex=[os.getcwd()], 31 | binaries=[], 32 | datas=data, 33 | hiddenimports=[ 34 | "qasync", 35 | "gui.dialogs.files.DragDropLabel", 36 | "pkg_resources.py2_warn", 37 | ], 38 | hookspath=[], 39 | runtime_hooks=[], 40 | excludes=[], 41 | win_no_prefer_redirects=False, 42 | win_private_assemblies=False, 43 | cipher=block_cipher, 44 | noarchive=False, 45 | ) 46 | 47 | pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) 48 | 49 | exe = EXE( 50 | pyz, 51 | a.scripts, 52 | [], 53 | exclude_binaries=True, 54 | name="PyMODA", 55 | debug=False, 56 | bootloader_ignore_signals=False, 57 | strip=False, 58 | upx=True, 59 | console=True, 60 | ) 61 | 62 | coll = COLLECT( 63 | exe, 64 | a.binaries, 65 | a.zipfiles, 66 | a.datas, 67 | strip=False, 68 | upx=True, 69 | upx_exclude=[], 70 | name="main", 71 | ) 72 | -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Table of Contents 4 | 5 | - [packages](#packages) 6 | 7 | 8 | 9 | ## packages 10 | 11 | The `packages` folder contains the MATLAB-packaged Python libraries used by PyMODA. 12 | 13 | ### install.py 14 | 15 | `install.py` is a Python script which installs the Python packages from this folder, and uses pip to install the 16 | requirements from `requirements.txt` in the root folder. 17 | 18 | To specify a particular Python version to use, add it as a command-line argument. For example, `sudo python3 install.py python3.7` will install the packages with `python3.7` even if `python3` is Python 3.6. -------------------------------------------------------------------------------- /packages/WFT/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WFT/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/WFT/for_redistribution_files_only/WFT/WFT.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WFT/for_redistribution_files_only/WFT/WFT.ctf -------------------------------------------------------------------------------- /packages/WFT/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'WFT' 28 | ], 29 | package_data={'WFT': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/WFT/for_testing/WFT/WFT.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WFT/for_testing/WFT/WFT.ctf -------------------------------------------------------------------------------- /packages/WFT/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | WFT MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | 13 | Alternatively, download and install the Linux version of the MATLAB Runtime for R2019a 14 | from the following link on the MathWorks website: 15 | 16 | http://www.mathworks.com/products/compiler/mcr/index.html 17 | 18 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 19 | "Distribute Applications" in the MATLAB Compiler SDK documentation 20 | in the MathWorks Documentation Center. 21 | 22 | Verify that a Linux version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 23 | 24 | 2. Installing the WFT Package 25 | 26 | A. Change to the directory that contains the file setup.py and the subdirectory WFT. If 27 | you do not have write permissions, copy all its contents to a temporary location and 28 | change to that directory. 29 | 30 | B. Execute the command: 31 | 32 | python setup.py install [options] 33 | 34 | If you have full administrator privileges, and install to the default location, you do 35 | not need to specify any options. Otherwise, use --user to install to your home folder, or 36 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 37 | the PYTHONPATH environment variable. For details, refer to: 38 | 39 | https://docs.python.org/2/install/index.html 40 | 41 | C. Set environment variables as follows: 42 | 43 | In the following directions, replace MR/v96 by the directory on the target machine where MATLAB is installed, or MR by the directory where the MATLAB Runtime is installed. 44 | 45 | (1) Set the environment variable XAPPLRESDIR to this value: 46 | 47 | MR/v96/X11/app-defaults 48 | 49 | 50 | (2) If the environment variable LD_LIBRARY_PATH is undefined, set it to the following: 51 | 52 | MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 53 | 54 | If it is defined, set it to the following: 55 | 56 | ${LD_LIBRARY_PATH}:MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 57 | 58 | 3. Using the WFT Package 59 | 60 | The WFT package is on your Python path. To import it into a Python script or session, 61 | execute: 62 | 63 | import WFT 64 | 65 | If a namespace must be specified for the package, modify the import statement accordingly. 66 | -------------------------------------------------------------------------------- /packages/WFT/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 35000 -------------------------------------------------------------------------------- /packages/WFT/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'WFT' 28 | ], 29 | package_data={'WFT': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/WT/for_redistribution/MyAppInstaller_web.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WT/for_redistribution/MyAppInstaller_web.exe -------------------------------------------------------------------------------- /packages/WT/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WT/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/WT/for_redistribution_files_only/WT/WT.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WT/for_redistribution_files_only/WT/WT.ctf -------------------------------------------------------------------------------- /packages/WT/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'WT' 28 | ], 29 | package_data={'WT': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/WT/for_redistribution_files_only/wt_test/wt_test.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WT/for_redistribution_files_only/wt_test/wt_test.ctf -------------------------------------------------------------------------------- /packages/WT/for_testing/WT/WT.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WT/for_testing/WT/WT.ctf -------------------------------------------------------------------------------- /packages/WT/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | WT MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | NOTE: You will need administrator rights to run the MATLAB Runtime installer. 13 | 14 | Alternatively, download and install the Windows version of the MATLAB Runtime for R2019a 15 | from the following link on the MathWorks website: 16 | 17 | http://www.mathworks.com/products/compiler/mcr/index.html 18 | 19 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 20 | "Distribute Applications" in the MATLAB Compiler SDK documentation 21 | in the MathWorks Documentation Center. 22 | 23 | Verify that a Windows version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 24 | 25 | 2. Installing the WT Package 26 | 27 | A. Change to the directory that contains the file setup.py and the subdirectory WT. If 28 | you do not have write permissions, copy all its contents to a temporary location and 29 | change to that directory. 30 | 31 | B. Execute the command: 32 | 33 | python setup.py install [options] 34 | 35 | If you have full administrator privileges, and install to the default location, you do 36 | not need to specify any options. Otherwise, use --user to install to your home folder, or 37 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 38 | the PYTHONPATH environment variable. For details, refer to: 39 | 40 | https://docs.python.org/2/install/index.html 41 | 42 | 43 | 3. Using the WT Package 44 | 45 | The WT package is on your Python path. To import it into a Python script or session, 46 | execute: 47 | 48 | import WT 49 | 50 | If a namespace must be specified for the package, modify the import statement accordingly. 51 | -------------------------------------------------------------------------------- /packages/WT/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 35000 -------------------------------------------------------------------------------- /packages/WT/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'WT' 28 | ], 29 | package_data={'WT': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/WT/for_testing/wt_test/wt_test.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/WT/for_testing/wt_test/wt_test.ctf -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/biphaseWavPython/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_redistribution_files_only/biphaseWavPython/biphaseWavPython.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/biphaseWavPython/for_redistribution_files_only/biphaseWavPython/biphaseWavPython.ctf -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'biphaseWavPython' 28 | ], 29 | package_data={'biphaseWavPython': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_testing/biphaseWavPython/biphaseWavPython.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/biphaseWavPython/for_testing/biphaseWavPython/biphaseWavPython.ctf -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | biphaseWavPython MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | 13 | Alternatively, download and install the Linux version of the MATLAB Runtime for R2019a 14 | from the following link on the MathWorks website: 15 | 16 | http://www.mathworks.com/products/compiler/mcr/index.html 17 | 18 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 19 | "Distribute Applications" in the MATLAB Compiler SDK documentation 20 | in the MathWorks Documentation Center. 21 | 22 | Verify that a Linux version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 23 | 24 | 2. Installing the biphaseWavPython Package 25 | 26 | A. Change to the directory that contains the file setup.py and the subdirectory 27 | biphaseWavPython. If you do not have write permissions, copy all its contents to a 28 | temporary location and change to that directory. 29 | 30 | B. Execute the command: 31 | 32 | python setup.py install [options] 33 | 34 | If you have full administrator privileges, and install to the default location, you do 35 | not need to specify any options. Otherwise, use --user to install to your home folder, or 36 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 37 | the PYTHONPATH environment variable. For details, refer to: 38 | 39 | https://docs.python.org/2/install/index.html 40 | 41 | C. Set environment variables as follows: 42 | 43 | In the following directions, replace MR/v96 by the directory on the target machine where MATLAB is installed, or MR by the directory where the MATLAB Runtime is installed. 44 | 45 | (1) Set the environment variable XAPPLRESDIR to this value: 46 | 47 | MR/v96/X11/app-defaults 48 | 49 | 50 | (2) If the environment variable LD_LIBRARY_PATH is undefined, set it to the following: 51 | 52 | MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 53 | 54 | If it is defined, set it to the following: 55 | 56 | ${LD_LIBRARY_PATH}:MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 57 | 58 | 3. Using the biphaseWavPython Package 59 | 60 | The biphaseWavPython package is on your Python path. To import it into a Python script or 61 | session, execute: 62 | 63 | import biphaseWavPython 64 | 65 | If a namespace must be specified for the package, modify the import statement accordingly. 66 | -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 -------------------------------------------------------------------------------- /packages/biphaseWavPython/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'biphaseWavPython' 28 | ], 29 | package_data={'biphaseWavPython': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/bispecWavPython/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/bispecWavPython/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/bispecWavPython/for_redistribution_files_only/bispecWavPython/bispecWavPython.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/bispecWavPython/for_redistribution_files_only/bispecWavPython/bispecWavPython.ctf -------------------------------------------------------------------------------- /packages/bispecWavPython/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'bispecWavPython' 28 | ], 29 | package_data={'bispecWavPython': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/bispecWavPython/for_testing/bispecWavPython/bispecWavPython.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/bispecWavPython/for_testing/bispecWavPython/bispecWavPython.ctf -------------------------------------------------------------------------------- /packages/bispecWavPython/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | bispecWavPython MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | 13 | Alternatively, download and install the Linux version of the MATLAB Runtime for R2019a 14 | from the following link on the MathWorks website: 15 | 16 | http://www.mathworks.com/products/compiler/mcr/index.html 17 | 18 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 19 | "Distribute Applications" in the MATLAB Compiler SDK documentation 20 | in the MathWorks Documentation Center. 21 | 22 | Verify that a Linux version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 23 | 24 | 2. Installing the bispecWavPython Package 25 | 26 | A. Change to the directory that contains the file setup.py and the subdirectory 27 | bispecWavPython. If you do not have write permissions, copy all its contents to a 28 | temporary location and change to that directory. 29 | 30 | B. Execute the command: 31 | 32 | python setup.py install [options] 33 | 34 | If you have full administrator privileges, and install to the default location, you do 35 | not need to specify any options. Otherwise, use --user to install to your home folder, or 36 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 37 | the PYTHONPATH environment variable. For details, refer to: 38 | 39 | https://docs.python.org/2/install/index.html 40 | 41 | C. Set environment variables as follows: 42 | 43 | In the following directions, replace MR/v96 by the directory on the target machine where MATLAB is installed, or MR by the directory where the MATLAB Runtime is installed. 44 | 45 | (1) Set the environment variable XAPPLRESDIR to this value: 46 | 47 | MR/v96/X11/app-defaults 48 | 49 | 50 | (2) If the environment variable LD_LIBRARY_PATH is undefined, set it to the following: 51 | 52 | MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 53 | 54 | If it is defined, set it to the following: 55 | 56 | ${LD_LIBRARY_PATH}:MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 57 | 58 | 3. Using the bispecWavPython Package 59 | 60 | The bispecWavPython package is on your Python path. To import it into a Python script or 61 | session, execute: 62 | 63 | import bispecWavPython 64 | 65 | If a namespace must be specified for the package, modify the import statement accordingly. 66 | -------------------------------------------------------------------------------- /packages/bispecWavPython/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 35119 35000 -------------------------------------------------------------------------------- /packages/bispecWavPython/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'bispecWavPython' 28 | ], 29 | package_data={'bispecWavPython': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ecurve/for_redistribution/MyAppInstaller_web.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ecurve/for_redistribution/MyAppInstaller_web.exe -------------------------------------------------------------------------------- /packages/ecurve/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ecurve/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/ecurve/for_redistribution_files_only/ecurve/ecurve.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ecurve/for_redistribution_files_only/ecurve/ecurve.ctf -------------------------------------------------------------------------------- /packages/ecurve/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'ecurve' 28 | ], 29 | package_data={'ecurve': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ecurve/for_testing/ecurve/ecurve.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ecurve/for_testing/ecurve/ecurve.ctf -------------------------------------------------------------------------------- /packages/ecurve/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | ecurve MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | NOTE: You will need administrator rights to run the MATLAB Runtime installer. 13 | 14 | Alternatively, download and install the Windows version of the MATLAB Runtime for R2019a 15 | from the following link on the MathWorks website: 16 | 17 | http://www.mathworks.com/products/compiler/mcr/index.html 18 | 19 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 20 | "Distribute Applications" in the MATLAB Compiler SDK documentation 21 | in the MathWorks Documentation Center. 22 | 23 | Verify that a Windows version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 24 | 25 | 2. Installing the ecurve Package 26 | 27 | A. Change to the directory that contains the file setup.py and the subdirectory ecurve. 28 | If you do not have write permissions, copy all its contents to a temporary location and 29 | change to that directory. 30 | 31 | B. Execute the command: 32 | 33 | python setup.py install [options] 34 | 35 | If you have full administrator privileges, and install to the default location, you do 36 | not need to specify any options. Otherwise, use --user to install to your home folder, or 37 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 38 | the PYTHONPATH environment variable. For details, refer to: 39 | 40 | https://docs.python.org/2/install/index.html 41 | 42 | 43 | 3. Using the ecurve Package 44 | 45 | The ecurve package is on your Python path. To import it into a Python script or session, 46 | execute: 47 | 48 | import ecurve 49 | 50 | If a namespace must be specified for the package, modify the import statement accordingly. 51 | -------------------------------------------------------------------------------- /packages/ecurve/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 35000 -------------------------------------------------------------------------------- /packages/ecurve/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'ecurve' 28 | ], 29 | package_data={'ecurve': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/full_bayesian/for_redistribution/MyAppInstaller_web.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/full_bayesian/for_redistribution/MyAppInstaller_web.exe -------------------------------------------------------------------------------- /packages/full_bayesian/for_redistribution_files_only/full_bayesian/full_bayesian.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/full_bayesian/for_redistribution_files_only/full_bayesian/full_bayesian.ctf -------------------------------------------------------------------------------- /packages/full_bayesian/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'full_bayesian' 28 | ], 29 | package_data={'full_bayesian': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/full_bayesian/for_testing/full_bayesian/full_bayesian.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/full_bayesian/for_testing/full_bayesian/full_bayesian.ctf -------------------------------------------------------------------------------- /packages/full_bayesian/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | full_bayesian MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | NOTE: You will need administrator rights to run the MATLAB Runtime installer. 13 | 14 | Alternatively, download and install the Windows version of the MATLAB Runtime for R2019a 15 | from the following link on the MathWorks website: 16 | 17 | http://www.mathworks.com/products/compiler/mcr/index.html 18 | 19 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 20 | "Distribute Applications" in the MATLAB Compiler SDK documentation 21 | in the MathWorks Documentation Center. 22 | 23 | Verify that a Windows version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 24 | 25 | 2. Installing the full_bayesian Package 26 | 27 | A. Change to the directory that contains the file setup.py and the subdirectory 28 | full_bayesian. If you do not have write permissions, copy all its contents to a temporary 29 | location and change to that directory. 30 | 31 | B. Execute the command: 32 | 33 | python setup.py install [options] 34 | 35 | If you have full administrator privileges, and install to the default location, you do 36 | not need to specify any options. Otherwise, use --user to install to your home folder, or 37 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 38 | the PYTHONPATH environment variable. For details, refer to: 39 | 40 | https://docs.python.org/2/install/index.html 41 | 42 | 43 | 3. Using the full_bayesian Package 44 | 45 | The full_bayesian package is on your Python path. To import it into a Python script or 46 | session, execute: 47 | 48 | import full_bayesian 49 | 50 | If a namespace must be specified for the package, modify the import statement accordingly. 51 | -------------------------------------------------------------------------------- /packages/full_bayesian/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35000 35010 -------------------------------------------------------------------------------- /packages/full_bayesian/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'full_bayesian' 28 | ], 29 | package_data={'full_bayesian': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/rectfr/for_redistribution/MyAppInstaller_web.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/rectfr/for_redistribution/MyAppInstaller_web.exe -------------------------------------------------------------------------------- /packages/rectfr/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/rectfr/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/rectfr/for_redistribution_files_only/rectfr/rectfr.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/rectfr/for_redistribution_files_only/rectfr/rectfr.ctf -------------------------------------------------------------------------------- /packages/rectfr/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'rectfr' 28 | ], 29 | package_data={'rectfr': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/rectfr/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | rectfr MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | NOTE: You will need administrator rights to run the MATLAB Runtime installer. 13 | 14 | Alternatively, download and install the Windows version of the MATLAB Runtime for R2019a 15 | from the following link on the MathWorks website: 16 | 17 | http://www.mathworks.com/products/compiler/mcr/index.html 18 | 19 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 20 | "Distribute Applications" in the MATLAB Compiler SDK documentation 21 | in the MathWorks Documentation Center. 22 | 23 | Verify that a Windows version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 24 | 25 | 2. Installing the rectfr Package 26 | 27 | A. Change to the directory that contains the file setup.py and the subdirectory rectfr. 28 | If you do not have write permissions, copy all its contents to a temporary location and 29 | change to that directory. 30 | 31 | B. Execute the command: 32 | 33 | python setup.py install [options] 34 | 35 | If you have full administrator privileges, and install to the default location, you do 36 | not need to specify any options. Otherwise, use --user to install to your home folder, or 37 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 38 | the PYTHONPATH environment variable. For details, refer to: 39 | 40 | https://docs.python.org/2/install/index.html 41 | 42 | 43 | 3. Using the rectfr Package 44 | 45 | The rectfr package is on your Python path. To import it into a Python script or session, 46 | execute: 47 | 48 | import rectfr 49 | 50 | If a namespace must be specified for the package, modify the import statement accordingly. 51 | -------------------------------------------------------------------------------- /packages/rectfr/for_testing/rectfr/rectfr.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/rectfr/for_testing/rectfr/rectfr.ctf -------------------------------------------------------------------------------- /packages/rectfr/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 -------------------------------------------------------------------------------- /packages/rectfr/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'rectfr' 28 | ], 29 | package_data={'rectfr': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ridge_extraction/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ridge_extraction/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/ridge_extraction/for_redistribution_files_only/ridge_extraction/ridge_extraction.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ridge_extraction/for_redistribution_files_only/ridge_extraction/ridge_extraction.ctf -------------------------------------------------------------------------------- /packages/ridge_extraction/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'ridge_extraction' 28 | ], 29 | package_data={'ridge_extraction': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ridge_extraction/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | ridge_extraction MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | 13 | Alternatively, download and install the Linux version of the MATLAB Runtime for R2019a 14 | from the following link on the MathWorks website: 15 | 16 | http://www.mathworks.com/products/compiler/mcr/index.html 17 | 18 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 19 | "Distribute Applications" in the MATLAB Compiler SDK documentation 20 | in the MathWorks Documentation Center. 21 | 22 | Verify that a Linux version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 23 | 24 | 2. Installing the ridge_extraction Package 25 | 26 | A. Change to the directory that contains the file setup.py and the subdirectory 27 | ridge_extraction. If you do not have write permissions, copy all its contents to a 28 | temporary location and change to that directory. 29 | 30 | B. Execute the command: 31 | 32 | python setup.py install [options] 33 | 34 | If you have full administrator privileges, and install to the default location, you do 35 | not need to specify any options. Otherwise, use --user to install to your home folder, or 36 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 37 | the PYTHONPATH environment variable. For details, refer to: 38 | 39 | https://docs.python.org/2/install/index.html 40 | 41 | C. Set environment variables as follows: 42 | 43 | In the following directions, replace MR/v96 by the directory on the target machine where MATLAB is installed, or MR by the directory where the MATLAB Runtime is installed. 44 | 45 | (1) Set the environment variable XAPPLRESDIR to this value: 46 | 47 | MR/v96/X11/app-defaults 48 | 49 | 50 | (2) If the environment variable LD_LIBRARY_PATH is undefined, set it to the following: 51 | 52 | MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 53 | 54 | If it is defined, set it to the following: 55 | 56 | ${LD_LIBRARY_PATH}:MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 57 | 58 | 3. Using the ridge_extraction Package 59 | 60 | The ridge_extraction package is on your Python path. To import it into a Python script or 61 | session, execute: 62 | 63 | import ridge_extraction 64 | 65 | If a namespace must be specified for the package, modify the import statement accordingly. 66 | -------------------------------------------------------------------------------- /packages/ridge_extraction/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 35000 -------------------------------------------------------------------------------- /packages/ridge_extraction/for_testing/ridge_extraction/ridge_extraction.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/ridge_extraction/for_testing/ridge_extraction/ridge_extraction.ctf -------------------------------------------------------------------------------- /packages/ridge_extraction/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'ridge_extraction' 28 | ], 29 | package_data={'ridge_extraction': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/wavsurrogate/for_redistribution/MyAppInstaller_web.install: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/wavsurrogate/for_redistribution/MyAppInstaller_web.install -------------------------------------------------------------------------------- /packages/wavsurrogate/for_redistribution_files_only/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'wavsurrogate' 28 | ], 29 | package_data={'wavsurrogate': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/wavsurrogate/for_redistribution_files_only/wavsurrogate/wavsurrogate.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/wavsurrogate/for_redistribution_files_only/wavsurrogate/wavsurrogate.ctf -------------------------------------------------------------------------------- /packages/wavsurrogate/for_testing/readme.txt: -------------------------------------------------------------------------------- 1 | wavsurrogate MATLAB Python Package 2 | 3 | 1. Prerequisites for Deployment 4 | 5 | Verify that version 9.6 (R2019a) of the MATLAB Runtime is installed. 6 | If not, you can run the MATLAB Runtime installer. 7 | To find its location, enter 8 | 9 | >>mcrinstaller 10 | 11 | at the MATLAB prompt. 12 | 13 | Alternatively, download and install the Linux version of the MATLAB Runtime for R2019a 14 | from the following link on the MathWorks website: 15 | 16 | http://www.mathworks.com/products/compiler/mcr/index.html 17 | 18 | For more information about the MATLAB Runtime and the MATLAB Runtime installer, see 19 | "Distribute Applications" in the MATLAB Compiler SDK documentation 20 | in the MathWorks Documentation Center. 21 | 22 | Verify that a Linux version of Python 2.7, 3.5, 3.6, and/or 3.7 is installed. 23 | 24 | 2. Installing the wavsurrogate Package 25 | 26 | A. Change to the directory that contains the file setup.py and the subdirectory 27 | wavsurrogate. If you do not have write permissions, copy all its contents to a temporary 28 | location and change to that directory. 29 | 30 | B. Execute the command: 31 | 32 | python setup.py install [options] 33 | 34 | If you have full administrator privileges, and install to the default location, you do 35 | not need to specify any options. Otherwise, use --user to install to your home folder, or 36 | --prefix="installdir" to install to "installdir". In the latter case, add "installdir" to 37 | the PYTHONPATH environment variable. For details, refer to: 38 | 39 | https://docs.python.org/2/install/index.html 40 | 41 | C. Set environment variables as follows: 42 | 43 | In the following directions, replace MR/v96 by the directory on the target machine where MATLAB is installed, or MR by the directory where the MATLAB Runtime is installed. 44 | 45 | (1) Set the environment variable XAPPLRESDIR to this value: 46 | 47 | MR/v96/X11/app-defaults 48 | 49 | 50 | (2) If the environment variable LD_LIBRARY_PATH is undefined, set it to the following: 51 | 52 | MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 53 | 54 | If it is defined, set it to the following: 55 | 56 | ${LD_LIBRARY_PATH}:MR/v96/runtime/glnxa64:MR/v96/bin/glnxa64:MR/v96/sys/os/glnxa64:MR/v96/sys/opengl/lib/glnxa64 57 | 58 | 3. Using the wavsurrogate Package 59 | 60 | The wavsurrogate package is on your Python path. To import it into a Python script or 61 | session, execute: 62 | 63 | import wavsurrogate 64 | 65 | If a namespace must be specified for the package, modify the import statement accordingly. 66 | -------------------------------------------------------------------------------- /packages/wavsurrogate/for_testing/requiredMCRProducts.txt: -------------------------------------------------------------------------------- 1 | 35010 35000 -------------------------------------------------------------------------------- /packages/wavsurrogate/for_testing/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 The MathWorks, Inc. 2 | 3 | from distutils.core import setup 4 | from distutils.command.clean import clean 5 | from distutils.command.install import install 6 | 7 | class InstallRuntime(install): 8 | # Calls the default run command, then deletes the build area 9 | # (equivalent to "setup clean --all"). 10 | def run(self): 11 | install.run(self) 12 | c = clean(self.distribution) 13 | c.all = True 14 | c.finalize_options() 15 | c.run() 16 | 17 | if __name__ == '__main__': 18 | 19 | setup( 20 | name="matlabruntimeforpython", 21 | version="R2019a", 22 | description='A module to call MATLAB from Python', 23 | author='MathWorks', 24 | url='https://www.mathworks.com/', 25 | platforms=['Linux', 'Windows', 'MacOS'], 26 | packages=[ 27 | 'wavsurrogate' 28 | ], 29 | package_data={'wavsurrogate': ['*.ctf']}, 30 | # Executes the custom code above in order to delete the build area. 31 | cmdclass={'install': InstallRuntime} 32 | ) 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/wavsurrogate/for_testing/wavsurrogate/wavsurrogate.ctf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/packages/wavsurrogate/for_testing/wavsurrogate/wavsurrogate.ctf -------------------------------------------------------------------------------- /publish_update.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python script which automates the task of publishing an update to PyMODA. 3 | 4 | Example usage: 5 | - python publish_update.py v0.6.0 6 | - python publish_update.py v0.6.0 --push 7 | """ 8 | import os 9 | import sys 10 | from typing import List 11 | 12 | os.chdir(os.path.abspath(os.path.dirname(__file__))) 13 | args = sys.argv 14 | 15 | if len(args) <= 1: 16 | print("Please supply the new version tag as a command-line argument.") 17 | sys.exit(0) 18 | 19 | 20 | tag = sys.argv[1] 21 | if not tag.startswith("v"): 22 | tag = f"v{tag}" 23 | 24 | push = any([a in ["push", "--push"] for a in args[1:]]) 25 | 26 | 27 | def replace_version(lines: List[str]) -> List[str]: 28 | out = [] 29 | 30 | for l in lines: 31 | if not l.startswith("__version__"): 32 | out.append(l) 33 | else: 34 | out.append(f"__version__ = \"{tag.replace('v', '')}\"\n") 35 | 36 | return out 37 | 38 | 39 | with open("src/main.py", "r") as f: 40 | lines = f.readlines() 41 | 42 | with open("src/main.py", "w") as f: 43 | f.writelines(replace_version(lines)) 44 | 45 | # Run twice to ensure that pre-commit hooks are satisfied. 46 | for _ in range(2): 47 | os.system(f"git add src/main.py") 48 | os.system(f"git commit -m \"Bump version to {tag}\"") 49 | 50 | print(f"Tagging release as '{tag}'.") 51 | os.system(f"git tag {tag}") 52 | 53 | if push: 54 | os.system("git push -u") 55 | os.system(f"git push origin {tag}") 56 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.6.2 2 | AsyncProcessScheduler==0.9.0b1 3 | dataclasses==0.6 4 | EasySettings==4.0.0 5 | matplotlib==3.1.1 6 | multiprocess==0.70.8 7 | numpy==1.18.3 8 | pre-commit==1.18.3 9 | PyGithub==1.46 10 | PyMODAlib==0.11.2b1 11 | PyOpenGL==3.1.0 12 | PyQt5==5.14.2 13 | PyQt5-sip==12.7.0 14 | PyQt5-stubs==5.14.2 15 | PyQtGraph==0.10.0 16 | scipy==1.4.1 17 | psutil==5.7.0 18 | qasync==0.9.4 -------------------------------------------------------------------------------- /res/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Table of Contents 4 | 5 | - [res](#res) 6 | 7 | 8 | 9 | ## res 10 | 11 | The `res` folder contains the resources used by PyMODA. 12 | 13 | | Subfolder | Contents | 14 | | ----- | ----- | 15 | | `colours` | Colourmaps | 16 | | `img` | Images | 17 | | `layout` | Layout files which are produced by Qt Designer | 18 | | `data` | Data files which are used to test the program | -------------------------------------------------------------------------------- /res/data/butter.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/butter.mat -------------------------------------------------------------------------------- /res/data/mat/1signal_10Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/1signal_10Hz.mat -------------------------------------------------------------------------------- /res/data/mat/2signals_10Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/2signals_10Hz.mat -------------------------------------------------------------------------------- /res/data/mat/6signals_10Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/6signals_10Hz.mat -------------------------------------------------------------------------------- /res/data/mat/IEEEsigs/IEEEex_10Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/IEEEsigs/IEEEex_10Hz.mat -------------------------------------------------------------------------------- /res/data/mat/IEEEsigs/xp_100Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/IEEEsigs/xp_100Hz.mat -------------------------------------------------------------------------------- /res/data/mat/IEEEsigs/xp_10Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/IEEEsigs/xp_10Hz.mat -------------------------------------------------------------------------------- /res/data/mat/fm_square_200Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/fm_square_200Hz.mat -------------------------------------------------------------------------------- /res/data/mat/freq_mod_200Hz.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/freq_mod_200Hz.mat -------------------------------------------------------------------------------- /res/data/mat/group_coherence/group1_Cphd.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/group_coherence/group1_Cphd.mat -------------------------------------------------------------------------------- /res/data/mat/group_coherence/group2_phd.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/group_coherence/group2_phd.mat -------------------------------------------------------------------------------- /res/data/mat/load_first.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/load_first.mat -------------------------------------------------------------------------------- /res/data/mat/load_second.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/load_second.mat -------------------------------------------------------------------------------- /res/data/mat/noisy.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/noisy.mat -------------------------------------------------------------------------------- /res/data/mat/noisySIN.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/noisySIN.mat -------------------------------------------------------------------------------- /res/data/mat/noisyyy.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/mat/noisyyy.mat -------------------------------------------------------------------------------- /res/data/npy/1signal_10Hz.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/1signal_10Hz.npy -------------------------------------------------------------------------------- /res/data/npy/2signals_10Hz.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/2signals_10Hz.npy -------------------------------------------------------------------------------- /res/data/npy/6signals_10Hz.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/6signals_10Hz.npy -------------------------------------------------------------------------------- /res/data/npy/fm_square_200Hz.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/fm_square_200Hz.npy -------------------------------------------------------------------------------- /res/data/npy/freq_mod_200Hz.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/freq_mod_200Hz.npy -------------------------------------------------------------------------------- /res/data/npy/load_first.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/load_first.npy -------------------------------------------------------------------------------- /res/data/npy/load_second.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/load_second.npy -------------------------------------------------------------------------------- /res/data/npy/noisy.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/noisy.npy -------------------------------------------------------------------------------- /res/data/npy/noisySIN.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/noisySIN.npy -------------------------------------------------------------------------------- /res/data/npy/noisyyy.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/data/npy/noisyyy.npy -------------------------------------------------------------------------------- /res/img/physicslogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luphysics/PyMODA/ee648c5ca39f1326a8fb6977f793c407e30e388f/res/img/physicslogo.png -------------------------------------------------------------------------------- /res/layout/dialog_matlab_runtime.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 342 10 | 273 11 | 12 | 13 | 14 | MATLAB Runtime 15 | 16 | 17 | 18 | 19 | 20 | 21 | 16777215 22 | 16777215 23 | 24 | 25 | 26 | <html><head/><body><p>The MATLAB Runtime was not found. </p><p>Please specify the LD_LIBRARY_PATH using the &quot;runtime&quot; command-line argument, but do not add it to the environment variables. See the documentation for more details.</p><p>If this is message is in error, click &quot;Don't show again&quot;. </p></body></html> 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | Don't show again 37 | 38 | 39 | 40 | 41 | 42 | 43 | Qt::Horizontal 44 | 45 | 46 | QDialogButtonBox::Ok 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | buttonBox 56 | accepted() 57 | Dialog 58 | accept() 59 | 60 | 61 | 248 62 | 254 63 | 64 | 65 | 157 66 | 274 67 | 68 | 69 | 70 | 71 | buttonBox 72 | rejected() 73 | Dialog 74 | reject() 75 | 76 | 77 | 316 78 | 260 79 | 80 | 81 | 286 82 | 274 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Table of Contents 4 | 5 | - [src](#src) 6 | 7 | 8 | 9 | ## src 10 | 11 | The `src` folder is the folder containing PyMODA's source code. 12 | 13 | ### main.py 14 | 15 | `main.py` is the entry-point of PyMODA. 16 | 17 | ### Subfolders 18 | 19 | | Subfolder | Contents | 20 | | ----- | ----- | 21 | | `data` | Code related to loading data from files | 22 | | `gui` | Code related to the GUI | 23 | | `maths` | Code related to calculations, e.g. the algorithms used by PyMODA | 24 | | `processes` | Code related to multiprocessing, such as the `Scheduler` class | 25 | | `updater` | Code dedicated to checking for, and applying, updates | 26 | | `utils` | Utility code, e.g. error handling, user settings and command-line argument parsing | -------------------------------------------------------------------------------- /src/data/parsing/BaseParser.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from abc import ABC, abstractmethod 17 | from typing import List 18 | 19 | from numpy import ndarray 20 | 21 | 22 | class BaseParser(ABC): 23 | """ 24 | A base parser. The parser is intended to load data from a file. 25 | """ 26 | 27 | def __init__(self, filename: str): 28 | self.filename: str = filename 29 | 30 | @abstractmethod 31 | def parse(self) -> List[ndarray]: 32 | pass 33 | -------------------------------------------------------------------------------- /src/data/parsing/CsvParser.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import List 17 | 18 | from data.parsing import parsing 19 | from data.parsing.BaseParser import BaseParser 20 | 21 | 22 | class CsvParser(BaseParser): 23 | """ 24 | A class which can parse CSV data (comma-separated-values), 25 | either row-wise or column-wise. 26 | """ 27 | 28 | def parse(self) -> List[List[float]]: 29 | """ 30 | Parses the file, returning a list whose items are a list of 31 | the values in every signal respectively. 32 | """ 33 | lines = parsing.get_lines(self.filename) 34 | 35 | if not lines: 36 | return [[]] 37 | 38 | # If each line has more values than the number of lines, 39 | # then each line corresponds to a separate signal. 40 | row_wise = len(lines) < len(lines[0].split(",")) 41 | if row_wise: 42 | signal_count = len(lines) 43 | else: 44 | signal_count = len(lines[0].split(",")) 45 | 46 | # List containing a list of data for each signal. 47 | data = [[] for _ in range(signal_count)] 48 | 49 | index = 0 50 | for l in lines: 51 | split = l.split(",") 52 | 53 | for i in range(len(split)): 54 | value = float(split[i]) 55 | if row_wise: 56 | data[index].append(value) 57 | else: 58 | data[i].append(value) 59 | 60 | if row_wise: 61 | index += 1 62 | 63 | return data 64 | -------------------------------------------------------------------------------- /src/data/parsing/MatParser.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import List 17 | 18 | from numpy import ndarray 19 | from scipy.io import loadmat 20 | 21 | from data.parsing.BaseParser import BaseParser 22 | 23 | 24 | class MatParser(BaseParser): 25 | """ 26 | A parser which loads data from a .mat file. 27 | """ 28 | 29 | def parse(self) -> ndarray: 30 | from data.parsing.parsing import ParsingException 31 | 32 | data: dict = loadmat(self.filename) 33 | arrays: List[ndarray] = list( 34 | filter(lambda i: isinstance(i, ndarray), data.values()) 35 | ) 36 | 37 | if len(arrays) > 1: 38 | raise ParsingException( 39 | ".mat file should contain only one array. To load multiple signals," 40 | " each signal should be a row or column in the array." 41 | ) 42 | elif len(arrays) == 0: 43 | raise ParsingException("No arrays were found in the .mat file.") 44 | else: 45 | signals: ndarray = arrays[0] 46 | 47 | rows, cols = signals.shape 48 | row_wise = rows < cols 49 | 50 | if not row_wise: 51 | signals = signals.T # Transpose signals. 52 | 53 | return signals 54 | -------------------------------------------------------------------------------- /src/data/parsing/NpyParser.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | import numpy as np 18 | from numpy import ndarray 19 | 20 | from data.parsing.BaseParser import BaseParser 21 | 22 | 23 | class NpyParser(BaseParser): 24 | """ 25 | A parser which loads data from a .npy file. 26 | """ 27 | 28 | def parse(self) -> ndarray: 29 | signals: ndarray = np.load(self.filename) 30 | 31 | rows, cols = signals.shape 32 | row_wise = rows < cols 33 | 34 | if not row_wise: 35 | signals = signals.T # Transpose signals. 36 | 37 | return signals 38 | -------------------------------------------------------------------------------- /src/data/parsing/groups/GroupMatParser.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Dict 17 | 18 | import numpy as np 19 | from numpy import ndarray 20 | from scipy.io import loadmat 21 | 22 | from data.parsing.MatParser import MatParser 23 | 24 | 25 | class GroupMatParser(MatParser): 26 | """ 27 | Parser which loads group data from a .mat file. 28 | """ 29 | 30 | def parse(self) -> ndarray: 31 | from data.parsing.parsing import ParsingException 32 | 33 | data: Dict = loadmat(self.filename) 34 | arrays = list(filter(lambda i: isinstance(i, ndarray), data.values())) 35 | 36 | if len(arrays) > 1: 37 | raise ParsingException( 38 | f"Data files containing a signal group should only " 39 | f"contain 1 (3-dimensional) array: {self.filename}." 40 | ) 41 | 42 | group: ndarray = arrays[0] 43 | x, y, z = group.shape 44 | 45 | if z == 2 and x != 2: 46 | out = np.empty((x, y, z,)) 47 | out[0, :, :] = out[:, :, 0] 48 | out[1, :, :] = out[:, :, 1] 49 | else: 50 | out = group 51 | 52 | return out 53 | -------------------------------------------------------------------------------- /src/data/parsing/groups/GroupNpyParser.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | import numpy as np 18 | from numpy import ndarray 19 | 20 | from data.parsing.BaseParser import BaseParser 21 | 22 | 23 | class GroupNpyParser(BaseParser): 24 | """ 25 | A parser which loads group data from a .npy file. 26 | """ 27 | 28 | def parse(self) -> ndarray: 29 | group: ndarray = np.load(self.filename) 30 | x, y, z = group.shape 31 | 32 | if z == 2 and x != 2: 33 | out = np.empty((x, y, z,)) 34 | out[0, :, :] = out[:, :, 0] 35 | out[1, :, :] = out[:, :, 1] 36 | else: 37 | out = group 38 | 39 | return out 40 | -------------------------------------------------------------------------------- /src/data/parsing/parsing.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import os 17 | 18 | from data.parsing.BaseParser import BaseParser 19 | from data.parsing.CsvParser import CsvParser 20 | from data.parsing.MatParser import MatParser 21 | from data.parsing.NpyParser import NpyParser 22 | from data.parsing.groups.GroupMatParser import GroupMatParser 23 | from data.parsing.groups.GroupNpyParser import GroupNpyParser 24 | 25 | 26 | def get_lines(filename): 27 | lines = [] 28 | try: 29 | with open(filename, mode="r", encoding="utf-8-sig") as f: 30 | for line in f: 31 | lines.append(line) 32 | except FileNotFoundError: 33 | print(f"File not found at path: '{filename}'") 34 | raise ParsingException(f"File does not exist: {filename}") 35 | 36 | return lines 37 | 38 | 39 | def get_parser(filename, groups=False) -> BaseParser: 40 | """ 41 | Gets the appropriate parser for a given file. 42 | 43 | Parameters 44 | ---------- 45 | filename : str 46 | The name of the file which will be parsed. 47 | groups : Optional[bool] 48 | (Default = False) Whether the parser is intended to load a signal group, as used by group phase coherence. 49 | """ 50 | _, extension = os.path.splitext(filename) 51 | extension = extension.lower() 52 | 53 | if extension == ".mat": 54 | return MatParser(filename) if not groups else GroupMatParser(filename) 55 | elif extension == ".csv" or extension == ".txt": 56 | return CsvParser(filename) 57 | elif extension == ".npy": 58 | return NpyParser(filename) if not groups else GroupNpyParser(filename) 59 | 60 | raise ParsingException(f"Cannot parse a file with the extension: {extension}") 61 | 62 | 63 | class ParsingException(Exception): 64 | """ 65 | Exception raised when errors are encountered during parsing. 66 | """ 67 | -------------------------------------------------------------------------------- /src/gui/BaseUI.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | class BaseUI: 19 | """ 20 | A base UI element which defines the function 'setup_ui', which is used 21 | to setup UI elements and perform tasks such as inflating the layout from 22 | a .ui file. 23 | 24 | `setup_ui` is called in the constructor. 25 | """ 26 | 27 | def __init__(self): 28 | self.setup_ui() 29 | 30 | def setup_ui(self) -> None: 31 | """ 32 | A function which should be overridden and used to setup 33 | UI components. 34 | """ 35 | pass 36 | -------------------------------------------------------------------------------- /src/gui/components/DualSignalComponent.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Tuple 17 | 18 | from gui.plotting.plots.SignalPlot import SignalPlot 19 | from maths.signals.TimeSeries import TimeSeries 20 | 21 | 22 | class DualSignalComponent: 23 | """ 24 | Component used in windows which have two signals plotted 25 | simultaneously. 26 | """ 27 | 28 | def __init__(self, signal_plot: SignalPlot): 29 | self._signal_plot: SignalPlot = signal_plot 30 | 31 | def plot_signal_pair(self, pair: Tuple[TimeSeries, TimeSeries]): 32 | """Plots a pair of signals on the SignalPlot.""" 33 | sig1, sig2 = pair 34 | self._signal_plot.plot(sig1, clear=True) 35 | self._signal_plot.plot(sig2, clear=False) 36 | -------------------------------------------------------------------------------- /src/gui/components/FreqComponent.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Optional 17 | 18 | from PyQt5.QtWidgets import QLineEdit 19 | 20 | from utils.decorators import floaty 21 | 22 | 23 | class FreqComponent: 24 | """ 25 | A component which handles the 3 frequency-related QLineEdits: fmin, fmax and resolution. 26 | """ 27 | 28 | def __init__( 29 | self, 30 | lineedit_fmax: QLineEdit, 31 | lineedit_fmin: QLineEdit, 32 | lineedit_res: QLineEdit, 33 | ): 34 | self._res = lineedit_res 35 | self._fmin = lineedit_fmin 36 | self._fmax = lineedit_fmax 37 | 38 | def get_fmin(self) -> Optional[float]: 39 | fmin, fmax = self.__fmin(), self.__fmax() 40 | if fmin is not None and fmax is not None: 41 | if fmin > fmax: 42 | fmin, fmax = fmax, fmin 43 | 44 | return fmin 45 | 46 | def get_fmax(self) -> Optional[float]: 47 | fmin, fmax = self.__fmin(), self.__fmax() 48 | if fmin is not None and fmax is not None: 49 | if fmin > fmax: 50 | fmin, fmax = fmax, fmin 51 | 52 | return fmax 53 | 54 | @floaty 55 | def __fmin(self) -> Optional[float]: 56 | return self._fmin.text() 57 | 58 | @floaty 59 | def __fmax(self) -> Optional[float]: 60 | return self._fmax.text() 61 | 62 | @floaty 63 | def get_f0(self) -> Optional[float]: 64 | return self._res.text() 65 | -------------------------------------------------------------------------------- /src/gui/components/PreprocessComponent.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.plotting.plots.PreprocessPlot import PreprocessPlot 17 | 18 | 19 | class PreprocessComponent: 20 | """ 21 | Component which handles the plot of the preprocessed signal on a PreprocessPlot. 22 | """ 23 | 24 | def __init__(self, preproc_plot: PreprocessPlot): 25 | self._preproc_plot = preproc_plot 26 | 27 | def plot_preprocessed_signal(self, times, signal, preproc_signal): 28 | self._preproc_plot.plot(times, signal, preproc_signal) 29 | -------------------------------------------------------------------------------- /src/gui/components/SingleSignalComponent.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.plotting.plots.SignalPlot import SignalPlot 17 | from maths.signals.TimeSeries import TimeSeries 18 | 19 | 20 | class SingleSignalComponent: 21 | """ 22 | Component used in windows which have a single signal plotted 23 | simultaneously. 24 | """ 25 | 26 | def __init__(self, signal_plot: SignalPlot): 27 | self._signal_plot: SignalPlot = signal_plot 28 | 29 | def plot_signal(self, signal: TimeSeries): 30 | """Plots a signal on the SignalPlot.""" 31 | self._signal_plot.plot(signal) 32 | -------------------------------------------------------------------------------- /src/gui/components/VerticalMultiPlotComponent.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5 import sip 17 | from PyQt5.QtWidgets import QVBoxLayout 18 | 19 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 20 | 21 | 22 | class VerticalMultiPlotComponent: 23 | """ 24 | Component which handles a vertical layout containing a variable number of plots. 25 | """ 26 | 27 | def __init__(self, container: QVBoxLayout): 28 | self._container = container 29 | self._plots = [] 30 | 31 | def vplot_remove_plots(self, *plots: MatplotlibWidget): 32 | for plot in plots: 33 | if plot in self._plots: 34 | self._plots.remove(plot) 35 | 36 | if plot is not None: 37 | self._container.removeWidget(plot) 38 | plot.deleteLater() 39 | sip.delete(plot) 40 | 41 | def vplot_remove_all_plots(self): 42 | self.vplot_remove_plots(*self._plots) 43 | 44 | def vplot_insert_widget(self, index, plot): 45 | self._container.insertWidget(index, plot) 46 | self._plots.append(plot) 47 | 48 | def vplot_add_plots(self, *plots): 49 | for plot in plots: 50 | if plot: 51 | self._container.addWidget(plot) 52 | self._plots.append(plot) 53 | 54 | def vplot_get_plot(self, index: int) -> MatplotlibWidget: 55 | return self._plots[index] 56 | 57 | def vplot_count(self) -> int: 58 | return len(self._plots) 59 | -------------------------------------------------------------------------------- /src/gui/dialogs/ErrorBox.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QMessageBox 17 | 18 | 19 | class ErrorBox(QMessageBox): 20 | 21 | def __init__(self, exc_type, value, traceback): 22 | super().__init__() 23 | self.setIcon(QMessageBox.Warning) 24 | self.setWindowTitle("Error!") 25 | self.setText(f"{value}") 26 | 27 | self.setDetailedText(f"{value}\n{exc_type}\n{traceback}") 28 | self.exec() 29 | -------------------------------------------------------------------------------- /src/gui/dialogs/MatlabRuntimeDialog.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5 import uic 17 | from PyQt5.QtWidgets import QDialog, QCheckBox 18 | 19 | from data import resources 20 | from gui.BaseUI import BaseUI 21 | from utils.settings import Settings 22 | 23 | 24 | class MatlabRuntimeDialog(QDialog, BaseUI): 25 | def __init__(self): 26 | self.dont_show_again = False 27 | super(MatlabRuntimeDialog, self).__init__() 28 | 29 | def setup_ui(self) -> None: 30 | uic.loadUi(resources.get("layout:dialog_matlab_runtime.ui"), self) 31 | 32 | checkbox: QCheckBox = self.checkbox_dont_show_again 33 | checkbox.stateChanged.connect(self.on_check) 34 | 35 | def on_check(self, state: int): 36 | self.dont_show_again = bool(state) 37 | -------------------------------------------------------------------------------- /src/gui/dialogs/UseShortcutComponent.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Callable 17 | 18 | from PyQt5.QtCore import QObject 19 | from PyQt5.QtGui import QKeySequence 20 | from PyQt5.QtWidgets import QShortcut 21 | 22 | 23 | class UseShortcutComponent: 24 | def __init__( 25 | self, parent: QObject, on_shortcut_activated: Callable, *args, **kwargs 26 | ): 27 | super(UseShortcutComponent, self).__init__(*args, **kwargs) 28 | 29 | self.shortcut = QShortcut(QKeySequence("Ctrl+U"), parent) 30 | self.shortcut.activated.connect(on_shortcut_activated) 31 | -------------------------------------------------------------------------------- /src/gui/dialogs/files/DragDropLabel.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Callable 17 | 18 | from PyQt5 import QtGui 19 | from PyQt5.QtGui import QDropEvent 20 | from PyQt5.QtWidgets import QLabel 21 | 22 | 23 | class DragDropLabel(QLabel): 24 | """ 25 | A label which accepts drag-and-drop events for files. When a drop 26 | succeeds, the filename is shown in the label. The label changes 27 | colour while a drag event is in progress. 28 | """ 29 | 30 | def __init__(self, parent): 31 | self.drop_callback: Callable = None 32 | super().__init__(parent) 33 | 34 | def dropEvent(self, event: QDropEvent) -> None: 35 | """Called when a drop event occurs.""" 36 | file_path = event.mimeData().text().rstrip() 37 | if self.drop_callback: 38 | self.drop_callback(file_path) 39 | 40 | self.show_selected_file(file_path) 41 | 42 | def show_selected_file(self, file_path) -> None: 43 | file_name = file_path.split("/")[-1].split("\\")[-1] 44 | self.setText(f"File selected:\n{file_name}") 45 | self.set_background(highlighted=False) 46 | 47 | def dragLeaveEvent(self, e: QtGui.QDragLeaveEvent) -> None: 48 | self.set_background(highlighted=False) 49 | 50 | def set_background(self, highlighted: bool) -> None: 51 | background = "grey" if highlighted else "transparent" 52 | self.setStyleSheet( 53 | f"QLabel {{ background-color : {background}; color : black; }}" 54 | ) 55 | 56 | def dragEnterEvent(self, event: QtGui.QDragEnterEvent) -> None: 57 | """Called when a drag event enters the label.""" 58 | if event.mimeData().hasText(): 59 | event.accept() 60 | self.set_background(highlighted=True) 61 | else: 62 | event.ignore() 63 | 64 | def set_drop_callback(self, callback: Callable[[str], None]) -> None: 65 | """Sets a callback for when the drop is complete.""" 66 | self.drop_callback = callback 67 | -------------------------------------------------------------------------------- /src/gui/plotting/PlotWidget.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QWidget 17 | 18 | from gui.BaseUI import BaseUI 19 | 20 | 21 | class PlotWidget(QWidget, BaseUI): 22 | """ 23 | A base component for plotting. Should be independent of any 24 | plotting library. 25 | """ 26 | 27 | def get_xlabel(self) -> str: 28 | """Returns the label for the x-axis. Should be overridden in subclasses.""" 29 | pass 30 | 31 | def get_ylabel(self) -> str: 32 | """Returns the label for the y-axis. Should be overridden in subclasses.""" 33 | pass 34 | 35 | def set_in_progress(self, in_progress) -> None: 36 | """Sets the progress bar to display whether the plotting is in progress.""" 37 | pass 38 | 39 | def on_plot_complete(self) -> None: 40 | """ 41 | Should be called after the first plotting is complete. It will then set the initial state 42 | so that the reset button can work. 43 | """ 44 | pass 45 | 46 | def clear(self) -> None: 47 | """ 48 | Clears the plot, removing all plotted elements. 49 | """ 50 | pass 51 | 52 | def update_xlabel(self, text=None) -> None: 53 | """ 54 | Updates the x-label. If no value is passed, 55 | the result from get_xlabel() will be used. 56 | """ 57 | pass 58 | 59 | def update_ylabel(self, text=None) -> None: 60 | """ 61 | Updates the y-label. If no value is passed, 62 | the result from get_ylabel() will be used. 63 | """ 64 | pass 65 | -------------------------------------------------------------------------------- /src/gui/plotting/PyQtGraphWidget.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QVBoxLayout 17 | from pyqtgraph.opengl import GLViewWidget 18 | 19 | from gui.plotting.PlotWidget import PlotWidget 20 | 21 | 22 | class PyQtGraphWidget(PlotWidget): 23 | """ 24 | A widget which enables plotting via PyQtGraph. 25 | 26 | Warning: not fully implemented. Complete implementation is 27 | not currently planned. 28 | """ 29 | 30 | def setup_ui(self) -> None: 31 | self.layout = QVBoxLayout(self) 32 | 33 | self.plot_widget = GLViewWidget() 34 | self.layout.addWidget(self.plot_widget) 35 | -------------------------------------------------------------------------------- /src/gui/plotting/plots/AmplitudePlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 17 | 18 | 19 | class AmplitudePlot(MatplotlibWidget): 20 | 21 | def __init__(self, parent): 22 | self.ylabel = "Frequency (Hz)" 23 | self.xlabel = "" 24 | super(AmplitudePlot, self).__init__(parent) 25 | 26 | def plot(self, amplitude, freq, surrogates=None): 27 | self.clear() 28 | 29 | self.update_ylabel() 30 | self.update_xlabel() 31 | 32 | self.axes.yaxis.set_label_position("right") 33 | 34 | y = freq 35 | ylim = sorted([y[0], y[-1]]) 36 | self.axes.set_ylim(ylim) 37 | 38 | self.axes.plot(amplitude, freq) 39 | if hasattr(surrogates, "__len__") and len(surrogates) == len(amplitude): 40 | self.axes.plot(surrogates, freq) 41 | self.axes.legend(["Original signal", "Surrogate"]) 42 | 43 | self.apply_scale() 44 | self.axes.autoscale(False) 45 | self.on_plot_complete() 46 | 47 | def get_ylabel(self): 48 | return self.ylabel 49 | 50 | def get_xlabel(self): 51 | return self.xlabel 52 | 53 | def set_xlabel(self, text): 54 | self.xlabel = text 55 | 56 | def set_ylabel(self, text): 57 | self.ylabel = text 58 | -------------------------------------------------------------------------------- /src/gui/plotting/plots/GroupCoherencePlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import numpy as np 17 | 18 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 19 | 20 | 21 | class GroupCoherencePlot(MatplotlibWidget): 22 | def __init__(self, parent): 23 | self.xlabel = "Frequency (Hz)" 24 | self.ylabel = "Coherence" 25 | super(GroupCoherencePlot, self).__init__(parent) 26 | 27 | def plot(self, freq, coh1, coh2, average="median", percentile: float = 75): 28 | self.clear() 29 | 30 | if percentile is None or percentile > 100: 31 | return 32 | 33 | self.update_ylabel() 34 | self.update_xlabel() 35 | 36 | single = coh2 is None 37 | if average == "median": 38 | favg = np.nanmedian 39 | average = "Median" 40 | else: 41 | favg = np.nanmean 42 | average = "Mean" 43 | 44 | pc11 = np.nanpercentile(coh1, 100 - percentile, axis=0) 45 | pc12 = np.nanpercentile(coh1, percentile, axis=0) 46 | 47 | if not single: 48 | pc21 = np.nanpercentile(coh2, 100 - percentile, axis=0) 49 | pc22 = np.nanpercentile(coh2, percentile, axis=0) 50 | 51 | color1 = "black" 52 | color2 = "red" 53 | alpha = 0.1 54 | linewidth = 1.1 55 | 56 | self.axes.plot(freq, favg(coh1, axis=0), color=color1, linewidth=linewidth) 57 | self.axes.fill_between(freq, pc11, pc12, color=color1, alpha=alpha) 58 | 59 | legend = [ 60 | f"{average} coherence (group 1)", 61 | ] 62 | 63 | if not single: 64 | self.axes.plot(freq, favg(coh2, axis=0), color=color2, linewidth=linewidth) 65 | self.axes.fill_between(freq, pc21, pc22, color=color2, alpha=alpha) 66 | legend.append(legend[0].replace("1", "2")) 67 | 68 | self.set_log_scale(True, "x") 69 | self.axes.legend(legend) 70 | 71 | self.apply_scale() 72 | self.axes.autoscale(False) 73 | self.on_plot_complete() 74 | 75 | def get_ylabel(self): 76 | return self.ylabel 77 | 78 | def get_xlabel(self): 79 | return self.xlabel 80 | 81 | def set_xlabel(self, text): 82 | self.xlabel = text 83 | 84 | def set_ylabel(self, text): 85 | self.ylabel = text 86 | -------------------------------------------------------------------------------- /src/gui/plotting/plots/PreprocessPlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import numpy as np 17 | 18 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 19 | 20 | 21 | class PreprocessPlot(MatplotlibWidget): 22 | 23 | def plot(self, times: np.ndarray, original: np.ndarray, preprocessed: np.ndarray): 24 | self.clear() 25 | self.rect_stack.clear() 26 | 27 | self.axes.autoscale(True) 28 | 29 | width = 0.7 30 | self.axes.plot(times, original, linewidth=width) 31 | self.axes.plot(times, preprocessed, linewidth=width) 32 | 33 | self.axes.legend(["Original", "Preprocessed"]) 34 | 35 | xlim = sorted((times[0], times[-1],)) 36 | self.axes.set_xlim(xlim) 37 | 38 | self.axes.autoscale(False) 39 | self.on_plot_complete() 40 | -------------------------------------------------------------------------------- /src/gui/plotting/plots/Rect.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | class Rect: 19 | """ 20 | A simple class representing the coordinates of a rectangle 21 | that is drawn in matplotlib. (x1,y1) refer to the upper left 22 | corner, while (x2,y2) are the lower right corner. 23 | """ 24 | 25 | def __init__(self, x1, y1, x2=None, y2=None): 26 | self.x1 = x1 27 | self.x2 = x2 28 | self.y1 = y1 29 | self.y2 = y2 30 | 31 | def set_corner(self, x2, y2): 32 | """ 33 | Sets the coordinates of the "bottom right" corner; 34 | however, note that the corner can be in any location. 35 | """ 36 | self.x2 = x2 37 | self.y2 = y2 38 | 39 | def sorted(self): 40 | """ 41 | Returns a new Rect instance which is sorted so that the 42 | corner (x1, y1) is in the upper left and the corner 43 | (x2, y2) is in the lower right. 44 | 45 | If the current instance does not contain all four coordinates, 46 | returns the current instance. Does not affect the current instance. 47 | """ 48 | if not self.is_valid(): 49 | return self 50 | 51 | x1 = self.x1 52 | x2 = self.x2 53 | y1 = self.y1 54 | y2 = self.y2 55 | 56 | if x2 < x1: 57 | x1, x2 = x2, x1 # Swap values. 58 | 59 | if y2 < y1: 60 | y1, y2 = y2, y1 # Swap values. 61 | 62 | return Rect(x1, y1, x2, y2) 63 | 64 | def get_width(self): 65 | return self.x2 - self.x1 66 | 67 | def get_height(self): 68 | return self.y2 - self.y1 69 | 70 | def is_valid(self): 71 | """ 72 | Returns whether this is a Rect with both corners defined. 73 | Not affected by whether the corners are correctly sorted or not. 74 | """ 75 | return self.x2 is not None and self.y2 is not None 76 | 77 | def __str__(self) -> str: 78 | return f"{self.x1}, {self.y1}; {self.x2}, {self.y2}" 79 | -------------------------------------------------------------------------------- /src/gui/plotting/plots/SignalPlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from numpy import ndarray 18 | 19 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 20 | from maths.signals.TimeSeries import TimeSeries 21 | 22 | 23 | class SignalPlot(MatplotlibWidget): 24 | """ 25 | Plots the signal, which is a simple set of amplitudes against time. 26 | """ 27 | 28 | def __init__(self, parent): 29 | MatplotlibWidget.__init__(self, parent) 30 | self.toolbar.disable_panning() 31 | 32 | def plotxy(self, x: ndarray, y: ndarray, clear: bool = True) -> None: 33 | if clear: 34 | self.clear() 35 | self.rect_stack.clear() 36 | 37 | self.axes.xaxis.set_label_position("top") 38 | self.update_xlabel() 39 | self.update_ylabel() 40 | self.axes.autoscale(True) 41 | 42 | if len(x.shape) <= 1: 43 | xlim = (x[0], x[-1]) 44 | else: 45 | xlim = (x[0, 0], x[0, -1]) 46 | 47 | self.axes.plot(x, y, linewidth=0.7) 48 | self.axes.autoscale(False) 49 | self.axes.set_xlim(xlim) 50 | self.on_plot_complete() 51 | 52 | def plot(self, data: TimeSeries, clear: bool = True) -> None: 53 | x = data.times 54 | y = data.signal 55 | 56 | self.plotxy(x, y, clear=clear) 57 | 58 | def zoom_to(self, rect, save_state=True, trigger_listeners=True) -> None: 59 | """Override the zoom to not change the range of visible y-values.""" 60 | rect.y1, rect.y2 = self.ylim() 61 | super(SignalPlot, self).zoom_to(rect, save_state, trigger_listeners) 62 | 63 | def get_xlabel(self) -> str: 64 | return "Time (s)" 65 | 66 | def get_ylabel(self) -> str: 67 | return "Value" 68 | -------------------------------------------------------------------------------- /src/gui/windows/BaseWindow.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5 import QtGui 17 | from PyQt5.QtGui import QIcon 18 | from PyQt5.QtWidgets import QMainWindow 19 | 20 | from data import resources 21 | from gui.BaseUI import BaseUI 22 | 23 | 24 | class BaseWindow(QMainWindow, BaseUI): 25 | """ 26 | A base window which inherits from BaseUI. 27 | """ 28 | 29 | def __init__(self, application): 30 | self.application = application 31 | 32 | super(BaseWindow, self).__init__() 33 | self.update_title() 34 | self.set_icon() 35 | 36 | def update_title(self, title=resources.get_program_name()): 37 | """ 38 | Sets the title of the window. If no title is supplied, 39 | the default name of the application is used. 40 | """ 41 | self.setWindowTitle(title) 42 | 43 | def set_icon(self, img=resources.get("image:icon.svg")): 44 | icon = QIcon(img) 45 | self.setWindowIcon(icon) 46 | 47 | def closeEvent(self, e: QtGui.QCloseEvent) -> None: 48 | try: 49 | self.application.notify_close_event(self) 50 | except AttributeError: 51 | pass 52 | super(BaseWindow, self).closeEvent(e) 53 | -------------------------------------------------------------------------------- /src/gui/windows/CentredWindow.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from PyQt5.QtWidgets import QDesktopWidget 18 | 19 | from gui.windows.BaseWindow import BaseWindow 20 | 21 | 22 | class CentredWindow(BaseWindow): 23 | """ 24 | A window which opens at the centre of the screen. 25 | """ 26 | 27 | def __init__(self, application): 28 | super(CentredWindow, self).__init__(application) 29 | self.centre() 30 | 31 | def centre(self): 32 | """Moves the window to the centre of the screen.""" 33 | geometry = self.frameGeometry() 34 | centre = QDesktopWidget().availableGeometry().center() 35 | geometry.moveCenter(centre) 36 | self.move(geometry.topLeft()) 37 | -------------------------------------------------------------------------------- /src/gui/windows/MaximisedWindow.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.windows.CentredWindow import CentredWindow 17 | from utils import args 18 | 19 | 20 | class MaximisedWindow(CentredWindow): 21 | """ 22 | A window which is maximised when it opens, unless overridden 23 | by a command-line argument. 24 | """ 25 | 26 | def __init__(self, application): 27 | super().__init__(application) 28 | if args.maximise(): 29 | self.showMaximized() 30 | -------------------------------------------------------------------------------- /src/gui/windows/ViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | class ViewProperties: 19 | """ 20 | A class which should be overridden to declare the variables which 21 | will be added when the .ui file is inflated to create a window. 22 | 23 | The only purpose of this class is to provide useful code 24 | completion in the IDE. 25 | 26 | All variables should be initialised as None in the constructor 27 | and marked with appropriate type annotations. 28 | 29 | Important: The constructor for a ViewProperties class should be called 30 | before the .ui file is inflated. 31 | """ 32 | -------------------------------------------------------------------------------- /src/gui/windows/bayesian/DBPlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 17 | 18 | 19 | class DBPlot(MatplotlibWidget): 20 | def plot(self, times, values, *args, **kwargs): 21 | self.update_xlabel() 22 | self.update_ylabel() 23 | 24 | self.axes.autoscale(True) 25 | 26 | self.axes.plot(times, values, linewidth=0.8, *args, **kwargs) 27 | self.axes.autoscale(False) 28 | self.axes.set_xlim([times[0], times[-1]]) 29 | self.on_plot_complete() 30 | 31 | def get_xlabel(self): 32 | return "Time (s)" 33 | -------------------------------------------------------------------------------- /src/gui/windows/bayesian/DBPlot3d.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 17 | from gui.plotting.plots.ColorMeshPlot import colormap 18 | 19 | 20 | class DBPlot3d(MatplotlibWidget): 21 | def plot(self, x, y, z): 22 | self.axes.xaxis.set_label_position("top") 23 | self.axes.autoscale(True) 24 | 25 | self.axes.plot_surface(x, y, z, cmap=colormap()) 26 | self.axes.autoscale(False) 27 | 28 | self.axes.invert_xaxis() 29 | self.set_mouse_zoom_enabled(False) 30 | 31 | self.on_plot_complete() 32 | 33 | def is_3d(self) -> bool: 34 | return True 35 | -------------------------------------------------------------------------------- /src/gui/windows/bayesian/ParamSet.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Tuple 17 | 18 | 19 | class ParamSet: 20 | """ 21 | Represents the parameter set used by the dynamical Bayesian inference window. 22 | """ 23 | 24 | def __init__( 25 | self, 26 | freq_range1: Tuple[float, float], 27 | freq_range2: Tuple[float, float], 28 | window: float, 29 | propagation_const: float, 30 | surr_count: int, 31 | overlap: float, 32 | order: int, 33 | confidence_level: float, 34 | ): 35 | self.freq_range1 = freq_range1 36 | self.freq_range2 = freq_range2 37 | self.window = window 38 | self.propagation_const = propagation_const 39 | self.surr_count = surr_count 40 | self.overlap = overlap 41 | self.order = order 42 | self.confidence_level = confidence_level 43 | 44 | def to_string(self) -> Tuple[str, str]: 45 | """ 46 | Returns a string representation of this object for each frequency band. 47 | """ 48 | # Function to use for each frequency band. 49 | def convert(freq: Tuple[float, float]) -> str: 50 | freq_range_str = ",".join([str(i) for i in freq]) 51 | items = [ 52 | freq_range_str, 53 | self.window, 54 | self.overlap, 55 | self.propagation_const, 56 | self.order, 57 | self.surr_count, 58 | self.confidence_level, 59 | ] 60 | 61 | return " | ".join([str(i) for i in items]) 62 | 63 | return convert(self.freq_range1), convert(self.freq_range2) 64 | -------------------------------------------------------------------------------- /src/gui/windows/bispectrum/BAPlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from numpy import ndarray 17 | 18 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 19 | 20 | 21 | class BAPlot(MatplotlibWidget): 22 | 23 | def plot(self, x: ndarray, y: ndarray): 24 | self.clear() 25 | 26 | self.update_xlabel() 27 | self.update_ylabel() 28 | 29 | self.axes.autoscale(True) 30 | 31 | self.axes.plot(x, y, linewidth=0.8) 32 | self.axes.autoscale(False) 33 | self.axes.set_xlim([x[0], x[-1]]) 34 | self.on_plot_complete() 35 | -------------------------------------------------------------------------------- /src/gui/windows/bispectrum/BAViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import ( 17 | QLineEdit, 18 | QSlider, 19 | QComboBox, 20 | QVBoxLayout, 21 | QGridLayout, 22 | QPushButton, 23 | QListWidget, 24 | QCheckBox, 25 | QRadioButton, 26 | ) 27 | 28 | from gui.plotting.plots.AmplitudePlot import AmplitudePlot 29 | from gui.plotting.plots.ColorMeshPlot import ColorMeshPlot 30 | from gui.windows.ViewProperties import ViewProperties 31 | from gui.windows.bispectrum.BAPlot import BAPlot 32 | 33 | 34 | class BAViewProperties(ViewProperties): 35 | def __init__(self): 36 | """Define properties introduced by the .ui file.""" 37 | self.line_surrogate: QLineEdit = None 38 | self.slider_surrogate: QSlider = None 39 | 40 | self.lineedit_alpha: QLineEdit = None 41 | self.lineedit_nv: QLineEdit = None 42 | self.lineedit_freq_x: QLineEdit = None 43 | self.lineedit_freq_y: QLineEdit = None 44 | 45 | self.combo_plot_type: QComboBox = None 46 | 47 | self.grid_main: QGridLayout = None 48 | self.vbox_left: QVBoxLayout = None 49 | self.vbox_right: QVBoxLayout = None 50 | 51 | self.plot_right_bottom: BAPlot = None 52 | self.plot_right_middle: BAPlot = None 53 | 54 | self.plot_right_top: AmplitudePlot = None # Plots amplitude for WT. 55 | self.plot_main: ColorMeshPlot = None # Plots WT or bispectrum. 56 | 57 | self.btn_add_point: QPushButton = None 58 | self.btn_select_point: QPushButton = None 59 | self.btn_clear_plots: QPushButton = None 60 | 61 | self.listwidget_freq: QListWidget = None 62 | self.checkbox_plot_surr: QCheckBox = None 63 | 64 | self.radio_power: QRadioButton = None 65 | self.radio_ampl: QRadioButton = None 66 | -------------------------------------------------------------------------------- /src/gui/windows/common/BaseTFViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QPushButton 17 | 18 | from PyQt5.QtWidgets import QLineEdit, QListWidget, QMenuBar 19 | 20 | from gui.windows.ViewProperties import ViewProperties 21 | from gui.plotting.plots.PreprocessPlot import PreprocessPlot 22 | 23 | 24 | class BaseTFViewProperties(ViewProperties): 25 | 26 | def __init__(self): 27 | self.btn_calculate_single: QPushButton = None 28 | self.btn_calculate_all: QPushButton = None 29 | 30 | # The menu bar at the top of the window. 31 | self.menubar: QMenuBar = None 32 | 33 | # The QLineEdits for frequencies. 34 | self.line_fmin: QLineEdit = None 35 | self.line_fmax: QLineEdit = None 36 | self.line_res: QLineEdit = None 37 | 38 | # The QListWidget which contains the names of different signals. 39 | self.list_select_data: QListWidget = None 40 | -------------------------------------------------------------------------------- /src/gui/windows/groupcoherence/GCViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QLineEdit, QPushButton, QListWidget, QGroupBox 17 | 18 | from gui.windows.ViewProperties import ViewProperties 19 | 20 | 21 | class GCViewProperties(ViewProperties): 22 | def __init__(self): 23 | self.line_stat_fmin: QLineEdit = None 24 | self.line_stat_fmax: QLineEdit = None 25 | 26 | self.btn_stat_add: QPushButton = None 27 | self.btn_stat_del: QPushButton = None 28 | 29 | self.btn_stat_calc: QPushButton = None 30 | 31 | self.list_stat: QListWidget = None 32 | self.groupbox_stats: QGroupBox = None 33 | 34 | self.line_percentile: QLineEdit = None 35 | self.line_plot_percentile: QLineEdit = None 36 | -------------------------------------------------------------------------------- /src/gui/windows/harmonics/DHViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QComboBox, QLineEdit 17 | 18 | from gui.windows.ViewProperties import ViewProperties 19 | 20 | 21 | class DHViewProperties(ViewProperties): 22 | def __init__(self): 23 | self.combo_plot_type: QComboBox = None 24 | 25 | self.line_sigma: QLineEdit = None 26 | self.line_res: QLineEdit = None 27 | self.line_fmax: QLineEdit = None 28 | self.line_fmin: QLineEdit = None 29 | -------------------------------------------------------------------------------- /src/gui/windows/phasecoherence/PCViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QSlider, QLineEdit 17 | 18 | from gui.windows.ViewProperties import ViewProperties 19 | 20 | 21 | class PCViewProperties(ViewProperties): 22 | 23 | def __init__(self): 24 | self.slider_surrogate: QSlider = None 25 | self.line_surrogate: QLineEdit = None 26 | -------------------------------------------------------------------------------- /src/gui/windows/ridgeextraction/REPlot.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from gui.plotting.MatplotlibWidget import MatplotlibWidget 17 | 18 | 19 | class REPlot(MatplotlibWidget): 20 | 21 | def plot(self, times, values): 22 | self.axes.xaxis.set_label_position("top") 23 | self.update_xlabel() 24 | self.update_ylabel() 25 | 26 | self.axes.autoscale(True) 27 | 28 | self.axes.plot(times, values, linewidth=0.8) 29 | self.axes.autoscale(False) 30 | self.axes.set_xlim([times[0], times[-1]]) 31 | self.on_plot_complete() 32 | 33 | def get_xlabel(self): 34 | return "Time (s)" 35 | -------------------------------------------------------------------------------- /src/gui/windows/ridgeextraction/REViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QPushButton, QLineEdit, QListWidget 17 | 18 | from gui.windows.ViewProperties import ViewProperties 19 | from gui.windows.ridgeextraction.REPlot import REPlot 20 | 21 | 22 | class REViewProperties(ViewProperties): 23 | 24 | def __init__(self): 25 | # The top and bottom REPlots. 26 | self.re_top: REPlot = None 27 | self.re_bottom: REPlot = None 28 | 29 | self.btn_add_region: QPushButton = None 30 | self.btn_mark_region: QPushButton = None 31 | 32 | self.btn_filter: QPushButton = None 33 | self.btn_ridges: QPushButton = None 34 | 35 | self.line_freq1: QLineEdit = None 36 | self.line_freq2: QLineEdit = None 37 | 38 | self.list_intervals: QListWidget = None 39 | -------------------------------------------------------------------------------- /src/gui/windows/timefrequency/TFViewProperties.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QRadioButton, QComboBox 17 | 18 | from gui.plotting.plots.PreprocessPlot import PreprocessPlot 19 | from gui.windows.ViewProperties import ViewProperties 20 | 21 | 22 | class TFViewProperties(ViewProperties): 23 | def __init__(self): 24 | self.radio_transform_wt: QRadioButton = None 25 | self.plot_preproc: PreprocessPlot = None 26 | 27 | self.combo_impl: QComboBox = None 28 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | """ 17 | The entry-point of PyMODA. 18 | """ 19 | 20 | __version__ = "0.8.2" 21 | 22 | import asyncio 23 | import multiprocessing 24 | import os 25 | import signal 26 | import sys 27 | from os import path 28 | from pathlib import Path 29 | 30 | import multiprocess 31 | import qasync 32 | 33 | import utils 34 | from gui.Application import Application 35 | from processes import mp_utils 36 | from utils import errorhandling, stdout_redirect, args, log_utils, launcher 37 | 38 | if __name__ == "__main__": 39 | # Fix issues when packaged with PyInstaller. 40 | for m in (multiprocess, multiprocessing): 41 | m.freeze_support() 42 | 43 | # If not started via the launcher, exit and run new instance via launcher. 44 | if utils.is_frozen and not args.launcher() and launcher.is_launcher_present(): 45 | launcher.start_via_launcher() 46 | sys.exit(0) 47 | 48 | # Choose the desired working directory. 49 | if utils.is_frozen: 50 | # When packaged with PyInstaller, use the directory containing the executable. 51 | location = os.path.abspath(sys._MEIPASS) 52 | else: 53 | # When running as a normal Python program, use the root of the repository. 54 | location = Path(path.abspath(path.dirname(__file__))).parent 55 | 56 | # Set the working directory for consistency. 57 | os.chdir(location) 58 | 59 | # Fix Ctrl-C behaviour with PyQt. 60 | signal.signal(signal.SIGINT, signal.SIG_DFL) 61 | 62 | args.init() 63 | log_utils.init() 64 | errorhandling.init() 65 | stdout_redirect.init() 66 | 67 | app = Application(sys.argv) 68 | 69 | # Setup asyncio to work with PyQt. 70 | loop = qasync.QEventLoop(app) 71 | asyncio.set_event_loop(loop) 72 | 73 | # Fix multiprocessing on macOS. 74 | mp_utils.set_mp_start_method() 75 | 76 | # Open the launcher window. Must be called after setting the event loop. 77 | app.start_launcher() 78 | 79 | with loop: 80 | sys.exit(loop.run_forever()) 81 | -------------------------------------------------------------------------------- /src/maths/algorithms/matlabwrappers/bayesian_inference.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from multiprocess import Queue 18 | 19 | from gui.windows.bayesian.ParamSet import ParamSet 20 | from maths.signals.TimeSeries import TimeSeries 21 | 22 | 23 | def _moda_dynamic_bayesian_inference( 24 | queue: Queue, signal1: TimeSeries, signal2: TimeSeries, params: ParamSet 25 | ): 26 | """ 27 | UNUSED. 28 | 29 | Uses the MATLAB-packaged function to perform Bayesian inference. 30 | Unused because it causes a serious error on Linux. Check the Python implementation 31 | of Bayesian inference instead (`bayesian.py`). 32 | """ 33 | import full_bayesian 34 | import matlab 35 | 36 | package = full_bayesian.initialize() 37 | 38 | sig1 = matlab.double(signal1.signal.tolist()) 39 | sig2 = matlab.double(signal2.signal.tolist()) 40 | 41 | int1 = list(params.freq_range1) 42 | int2 = list(params.freq_range2) 43 | 44 | fs = signal1.frequency 45 | win = params.window 46 | pr = params.propagation_const 47 | ovr = params.overlap 48 | bn = params.order 49 | ns = params.surr_count 50 | signif = params.confidence_level 51 | 52 | result = package.full_bayesian( 53 | sig1, sig2, *int1, *int2, fs, win, pr, ovr, bn, ns, signif 54 | ) 55 | 56 | queue.put((signal1.name, *result)) 57 | -------------------------------------------------------------------------------- /src/maths/algorithms/matlabwrappers/biphase_wav_new.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | """ 18 | Do not import this module in the main process, or it will break Linux support 19 | due to issues with the LD_LIBRARY_PATH. 20 | """ 21 | from typing import Tuple 22 | 23 | from numpy import ndarray 24 | from pymodalib.utils.decorators import matlabwrapper 25 | 26 | from maths.num_utils import matlab_to_numpy 27 | 28 | 29 | @matlabwrapper(module="biphaseWavNew") 30 | def calculate( 31 | signal1: ndarray, signal2: ndarray, fs, f0, fr, opt: dict 32 | ) -> Tuple[ndarray, ndarray]: 33 | """ 34 | Calculates the biphase and biamplitude from the bispectrum using the MATLAB-packaged function. 35 | """ 36 | import biphaseWavPython 37 | import matlab 38 | 39 | package = biphaseWavPython.initialize() 40 | 41 | result = package.biphaseWavPython( 42 | matlab.double(signal1), 43 | matlab.double(signal2), 44 | fs, 45 | f0, 46 | matlab.double(list(fr)), 47 | opt, 48 | nargout=2, 49 | ) 50 | 51 | biamp, biphase = result 52 | 53 | biamp = matlab_to_numpy(biamp) 54 | biphase = matlab_to_numpy(biphase) 55 | 56 | return biamp, biphase 57 | 58 | 59 | def expand(_dict: dict) -> tuple: 60 | """ 61 | Expands a dictionary into a MATLAB-friendly list of arguments. 62 | For example, {"fmin": 5, "f0": 1} expands to ("fmin", 5, "f0", 1). 63 | """ 64 | _list = [] 65 | for key, value in _dict.items(): 66 | _list.append(key) 67 | _list.append(value) 68 | 69 | return tuple(_list) 70 | -------------------------------------------------------------------------------- /src/maths/algorithms/matlabwrappers/bispec_wav_new.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from typing import Tuple 18 | 19 | import numpy as np 20 | from numpy import ndarray 21 | from pymodalib.utils.decorators import matlabwrapper 22 | 23 | from maths.num_utils import multi_matlab_to_numpy 24 | 25 | 26 | @matlabwrapper(module="bispecWavPython") 27 | def calculate( 28 | signal1: ndarray, signal2: ndarray, fs, params: dict 29 | ) -> Tuple[ndarray, ndarray, ndarray, ndarray, dict]: 30 | """ 31 | Calculates the bispectrum of 2 signals using the MATLAB-packaged function. 32 | """ 33 | import bispecWavPython 34 | import matlab 35 | 36 | package = bispecWavPython.initialize() 37 | 38 | result = package.bispecWavPython( 39 | matlab.double(signal1), matlab.double(signal2), fs, *expand(params), nargout=5 40 | ) 41 | 42 | bisp, freq, wt1, wt2, opt = result 43 | bisp, freq, wt1, wt2 = multi_matlab_to_numpy(bisp, freq, wt1, wt2) 44 | 45 | opt["PadLR1"], opt["PadLR2"], opt["twf1"], opt["twf2"] = [ 46 | n.tolist()[0] 47 | for n in multi_matlab_to_numpy( 48 | opt["PadLR1"], opt["PadLR2"], opt["twf1"], opt["twf2"] 49 | ) 50 | ] 51 | 52 | output = (np.abs(bisp), freq, np.abs(wt1), np.abs(wt2), opt) 53 | return output 54 | 55 | 56 | def expand(_dict: dict) -> tuple: 57 | """ 58 | Expands a dictionary into a MATLAB-friendly list of arguments. 59 | For example, {"fmin": 5, "f0": 1} expands to ("fmin", 5, "f0", 1). 60 | """ 61 | _list = [] 62 | for key, value in _dict.items(): 63 | _list.append(key) 64 | _list.append(value) 65 | 66 | return tuple(_list) 67 | -------------------------------------------------------------------------------- /src/maths/algorithms/matlabwrappers/wav_surrogate.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from numpy import ndarray 18 | 19 | 20 | def calculate(signal: ndarray, surr_type: str, adj: int) -> ndarray: 21 | """ 22 | Calculates surrogates using the MATLAB-packaged function. Used in bispectrum analysis for 23 | IAAFT2 surrogates. 24 | 25 | :param signal: the signal 26 | :param surr_type: the type of surrogate 27 | :param adj: ? 28 | :return: [1D array] the surrogate signal 29 | """ 30 | import matlab 31 | import wavsurrogate 32 | 33 | package = wavsurrogate.initialize() 34 | 35 | if isinstance(signal, ndarray): 36 | signal = signal.tolist() 37 | 38 | result = package.wavsurrogate(matlab.double(signal), surr_type, adj) 39 | 40 | return result 41 | -------------------------------------------------------------------------------- /src/maths/algorithms/matlabwrappers/wft.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | from maths.params.TFParams import TFParams, _f0, _fmin 19 | from maths.signals.TimeSeries import TimeSeries 20 | 21 | 22 | def calculate(time_series: TimeSeries, params: TFParams): 23 | """ 24 | Calculates the windowed Fourier transform using the MATLAB-packaged function. 25 | 26 | :param time_series: the signal to perform the transform on 27 | :param params: the params object containing parameters to pass to the MATLAB function 28 | :return: [2D array] the windowed Fourier transform; [1D array] the frequencies 29 | """ 30 | 31 | import WFT 32 | import matlab 33 | 34 | package = WFT.initialize() 35 | 36 | signal_matlab = matlab.double([time_series.signal.tolist()]) 37 | 38 | """ 39 | The value passed for 'f0' should actually be that of 'fr' in the case 40 | of WFT. In the Matlab version this is handled before passing the value 41 | to the function, so we'll do the same. 42 | 43 | When the value has been left blank, there is no problem because this 44 | case is handled by the Matlab function. 45 | """ 46 | params_dict = params.get() 47 | 48 | f0 = params_dict.get(_f0) 49 | fmin = params_dict.get(_fmin) 50 | 51 | if f0 is not None and fmin is not None and fmin != 0: 52 | params_dict[_f0] = f0 / fmin 53 | 54 | wft, frequency = package.wft(signal_matlab, params.fs, params_dict, nargout=2) 55 | 56 | return wft, frequency 57 | -------------------------------------------------------------------------------- /src/maths/algorithms/multiprocessing/bandpass_filter.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Tuple 17 | 18 | import numpy as np 19 | from pymodalib.implementations.python.filtering import loop_butter 20 | from scipy.signal import hilbert 21 | from numpy import ndarray 22 | from maths.signals.TimeSeries import TimeSeries 23 | 24 | from processes.mp_utils import process 25 | 26 | 27 | @process 28 | def _bandpass_filter( 29 | time_series: TimeSeries, fmin, fmax, fs 30 | ) -> Tuple[str, ndarray, ndarray, ndarray, Tuple[float, float]]: 31 | """ 32 | Performs the bandpass filter on a signal. Used in ridge-extraction and filtering. 33 | 34 | :param time_series: the signal 35 | :param fmin: the minimum frequency 36 | :param fmax: the maximum frequency 37 | :param fs: the sampling frequency 38 | :return: 39 | [str] name of the signal; 40 | [?] bands 41 | [1D array] phase 42 | [1D array] amplitude 43 | [tuple] the min and max frequencies 44 | """ 45 | bands, _ = loop_butter(time_series.signal, fmin, fmax, fs) 46 | h = hilbert(bands) 47 | 48 | phase = np.angle(h) 49 | amp = np.abs(h) 50 | 51 | return time_series.name, bands, phase, amp, (fmin, fmax) 52 | -------------------------------------------------------------------------------- /src/maths/algorithms/multiprocessing/bayesian_inference.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from typing import Tuple 18 | 19 | import pymodalib 20 | from numpy import ndarray 21 | 22 | from gui.windows.bayesian.ParamSet import ParamSet 23 | from maths.signals.TimeSeries import TimeSeries 24 | from processes.mp_utils import process 25 | 26 | 27 | @process 28 | def _dynamic_bayesian_inference( 29 | signal1: TimeSeries, signal2: TimeSeries, params: ParamSet 30 | ) -> Tuple[ 31 | str, 32 | ndarray, 33 | ndarray, 34 | ndarray, 35 | ndarray, 36 | ndarray, 37 | ndarray, 38 | ndarray, 39 | ndarray, 40 | ndarray, 41 | ndarray, 42 | ndarray, 43 | ]: 44 | sig1 = signal1.signal 45 | sig2 = signal2.signal 46 | 47 | interval1, interval2 = params.freq_range1, params.freq_range2 48 | 49 | fs = signal1.frequency 50 | bn = params.order 51 | 52 | win = params.window 53 | ovr = params.overlap 54 | pr = params.propagation_const 55 | signif = params.confidence_level 56 | 57 | result = pymodalib.bayesian_inference( 58 | sig1, 59 | sig2, 60 | fs=fs, 61 | interval1=interval1, 62 | interval2=interval2, 63 | surrogates=params.surr_count, 64 | window=win, 65 | overlap=ovr, 66 | order=bn, 67 | propagation_const=pr, 68 | signif=signif, 69 | ) 70 | 71 | return (signal1.name, *result) 72 | -------------------------------------------------------------------------------- /src/maths/algorithms/wpc.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | """ 18 | Translation of the wavelet phase coherence algorithm from MODA. 19 | 20 | STATUS: Finished, although surrogates are not complete (see `surrogates.py`). 21 | """ 22 | from typing import Tuple 23 | 24 | import numpy as np 25 | from numpy import ndarray 26 | from pymodalib.algorithms.coherence import tlphcoh, wphcoh 27 | 28 | 29 | def wpc( 30 | wt1: ndarray, wt2: ndarray, freq: ndarray, fs: float, wsize: int = 10 31 | ) -> Tuple[ndarray, ndarray, ndarray]: 32 | """ 33 | Wavelet phase coherence. 34 | 35 | :param wt1: wavelet transform of the first signal 36 | :param wt2: wavelet transform of the second signal 37 | :param freq: frequencies at which transforms were calculated 38 | :param fs: sampling frequency 39 | :param wsize: window size 40 | :return: [2D array] absolute value of time-localised phase coherence; [1D array] phase coherence; [1D array] phase difference 41 | """ 42 | tlpc = tlphcoh(wt1, wt2, freq, fs, wsize) 43 | pc, pdiff = wphcoh(wt1, wt2) 44 | return np.abs(tlpc), pc, pdiff 45 | -------------------------------------------------------------------------------- /src/maths/params/BAParams.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from maths.signals.Signals import Signals 17 | 18 | 19 | class BAParams: 20 | def __init__( 21 | self, 22 | signals: Signals, 23 | fmin: float, 24 | fmax: float, 25 | f0: float, 26 | preprocess: bool, 27 | nv: float, 28 | surr_count: int, 29 | alpha: float, 30 | opt: dict, 31 | ): 32 | self.signals = signals 33 | self.fmin = fmin 34 | self.fmax = fmax 35 | self.f0 = f0 36 | self.preprocess = preprocess 37 | self.nv = nv 38 | self.surr_count = surr_count 39 | self.alpha = alpha 40 | self.fs = signals.frequency 41 | 42 | # The MATLAB algorithm returns a struct, `opt`, which is converted to this dict. 43 | self.opt = opt 44 | -------------------------------------------------------------------------------- /src/maths/params/DBParams.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from maths.params.PCParams import PCParams 17 | from maths.params.TFParams import _wft 18 | from maths.signals.Signals import Signals 19 | 20 | 21 | class DBParams(PCParams): 22 | 23 | def __init__(self, 24 | signals: Signals, 25 | fmin=0, 26 | fmax=None, 27 | fstep="auto", 28 | f0=1, 29 | padding="predictive", 30 | cut_edges=False, 31 | window="Gaussian", # Just for WFT. 32 | wavelet="Lognorm", # Just for WT. 33 | preprocess=True, 34 | rel_tolerance=0.01, 35 | transform=_wft, 36 | 37 | # Added in BAParams. 38 | fc: float = None, 39 | nv: float = None, 40 | 41 | # Added in PCParams. 42 | surr_enabled=False, 43 | surr_count=0, 44 | surr_method="RP", 45 | surr_preproc=False): 46 | super().__init__(signals, 47 | fmin, 48 | fmax, 49 | fstep, 50 | f0, 51 | padding, 52 | cut_edges, 53 | window, 54 | wavelet, 55 | preprocess, 56 | rel_tolerance, 57 | transform, 58 | 59 | surr_enabled, 60 | surr_count, 61 | surr_method, 62 | surr_preproc) 63 | 64 | self.data["nv"] = nv 65 | self.data["fc"] = fc 66 | -------------------------------------------------------------------------------- /src/maths/params/DHParams.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import List, Dict 17 | 18 | from maths.params.TFParams import TFParams 19 | from utils.dict_utils import sanitise 20 | 21 | 22 | class DHParams(TFParams): 23 | def __init__( 24 | self, signals, scale_min, scale_max, time_res, sigma, surr_count, crop 25 | ): 26 | super(DHParams, self).__init__(signals) 27 | 28 | self.crop = crop 29 | self.surr_count = surr_count 30 | self.sigma = sigma 31 | self.time_res = time_res 32 | self.scale_max = scale_max 33 | self.scale_min = scale_min 34 | 35 | def args(self) -> List: 36 | items = ( 37 | self.fs, 38 | self.scale_min, 39 | self.scale_max, 40 | self.sigma, 41 | self.time_res, 42 | self.surr_count, 43 | ) 44 | return [i for i in items if i is not None] 45 | 46 | def items_to_save(self) -> Dict: 47 | return sanitise( 48 | { 49 | "crop": self.crop, 50 | "surrogates": self.surr_count, 51 | "sigma": self.sigma, 52 | "scale_max": self.scale_max, 53 | "scale_min": self.scale_min, 54 | "time_res": self.time_res, 55 | } 56 | ) 57 | -------------------------------------------------------------------------------- /src/maths/params/PCParams.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Dict 17 | 18 | from maths.params.TFParams import TFParams, _wft 19 | from maths.signals.Signals import Signals 20 | from utils.decorators import override 21 | from utils.dict_utils import sanitise 22 | 23 | 24 | class PCParams(TFParams): 25 | def __init__( 26 | self, 27 | signals: Signals, 28 | fmin=0, 29 | fmax=None, 30 | fstep="auto", 31 | f0=1, 32 | padding="predictive", 33 | cut_edges=False, 34 | window="Gaussian", 35 | wavelet="Lognorm", 36 | preprocess=True, 37 | rel_tolerance=0.01, 38 | transform=_wft, 39 | # Parameters not passed to Matlab. 40 | surr_enabled=False, 41 | surr_count=0, 42 | surr_method="RP", 43 | surr_preproc=False, 44 | ): 45 | if not surr_enabled: 46 | surr_count = 0 47 | 48 | self.surr_count = surr_count 49 | self.surr_method = surr_method 50 | self.surr_preproc = surr_preproc 51 | 52 | super().__init__( 53 | signals, 54 | fmin, 55 | fmax, 56 | fstep, 57 | f0, 58 | padding, 59 | cut_edges, 60 | window, 61 | wavelet, 62 | preprocess, 63 | rel_tolerance, 64 | transform, 65 | ) 66 | 67 | @override 68 | def items_to_save(self) -> Dict: 69 | tf = super().items_to_save() 70 | 71 | out = {"surr_count": self.surr_count, "surr_type": self.surr_method, **tf} 72 | return sanitise(out) 73 | -------------------------------------------------------------------------------- /src/maths/params/REParams.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | from maths.params.TFParams import TFParams, _wft, _fmin, _fmax 18 | from maths.signals.Signals import Signals 19 | from utils.decorators import deprecated 20 | 21 | 22 | class REParams(TFParams): 23 | def __init__( 24 | self, 25 | signals: Signals, 26 | fmin=None, 27 | fmax=None, 28 | fstep="auto", 29 | f0=1, 30 | padding="predictive", 31 | cut_edges=False, 32 | window="Gaussian", 33 | wavelet="Lognorm", 34 | preprocess=True, 35 | rel_tolerance=0.01, 36 | transform=_wft, 37 | # Added in REParams. 38 | method=2, 39 | param=None, 40 | normalize=False, 41 | path_opt=True, 42 | max_iterations=20, 43 | cache_file=None, 44 | intervals=None, 45 | ): 46 | super().__init__( 47 | signals, 48 | fmin, 49 | fmax, 50 | fstep, 51 | f0, 52 | padding, 53 | cut_edges, 54 | window, 55 | wavelet, 56 | preprocess, 57 | rel_tolerance, 58 | transform, 59 | ) 60 | 61 | self.intervals = intervals 62 | 63 | # Add params not used in TFParams. 64 | self.data["Method"] = method 65 | 66 | if param: 67 | self.data["Param"] = param # Not tested, may not work. 68 | 69 | self.data["Normalize"] = normalize 70 | self.data["PathOpt"] = path_opt 71 | self.data["MaxIter"] = max_iterations 72 | 73 | if cache_file: 74 | self.data["CachedDataLocation"] = cache_file 75 | 76 | if fmin is None: 77 | self.delete(_fmin) 78 | else: 79 | self.data[_fmin] = fmin 80 | 81 | if fmax is None: 82 | self.delete(_fmax) 83 | else: 84 | self.data[_fmax] = fmax 85 | 86 | self.data["Display"] = "off" 87 | 88 | @deprecated 89 | def set_cache_file(self, file: str) -> None: 90 | self.data["CachedDataLocation"] = file 91 | -------------------------------------------------------------------------------- /src/maths/signals/data/BAOutputData.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Dict 17 | 18 | from dataclasses import dataclass 19 | from numpy import ndarray 20 | 21 | 22 | @dataclass 23 | class BAOutputData: 24 | """ 25 | Data class containing data returned by bispectrum analysis. 26 | """ 27 | 28 | # Amplitude and power of wavelet transform 1. 29 | amp_wt1: ndarray 30 | pow_wt1: ndarray 31 | 32 | # Average amplitude and power of wavelet transform 1. 33 | avg_amp_wt1: ndarray 34 | avg_pow_wt1: ndarray 35 | 36 | # Amplitude and power of wavelet transform 2. 37 | amp_wt2: ndarray 38 | pow_wt2: ndarray 39 | 40 | # Average amplitude and power of wavelet transform 2. 41 | avg_amp_wt2: ndarray 42 | avg_pow_wt2: ndarray 43 | 44 | times: ndarray 45 | freq: ndarray 46 | 47 | # Bispectra. 48 | bispxxx: ndarray 49 | bispppp: ndarray 50 | bispxpp: ndarray 51 | bisppxx: ndarray 52 | 53 | # Surrogates. 54 | surrxxx: ndarray 55 | surrppp: ndarray 56 | surrxpp: ndarray 57 | surrpxx: ndarray 58 | 59 | opt: dict 60 | 61 | biamp: Dict[float, ndarray] 62 | biphase: Dict[float, ndarray] 63 | 64 | def invalidate(self): 65 | amp_wt1: ndarray = None 66 | pow_wt1: ndarray = None 67 | avg_amp_wt1: ndarray = None 68 | avg_pow_wt1: ndarray = None 69 | amp_wt2: ndarray = None 70 | pow_wt2: ndarray = None 71 | avg_amp_wt2: ndarray = None 72 | avg_pow_wt2: ndarray = None 73 | times: ndarray = None 74 | freq: ndarray = None 75 | bispxxx: ndarray = None 76 | bispppp: ndarray = None 77 | bispxpp: ndarray = None 78 | bisppxx: ndarray = None 79 | surrxxx: ndarray = None 80 | surrppp: ndarray = None 81 | surrxpp: ndarray = None 82 | surrpxx: ndarray = None 83 | opt: dict = None 84 | biamp: Dict[float, ndarray] = None 85 | biphase: Dict[float, ndarray] = None 86 | -------------------------------------------------------------------------------- /src/maths/signals/data/DBOutputData.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | class DBOutputData: 19 | 20 | def __init__(self, 21 | tm, 22 | p1, 23 | p2, 24 | cpl1, 25 | cpl2, 26 | cf1, 27 | cf2, 28 | mcf1, 29 | mcf2, 30 | surr_cpl1, 31 | surr_cpl2): 32 | self.tm = tm 33 | self.surr_cpl2 = surr_cpl2 34 | self.surr_cpl1 = surr_cpl1 35 | self.mcf2 = mcf2 36 | self.mcf1 = mcf1 37 | self.cf2 = cf2 38 | self.cf1 = cf1 39 | self.cpl2 = cpl2 40 | self.cpl1 = cpl1 41 | self.p2 = p2 42 | self.p1 = p1 43 | -------------------------------------------------------------------------------- /src/updater/CleanupThread.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import logging 17 | import os 18 | import shutil 19 | import time 20 | from os.path import join 21 | 22 | from PyQt5.QtCore import QThread 23 | 24 | from utils import launcher, shortcuts 25 | 26 | 27 | class CleanupThread(QThread): 28 | """ 29 | Thread which deletes old installations of PyMODA. 30 | """ 31 | 32 | def run(self) -> None: 33 | time.sleep(5) 34 | launcher_dir = launcher.get_launcher_directory() 35 | 36 | import re 37 | 38 | pattern = "v[0-9].[0-9].[0-9]" 39 | versions = filter(lambda i: re.match(pattern, i), os.listdir(launcher_dir)) 40 | 41 | versions = sorted(list(versions)) 42 | logging.info(f"Currently installed versions: {versions}.") 43 | 44 | if len(versions) > 1: 45 | to_delete = versions[:-1] 46 | 47 | import main 48 | 49 | if main.__version__ in to_delete: 50 | logging.error( 51 | f"Cannot delete version {main.__version__}; it is the current version." 52 | ) 53 | return 54 | 55 | for v in to_delete: 56 | logging.info(f"Deleting old version: {v}") 57 | path = join(launcher_dir, v) 58 | shutil.rmtree(path) 59 | 60 | # Fix issue where shortcut linked to an older version breaks because older version is deleted. 61 | shortcuts.create_shortcut() 62 | -------------------------------------------------------------------------------- /src/updater/check.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import List, Tuple 17 | 18 | from github import Github 19 | from github.GitRelease import GitRelease 20 | 21 | g = Github() 22 | 23 | 24 | def _tuple_version(version_tag: str) -> Tuple[int, int, int]: 25 | return tuple([int(v) for v in version_tag.replace("v", "").split(".")]) 26 | 27 | 28 | def is_version_newer(new: str, old: str) -> bool: 29 | return _tuple_version(new) > _tuple_version(old) 30 | 31 | 32 | def is_update_available() -> Tuple[bool, str]: 33 | import main 34 | 35 | releases = get_releases() 36 | 37 | latest = releases[0] 38 | return is_version_newer(latest.title, main.__version__), latest.title 39 | 40 | 41 | def get_releases() -> List[GitRelease]: 42 | repo = g.get_repo("luphysics/PyMODA") 43 | 44 | releases = repo.get_releases() 45 | return releases 46 | -------------------------------------------------------------------------------- /src/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import sys 17 | 18 | is_frozen = frozen = getattr(sys, "frozen", False) 19 | -------------------------------------------------------------------------------- /src/utils/cache.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import os 17 | import shutil 18 | 19 | import scipy.io 20 | 21 | 22 | def clear(): 23 | c = Cache() 24 | c.clear_all() 25 | 26 | 27 | class Cache: 28 | 29 | def __init__(self): 30 | self.cache = self.get_cache_location() 31 | try: 32 | os.mkdir(self.cache) 33 | except: 34 | pass 35 | 36 | @staticmethod 37 | def get_cache_location() -> str: 38 | base = os.getcwd() 39 | 40 | # If current directory is src, then create cache 41 | # in directory above. 42 | if base.split("/")[-1] == "src": 43 | base = f"{base}/.." 44 | 45 | return f"{base}/cache" 46 | 47 | def get_file_names(self) -> list: 48 | return os.listdir(self.cache) 49 | 50 | def generate_file_name(self, extension=".mat") -> str: 51 | # Get names of existing files without their file extensions. 52 | names = [".".join(name.split(".")[:-1]) for name in self.get_file_names()] 53 | 54 | i = -1 55 | while True: 56 | i += 1 57 | n = self._name_template(i) 58 | 59 | if n not in names: 60 | break 61 | 62 | return f"{n}{extension}" 63 | 64 | def get_path_to(self, file: str) -> str: 65 | return f"{self.cache}/{file}" 66 | 67 | def save_data(self, **kwargs) -> str: 68 | name = self.generate_file_name() 69 | path = self.get_path_to(name) 70 | scipy.io.savemat(path, kwargs) 71 | return path 72 | 73 | def clear_all(self): 74 | """ 75 | Removes the cache folder and all its contents. 76 | 77 | The cache folder will be recreated next time 78 | Cache is instantiated. 79 | """ 80 | shutil.rmtree(self.cache) 81 | 82 | @staticmethod 83 | def _name_template(index) -> str: 84 | return f"data{index}" 85 | -------------------------------------------------------------------------------- /src/utils/dict_utils.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from typing import Dict 17 | 18 | 19 | def sanitise(dictionary: Dict) -> Dict: 20 | """ 21 | Creates a sanitised copy of a dictionary, removing all None items. 22 | Does not modify the existing dictionary. 23 | 24 | :param dictionary: the dictionary to remove None items from 25 | :return: the new dictionary 26 | """ 27 | new = {} 28 | 29 | for key, value in dictionary.items(): 30 | if key is not None and value is not None: 31 | new[key] = value 32 | 33 | return new 34 | -------------------------------------------------------------------------------- /src/utils/file_utils.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import os 17 | import warnings 18 | from os import path 19 | from os.path import join 20 | 21 | from utils.os_utils import OS 22 | 23 | pymoda_path = None 24 | 25 | username = os.environ.get("USERNAME") or os.environ.get("USER") 26 | home = os.path.expanduser("~") 27 | 28 | if OS.is_windows(): 29 | pymoda_path = f"C:\\Users\\{username}\\AppData\\Roaming\\PyMODA" 30 | else: 31 | pymoda_path = f"{home}/.pymoda" 32 | 33 | os.makedirs(pymoda_path, exist_ok=True) 34 | 35 | log_path = join(pymoda_path, "pymoda.log") 36 | settings_path = join(pymoda_path, "settings.conf") 37 | 38 | _whitelist = ["src", "res"] 39 | 40 | 41 | def get_root_folder() -> str: 42 | """ 43 | Returns the absolute path to PyMODA's root folder. 44 | 45 | WARNING: This function relies on the fact that the current working directory points to `src/`. 46 | """ 47 | _, folder = path.split(os.getcwd()) 48 | # if folder not in _whitelist: 49 | if not any([f in _whitelist for f in os.listdir(os.getcwd())]): 50 | import inspect 51 | 52 | warnings.warn( 53 | f"\nWARNING: function '{inspect.currentframe().f_code.co_name}' is attempting to find " 54 | f"PyMODA's root directory, but the current working directory may not be the root directory." 55 | f"The current working directory is '{os.getcwd()}'.", 56 | RuntimeWarning, 57 | ) 58 | 59 | return os.getcwd() 60 | -------------------------------------------------------------------------------- /src/utils/launcher.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import os 17 | import subprocess 18 | import sys 19 | from os.path import join 20 | 21 | from utils import file_utils 22 | from utils.os_utils import OS 23 | 24 | 25 | def get_launcher_directory() -> str: 26 | return file_utils.pymoda_path 27 | 28 | 29 | def is_launcher_present() -> bool: 30 | return any( 31 | [ 32 | name in os.listdir(get_launcher_directory()) 33 | for name in ["launcher", "launcher.exe"] 34 | ] 35 | ) 36 | 37 | 38 | def get_launcher_path() -> str: 39 | folder = get_launcher_directory() 40 | 41 | target = join(folder, _get_launcher_name()) 42 | 43 | if os.path.exists(target): 44 | return target 45 | else: 46 | return None 47 | 48 | 49 | def _get_launcher_name() -> str: 50 | return "launcher.exe" if OS.is_windows() else "launcher" 51 | 52 | 53 | def start_via_launcher() -> None: 54 | target = get_launcher_path() 55 | if not target: 56 | return 57 | 58 | subprocess.Popen([target, *sys.argv[1:]]) 59 | -------------------------------------------------------------------------------- /src/utils/log_utils.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import datetime 17 | import logging 18 | import os 19 | from logging.handlers import RotatingFileHandler 20 | from os.path import join 21 | 22 | from utils import file_utils, errorhandling 23 | 24 | global_filename = None # Only used by processes on macOS/Linux. 25 | 26 | 27 | def init(filepath: str = None) -> None: 28 | if filepath: 29 | log_path = filepath 30 | else: 31 | log_path = file_utils.log_path 32 | 33 | logging.basicConfig(filename=log_path, level=logging.INFO) 34 | log = logging.getLogger("root") 35 | 36 | handler = RotatingFileHandler(log_path, mode="a", maxBytes=1024 * 1024 * 10) 37 | log.addHandler(handler) 38 | 39 | 40 | def process_init() -> None: 41 | """ 42 | Initialise logging for a process. Different processes need to log to different files, 43 | so each process can call this function to start logging. 44 | """ 45 | filename = str(datetime.datetime.now()).replace(" ", "_").replace(":", "-") 46 | filename = ".".join(filename.split(".")[:-1]) 47 | 48 | filepath = join(file_utils.pymoda_path, "processes") 49 | os.makedirs(filepath, exist_ok=True) 50 | 51 | global global_filename 52 | global_filename = join(filepath, f"{filename}.log") 53 | 54 | logging.basicConfig(filename=global_filename, level=logging.INFO) 55 | errorhandling.init() 56 | 57 | 58 | def process_write_log(msg: str) -> None: 59 | """ 60 | Used by processes on macOS/Linux; writes a message to the log file manually. 61 | 62 | Parameters 63 | ---------- 64 | msg : str 65 | The text to append to the log file. 66 | """ 67 | global global_filename 68 | 69 | with open(global_filename, "a+") as f: 70 | f.write(msg) 71 | -------------------------------------------------------------------------------- /src/utils/os_utils.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import os 17 | import platform 18 | 19 | system = platform.system() 20 | 21 | # Variables used to test functionality from different operating systems. 22 | override_windows = "MOCKOS_WINDOWS" in os.environ 23 | override_linux = "MOCKOS_LINUX" in os.environ 24 | override_macos = "MOCKOS_MACOS" in os.environ 25 | 26 | 27 | class OS: 28 | @staticmethod 29 | def is_windows() -> bool: 30 | """ 31 | Returns whether the current OS is Windows. 32 | """ 33 | return override_windows or ( 34 | system == "Windows" and not override_macos and not override_linux 35 | ) 36 | 37 | @staticmethod 38 | def is_linux() -> bool: 39 | """ 40 | Returns whether the current OS is Linux-based. 41 | """ 42 | return override_linux or ( 43 | system == "Linux" and not override_macos and not override_windows 44 | ) 45 | 46 | @staticmethod 47 | def is_mac_os() -> bool: 48 | """ 49 | Returns whether the current OS is macOS (hopefully). 50 | """ 51 | return override_macos or ( 52 | system == "Darwin" and not override_linux and not override_windows 53 | ) 54 | -------------------------------------------------------------------------------- /src/utils/qutils.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2020 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | from PyQt5.QtWidgets import QWidget, QSizePolicy 17 | 18 | 19 | def retain_size_when_hidden(widget: QWidget) -> None: 20 | sizepolicy: QSizePolicy = widget.sizePolicy() 21 | sizepolicy.setRetainSizeWhenHidden(True) 22 | widget.setSizePolicy(sizepolicy) 23 | -------------------------------------------------------------------------------- /src/utils/stdout_redirect.py: -------------------------------------------------------------------------------- 1 | # PyMODA, a Python implementation of MODA (Multiscale Oscillatory Dynamics Analysis). 2 | # Copyright (C) 2019 Lancaster University 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | import datetime 17 | import logging 18 | import sys 19 | import threading 20 | 21 | 22 | class StdOut: 23 | def write(self, text): 24 | # Output text as normal. 25 | sys_out.write(text) 26 | 27 | # Write to log file. 28 | logging.info(msg=text) 29 | 30 | # Notify subscribers. 31 | for s in subscribers: 32 | if threading.current_thread() is threading.main_thread(): 33 | if isinstance(s, WindowLogger): 34 | s.update(text) 35 | else: 36 | s(text) 37 | 38 | def flush(self): 39 | return 40 | 41 | 42 | subscribers = [] # List of subscribers to be notified when stdout is used. 43 | 44 | out = StdOut() # The redirected stdout. 45 | sys_out = sys.__stdout__ # The original system stdout. 46 | 47 | 48 | def init(): 49 | sys.stdout = out 50 | 51 | 52 | def subscribe(subscriber): 53 | subscribers.append(subscriber) 54 | 55 | 56 | def unsubscribe(subscriber): 57 | subscribers.remove(subscriber) 58 | 59 | 60 | class WindowLogger: 61 | """ 62 | A class which handles logging to a log pane safely, 63 | without excessive memory usage. When the number of 64 | logged lines exceeds the max size, the first half 65 | of the lines are deleted. 66 | """ 67 | 68 | def __init__(self, func, max_lines=200): 69 | self.func = func 70 | self.lines = [] 71 | self.max_lines = max_lines 72 | 73 | def update(self, text): 74 | if text == "\n": 75 | return 76 | 77 | self.lines.append(f"{self.get_time()} - {text}") 78 | count = len(self.lines) 79 | 80 | if count > self.max_lines: 81 | # Remove the first half of the lines to save memory. 82 | self.lines = self.lines[count // 2 :] 83 | 84 | self.func("\n".join(self.lines)) 85 | 86 | @staticmethod 87 | def get_time() -> str: 88 | time = datetime.datetime.now() 89 | return f"{time:%H:%M:%S}" 90 | --------------------------------------------------------------------------------