├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── SpinTool.sh ├── docker-compose.yml ├── docs └── img │ ├── ST_Carla_MIDI.png │ ├── ST_Carla_Suite.png │ ├── ST_Icons.png │ ├── ST_Launcher_00.png │ ├── ST_Launcher_01.png │ ├── ST_Launcher_02.png │ ├── ST_Logo_L.png │ ├── ST_Logo_Transparent.png │ ├── ST_beat_amount_detail.png │ ├── ST_bpm_detail.png │ ├── ST_connections_audio.png │ ├── ST_connections_midi.png │ ├── ST_overview.png │ └── info ├── midi_controllers ├── AKAI_APC_Key25.sbm ├── AKAI_APC_Key25_KORG_nanoKONTROL2.sbm └── info ├── readme.md ├── requirements.txt └── superboucle ├── __init__.py ├── about.py ├── about_ui.py ├── about_ui.ui ├── add_clip.py ├── add_clip_ui.py ├── add_clip_ui.ui ├── add_port.py ├── add_port_ui.py ├── add_port_ui.ui ├── add_scene.py ├── add_scene_ui.py ├── add_scene_ui.ui ├── assistant.py ├── assistant_ui.py ├── assistant_ui.ui ├── cell.py ├── cell_ui.py ├── cell_ui.ui ├── clip.py ├── common.py ├── device.py ├── device_manager.py ├── device_manager_ui.py ├── device_manager_ui.ui ├── edit_clips.py ├── edit_clips_ui.py ├── edit_clips_ui.ui ├── export_samples.py ├── export_samples_ui.py ├── export_samples_ui.ui ├── gui.py ├── gui.qrc ├── gui_rc.py ├── gui_ui.py ├── gui_ui.ui ├── help.py ├── help_ui.py ├── icons ├── MeltinPop_Logo_100.png ├── ST_Logo_Small_Transparent.png ├── ST_Logo_XSmall_Transparent.png ├── Superdirt_Logo_80.png ├── black_goto_24.png ├── black_pause_24.png ├── black_play_24.png ├── black_rewind_24.png ├── clip-add.png ├── clip-behaviour.png ├── clip-edit.png ├── clip-functions.png ├── clip-groups.png ├── clip-measures.png ├── clip-start-stop.png ├── copy.png ├── edit-delete.png ├── fader.png ├── file.png ├── folder.png ├── icon.png ├── icon_dev.png ├── info ├── logo_sb.png ├── record_24.png ├── shift.png ├── sliders.png ├── stop.png ├── warning.png ├── white_goto_24.png ├── white_pause_24.png ├── white_play_24.png └── white_rewind_24.png ├── jack.py ├── learn.py ├── learn_cell_ui.py ├── learn_cell_ui.ui ├── learn_ui.py ├── learn_ui.ui ├── mixer.py ├── mixer_ui.py ├── mixer_ui.ui ├── mixerstrip.py ├── mixerstrip_ui.py ├── mixerstrip_ui.ui ├── new_song.py ├── new_song_ui.py ├── new_song_ui.ui ├── playlist.py ├── playlist_ui.py ├── playlist_ui.ui ├── port_manager.py ├── port_manager_ui.py ├── port_manager_ui.ui ├── preferences.py ├── preferences_ui.py ├── preferences_ui.ui ├── qsuperdial.py ├── qtmodern ├── __init__.py ├── _utils.py ├── info ├── resources │ ├── frameless.qss │ ├── info │ ├── style (copy).qss │ └── style.qss ├── styles (copy).py ├── styles.py └── windows.py ├── scene_manager.py ├── scene_manager_ui.py ├── scene_manager_ui.ui ├── settings.py ├── settings.pyc ├── song_annotation.py ├── song_annotation_ui.py ├── song_annotation_ui.ui └── spin.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-buster 2 | 3 | WORKDIR /usr/src 4 | RUN apt-get update && \ 5 | apt-get install -y libsndfile1-dev libgl1 libjack-jackd2-dev && \ 6 | apt-get clean 7 | COPY requirements.txt /usr/src 8 | RUN pip3 install -r requirements.txt 9 | COPY . /usr/src/ 10 | RUN python3 setup.py install 11 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md superboucle/gui.qrc superboucle/icons/icon.ico 2 | recursive-include superboucle *.png 3 | -------------------------------------------------------------------------------- /SpinTool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 3 | cd $SCRIPTPATH 4 | export PYTHONPATH=$(pwd):$PYTHONPATH 5 | python3 superboucle/spin.py 6 | read 7 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | builder: 4 | build: ./ 5 | image: spintool-builder 6 | stdin_open: true 7 | tty: true 8 | environment: 9 | - DISPLAY=:0 10 | volumes: 11 | - /tmp/.X11-unix:/tmp/.X11-unix 12 | - /dev/shm:/dev/shm 13 | -------------------------------------------------------------------------------- /docs/img/ST_Carla_MIDI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Carla_MIDI.png -------------------------------------------------------------------------------- /docs/img/ST_Carla_Suite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Carla_Suite.png -------------------------------------------------------------------------------- /docs/img/ST_Icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Icons.png -------------------------------------------------------------------------------- /docs/img/ST_Launcher_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Launcher_00.png -------------------------------------------------------------------------------- /docs/img/ST_Launcher_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Launcher_01.png -------------------------------------------------------------------------------- /docs/img/ST_Launcher_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Launcher_02.png -------------------------------------------------------------------------------- /docs/img/ST_Logo_L.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Logo_L.png -------------------------------------------------------------------------------- /docs/img/ST_Logo_Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_Logo_Transparent.png -------------------------------------------------------------------------------- /docs/img/ST_beat_amount_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_beat_amount_detail.png -------------------------------------------------------------------------------- /docs/img/ST_bpm_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_bpm_detail.png -------------------------------------------------------------------------------- /docs/img/ST_connections_audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_connections_audio.png -------------------------------------------------------------------------------- /docs/img/ST_connections_midi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_connections_midi.png -------------------------------------------------------------------------------- /docs/img/ST_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/docs/img/ST_overview.png -------------------------------------------------------------------------------- /docs/img/info: -------------------------------------------------------------------------------- 1 | IMGs for Wiki and other documentation 2 | -------------------------------------------------------------------------------- /midi_controllers/AKAI_APC_Key25.sbm: -------------------------------------------------------------------------------- 1 | {"start_stop": [[[9, 0, 32, 127], [9, 0, 33, 127], [9, 0, 34, 127], [9, 0, 35, 127], [9, 0, 36, 127], [9, 0, 37, 127], [9, 0, 38, 127], [9, 0, 39, 127]], [[9, 0, 24, 127], [9, 0, 25, 127], [9, 0, 26, 127], [9, 0, 27, 127], [9, 0, 28, 127], [9, 0, 29, 127], [9, 0, 30, 127], [9, 0, 31, 127]], [[9, 0, 16, 127], [9, 0, 17, 127], [9, 0, 18, 127], [9, 0, 19, 127], [9, 0, 20, 127], [9, 0, 21, 127], [9, 0, 22, 127], [9, 0, 23, 127]], [[9, 0, 8, 127], [9, 0, 9, 127], [9, 0, 10, 127], [9, 0, 11, 127], [9, 0, 12, 127], [9, 0, 13, 127], [9, 0, 14, 127], [9, 0, 15, 127]], [[9, 0, 0, 127], [9, 0, 1, 127], [9, 0, 2, 127], [9, 0, 3, 127], [9, 0, 4, 127], [9, 0, 5, 127], [9, 0, 6, 127], [9, 0, 7, 127]]], "black_vel": 0, "green_vel": 1, "blink_green_vel": 2, "red_vel": 3, "blink_red_vel": 4, "amber_vel": 5, "blink_amber_vel": 6, "song_volume_ctrl": false, "play_btn": [9, 0, 91, 127], "pause_btn": false, "rewind_btn": false, "goto_btn": false, "record_btn": [9, 0, 93, 127], "stop_btn": [9, 0, 81, 127], "shift_btn": [9, 0, 98, 127], "unlink_stripes_btn": false, "custom_reset_btn": false, "init_command": [], "scene_buttons": [[9, 0, 82, 127], [9, 0, 83, 127], [9, 0, 84, 127], [9, 0, 85, 127], [9, 0, 86, 127]], "mute_buttons": [[9, 0, 64, 127], [9, 0, 65, 127], [9, 0, 66, 127], [9, 0, 67, 127], [9, 0, 68, 127], [9, 0, 69, 127], [9, 0, 70, 127], [9, 0, 71, 127]], "ctrls": [[11, 0, 48], [11, 0, 49], [11, 0, 50], [11, 0, 51], [11, 0, 52], [11, 0, 53], [11, 0, 54], [11, 0, 55]], "send1ctrls": [], "send2ctrls": [], "name": "AKAI APC key 25"} -------------------------------------------------------------------------------- /midi_controllers/AKAI_APC_Key25_KORG_nanoKONTROL2.sbm: -------------------------------------------------------------------------------- 1 | {"start_stop": [[[9, 0, 32, 127], [9, 0, 33, 127], [9, 0, 34, 127], [9, 0, 35, 127], [9, 0, 36, 127], [9, 0, 37, 127], [9, 0, 38, 127], [9, 0, 39, 127]], [[9, 0, 24, 127], [9, 0, 25, 127], [9, 0, 26, 127], [9, 0, 27, 127], [9, 0, 28, 127], [9, 0, 29, 127], [9, 0, 30, 127], [9, 0, 31, 127]], [[9, 0, 16, 127], [9, 0, 17, 127], [9, 0, 18, 127], [9, 0, 19, 127], [9, 0, 20, 127], [9, 0, 21, 127], [9, 0, 22, 127], [9, 0, 23, 127]], [[9, 0, 8, 127], [9, 0, 9, 127], [9, 0, 10, 127], [9, 0, 11, 127], [9, 0, 12, 127], [9, 0, 13, 127], [9, 0, 14, 127], [9, 0, 15, 127]], [[9, 0, 0, 127], [9, 0, 1, 127], [9, 0, 2, 127], [9, 0, 3, 127], [9, 0, 4, 127], [9, 0, 5, 127], [9, 0, 6, 127], [9, 0, 7, 127]]], "black_vel": 0, "green_vel": 1, "blink_green_vel": 2, "red_vel": 3, "blink_red_vel": 4, "amber_vel": 5, "blink_amber_vel": 6, "song_volume_ctrl": false, "play_btn": [11, 2, 41, 127], "pause_btn": [9, 0, 91, 127], "rewind_btn": [11, 2, 43, 127], "goto_btn": [11, 2, 44, 127], "record_btn": [11, 2, 45, 127], "stop_btn": [11, 2, 42, 127], "shift_btn": [9, 0, 98, 127], "unlink_stripes_btn": [11, 2, 46, 127], "custom_reset_btn": [11, 2, 60, 127], "init_command": [], "scene_buttons": [[9, 0, 82, 127], [9, 0, 83, 127], [9, 0, 84, 127], [9, 0, 85, 127], [9, 0, 86, 127]], "mute_buttons": [[9, 0, 64, 127], [9, 0, 65, 127], [9, 0, 66, 127], [9, 0, 67, 127], [9, 0, 68, 127], [9, 0, 69, 127], [9, 0, 70, 127], [9, 0, 71, 127]], "ctrls": [[11, 2, 0], [11, 2, 1], [11, 2, 2], [11, 2, 3], [11, 2, 4], [11, 2, 5], [11, 2, 6], [11, 2, 7]], "send1ctrls": [[11, 2, 16], [11, 2, 17], [11, 2, 18], [11, 2, 19], [11, 2, 20], [11, 2, 21], [11, 2, 22], [11, 2, 23]], "send2ctrls": [[11, 0, 48], [11, 0, 49], [11, 0, 50], [11, 0, 51], [11, 0, 52], [11, 0, 53], [11, 0, 54], [11, 0, 55]], "name": "AKAI APC key 25 + KORG nanoKONTROL 2", "description": "Configuration\n\nAKAI APC key 25 (on midi channels 1+2) :\n\nCLIPS buttons, \nSCENES buttons (scene launch 1 to 5), \nMUTE BUTTONS ( buttons under clips) \nSHIFT (shift), \nPAUSE (play/pause), \nSENDS2 (knobs)\n\nKORG nanoKONTROL 2 (on midi channel 3):\n\nOUTPUT PORTS VOLUMES (sliders),\nTRANSPORT (REWIND, forward as GO..TO, STOP, PLAY, REC), \nUNLINK (cycle), \nCUSTOM RESET (Set), \nSENDS1 (knobs)\n\nMIDI setup by Manu Controvento (Meltin'Pop)", "send1_enabled_buttons": [[11, 2, 32, 127], [11, 2, 33, 127], [11, 2, 34, 127], [11, 2, 35, 127], [11, 2, 36, 127], [11, 2, 37, 127], [11, 2, 38, 127], [11, 2, 39, 127]], "send2_enabled_buttons": [[9, 0, 64, 127], [9, 0, 65, 127], [9, 0, 66, 127], [9, 0, 67, 127], [9, 0, 68, 127], [9, 0, 69, 127], [9, 0, 70, 127], [9, 0, 71, 127]]} -------------------------------------------------------------------------------- /midi_controllers/info: -------------------------------------------------------------------------------- 1 | Midi controllers scripts for SpinTools/SuperBoucle 2 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SpinTool 2 | 3 | SpinTool is a loop based software fully controllable with any midi device, 4 | synced with Jack transport. This version derives from original Vampouille's 5 | SuperBoucle, starting from a fork of the original repository. 6 | 7 | ![SpinTool main window](https://raw.githubusercontent.com/manucontrovento/SpinTool/master/docs/img/ST_overview.png) 8 | 9 | Check user manual (from Info menu of application) for a description of 10 | usage and functions. 11 | Press the H key for integrated help to have a quick 12 | help on what the mouse cursor is pointing at. 13 | 14 | Usage Wiki for Starting: 15 | https://github.com/manucontrovento/SpinTool/wiki 16 | 17 | 18 | ## Features 19 | 20 | * Jack Transport 21 | * Record 22 | * Audio input / output 23 | * Midi input / output 24 | * Normalize and revert samples 25 | * Negative sample offset, sample offset in beats or frames 26 | * Load several formats: WAV, FLAC, AIFF, ... (no MP3 at the moment) 27 | * Playlist management 28 | * Scenes management 29 | * Full intuitive MIDI learn interface 30 | * Support any MIDI device: generic keyboard, pad, BCF, Akai APC, ... 31 | * Fully controllable by MIDI device or mouse/keyboard 32 | * One-shot clips 33 | * Light-on all midi device buttons (scenes and transport too) 34 | * Preferences Window 35 | * Start a clip just after recording it 36 | * Show clip details when triggered for start/stop 37 | * Show clip details when its volume is changed from controller 38 | * Preferred grid size, to match your controller's buttons grid 39 | * Choice of clip recording color (amber is default, red available) 40 | * Open scenes and playlist windows on start up 41 | * New grid cells style, showing clip volume 42 | * New massive clips edit functions to change clips parameters and column instrument-wise assignment 43 | * New Export-all samples function 44 | * Mixer for output ports with full MIDI support 45 | * Integrated Help/Manual 46 | * Force clips play/stop 47 | 48 | SpinTool releases start from version 20.04.07 49 | 50 | ## Requirements 51 | 52 | ### Linux 53 | 54 | * Python 3 55 | * Pip for python 3 56 | * Python modules : Cffi, PySoundFile, Numpy, PyQT 5 57 | * Running jack server 58 | 59 | Recommended: 60 | * a2jmidid to access midi controller 61 | * Carla to save connections 62 | 63 | ## Installation 64 | 65 | * Install Jack server : 66 | 67 | sudo aptitude install jackd2 qjackctl 68 | 69 | * Install midi bridge (optional) : 70 | 71 | sudo aptitude install a2jmidid 72 | 73 | * Install python modules : 74 | 75 | sudo aptitude install python3 python3-pip python3-cffi python3-numpy python3-pyqt5 76 | sudo pip3 install PySoundFile 77 | 78 | * Download and extract last version of SpinTool from https://github.com/manucontrovento/SpinTool/releases/ 79 | 80 | ## Running 81 | 82 | Start Jack audio server and then run SpinTool.sh script from SpinTool directory : 83 | 84 | ./SpinTool.sh 85 | 86 | ## Credits and links 87 | 88 | I have to thank first of all Vampouille, who created SuperBoucle and fixed some 89 | issues I reported, and Vince who helped me understanding how to start and proceed 90 | in my development. 91 | 92 | Original SuperBoucle master repository is here: 93 | https://github.com/Vampouille/superboucle 94 | 95 | and Vince activities website: 96 | https://www.sonejo.net/ 97 | 98 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5>=5.11 2 | SoundFile>=0.8.0 3 | numpy>=1.16 4 | psutil>=5.7.2 -------------------------------------------------------------------------------- /superboucle/__init__.py: -------------------------------------------------------------------------------- 1 | from superboucle.clip import Clip, Song, load_song_from_file 2 | from superboucle.gui import Gui 3 | #from superboucle.cell import cell_ui 4 | #import jack 5 | import superboucle.cell_ui as cell_ui 6 | import superboucle.qsuperdial 7 | -------------------------------------------------------------------------------- /superboucle/about.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog 2 | from superboucle.about_ui import Ui_Dialog 3 | from PyQt5.QtGui import QDesktopServices 4 | from PyQt5.QtCore import QUrl 5 | import common 6 | 7 | class About(QDialog, Ui_Dialog): 8 | 9 | def __init__(self, parent): 10 | super(About, self).__init__(parent) 11 | self.gui = parent 12 | self.setupUi(self) 13 | self.webLink = "https://github.com/manucontrovento/SpinTool" 14 | self.btnWebLink.clicked.connect(self.onWebLinkDoubleClick) 15 | 16 | self.labelVersion.setText(common.APP_VERSION) 17 | 18 | self.btnWebLink.setText(self.webLink) 19 | 20 | 21 | self.description = '''Current development by 22 | Manu Controvento (Meltin'Pop) and Vincent Rateau (Superdirt³ / Sonejo.net). 23 | Many many thanks go to Vampouille (Julien Acroute) for developing SuperBoucle, 24 | which was the base of SpinTool, and to IARI (Julian Jarecki) for further development on SB. 25 | ''' 26 | 27 | 28 | self.license = '''This is a free and open source application, you can re-distribute and/or change it according to 29 | GNU General Public License version 2 or later. 30 | ''' 31 | 32 | 33 | self.labelText.setText(self.description) 34 | self.labelLicense.setText(self.license) 35 | 36 | self.setModal(True) 37 | self.show() 38 | 39 | def onWebLinkDoubleClick(self): 40 | QDesktopServices.openUrl(QUrl(self.webLink)) 41 | 42 | def onFinished(self): 43 | pass 44 | -------------------------------------------------------------------------------- /superboucle/add_clip.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import Qt 2 | from superboucle.assistant import Assistant 3 | from PyQt5.QtGui import QCursor 4 | import help 5 | from PyQt5.QtWidgets import QDialog, QApplication 6 | from superboucle.add_clip_ui import Ui_Dialog 7 | from superboucle.clip import Clip, basename 8 | import os 9 | import settings 10 | import common 11 | 12 | 13 | class AddClipDialog(QDialog, Ui_Dialog): 14 | def __init__(self, parent, cell): 15 | super(AddClipDialog, self).__init__(parent) 16 | self.gui = parent 17 | self.cell = cell 18 | self.type = None 19 | self.setupUi(self) 20 | 21 | # default choice 22 | self.newButton.setChecked(True) 23 | self.type = 'new' 24 | self.fileList.setEnabled(False) 25 | 26 | self.newButton.clicked.connect(self.onNew) 27 | self.useButton.clicked.connect(self.onUse) 28 | self.emptyButton.clicked.connect(self.onEmpty) 29 | self.accepted.connect(self.onOk) 30 | self.cBoxMetronomeClip.stateChanged.connect(self.onMetronomeClip) 31 | 32 | for wav_id in self.gui.song.data: 33 | self.fileList.addItem(wav_id) 34 | 35 | self.setModal(True) 36 | self.show() 37 | 38 | def onNew(self): 39 | self.type = 'new' 40 | 41 | def onUse(self): 42 | self.type = 'use' 43 | 44 | 45 | def onEmpty(self): 46 | self.type = 'empty' 47 | 48 | 49 | def onMetronomeClip(self): 50 | if self.cBoxMetronomeClip.isChecked(): 51 | 52 | if self.emptyButton.isChecked(): 53 | self.newButton.setChecked(True) 54 | self.emptyButton.setEnabled(False) 55 | self.cBoxLockRec.setChecked(False) 56 | self.cBoxOneShotClip.setChecked(False) 57 | self.cBoxLockRec.setEnabled(False) 58 | self.cBoxOneShotClip.setEnabled(False) 59 | 60 | else: 61 | 62 | self.emptyButton.setEnabled(True) 63 | self.cBoxLockRec.setEnabled(True) 64 | self.cBoxOneShotClip.setEnabled(True) 65 | 66 | 67 | def onOk(self): 68 | 69 | new_clip = None 70 | 71 | if self.type == 'new': 72 | new_clip = self.cell.openClip() 73 | 74 | elif self.type == 'use': 75 | wav_id = self.fileList.currentText() 76 | new_clip = Clip(audio_file=basename(wav_id), name=os.path.splitext(basename(wav_id))[0]) 77 | 78 | elif self.type == 'empty': 79 | new_clip = Clip(audio_file=None, 80 | name='audio-%02d' % len(self.gui.song.clips)) 81 | 82 | if new_clip: 83 | new_clip.one_shot = self.cBoxOneShotClip.isChecked() 84 | new_clip.lock_rec = self.cBoxLockRec.isChecked() 85 | 86 | 87 | if self.cBoxMetronomeClip.isChecked(): 88 | 89 | new_clip.always_play = True 90 | new_clip.mute_group = 0 91 | 92 | # checking if CLICK output port exists; creating it otherwise 93 | click_st_port = common.checkClickPort(settings.output_ports) 94 | 95 | # adding it to jack, ST ports and GUI selection, un-route to master 96 | if click_st_port == False: 97 | self.gui.addPort(common.CLICK_PORT, False) 98 | #settings.output_ports[common.CLICK_PORT]["to_master"] = False # -> un-route port from master 99 | #self.gui.output_mixer.updateGui(settings.output_ports) # -> re-update mixer 100 | #print(settings.output_ports[common.CLICK_PORT]) 101 | 102 | 103 | # Assigning CLICK port to clip 104 | new_clip.output = common.CLICK_PORT 105 | 106 | 107 | self.cell.setClip(new_clip) 108 | 109 | if settings.auto_assign_new_clip_column: 110 | self.gui.autoAssignClipColumn(new_clip) 111 | 112 | 113 | # HELP management --------------------------------------------------------- 114 | 115 | def keyPressEvent(self, event): 116 | 117 | if event.key() == Qt.Key_H: # if pressed key is H (help) 118 | 119 | pos = QCursor.pos() 120 | widget = QApplication.widgetAt(pos) # this is widget under cursor 121 | 122 | if widget is None: return 123 | accName = widget.accessibleName() # this is widget accessible name 124 | 125 | if accName != "": 126 | wantedHelp = help.Context(accName) # Conversion of string accessible name to Context enum 127 | self.showContextHelp(wantedHelp) 128 | 129 | 130 | def showContextHelp(self, wantedHelp): 131 | helpText = help.getContextHelp(wantedHelp) 132 | Assistant(self, helpText, Assistant.MODE_CONTEXT) 133 | -------------------------------------------------------------------------------- /superboucle/add_clip_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/add_clip_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(350, 240) 17 | Dialog.setMinimumSize(QtCore.QSize(350, 240)) 18 | Dialog.setMaximumSize(QtCore.QSize(350, 240)) 19 | Dialog.setStyleSheet("/* background-color: rgb(242, 242, 242);") 20 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog) 21 | self.verticalLayout_2.setObjectName("verticalLayout_2") 22 | self.newButton = QtWidgets.QRadioButton(Dialog) 23 | self.newButton.setStyleSheet("color: rgb(0, 0, 0);\n" 24 | "font: 10pt \"Noto Sans\";") 25 | self.newButton.setObjectName("newButton") 26 | self.verticalLayout_2.addWidget(self.newButton) 27 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 28 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 29 | self.useButton = QtWidgets.QRadioButton(Dialog) 30 | self.useButton.setStyleSheet("/*color: rgb(0, 0, 0);\n" 31 | "font: 10pt \"Noto Sans\";") 32 | self.useButton.setObjectName("useButton") 33 | self.horizontalLayout_2.addWidget(self.useButton) 34 | self.fileList = QtWidgets.QComboBox(Dialog) 35 | self.fileList.setMinimumSize(QtCore.QSize(250, 0)) 36 | self.fileList.setStyleSheet("/*color: rgb(0, 0, 0);") 37 | self.fileList.setObjectName("fileList") 38 | self.horizontalLayout_2.addWidget(self.fileList) 39 | self.verticalLayout_2.addLayout(self.horizontalLayout_2) 40 | self.emptyButton = QtWidgets.QRadioButton(Dialog) 41 | self.emptyButton.setStyleSheet("/*color: rgb(0, 0, 0);\n" 42 | "font: 10pt \"Noto Sans\";") 43 | self.emptyButton.setObjectName("emptyButton") 44 | self.verticalLayout_2.addWidget(self.emptyButton) 45 | self.line = QtWidgets.QFrame(Dialog) 46 | self.line.setFrameShape(QtWidgets.QFrame.HLine) 47 | self.line.setFrameShadow(QtWidgets.QFrame.Sunken) 48 | self.line.setObjectName("line") 49 | self.verticalLayout_2.addWidget(self.line) 50 | self.groupBoxClip = QtWidgets.QGroupBox(Dialog) 51 | self.groupBoxClip.setStyleSheet("/*font: italic 9pt \"Noto Sans\";\n" 52 | "color: rgb(0, 0, 0);\n" 53 | "border: 1;") 54 | self.groupBoxClip.setFlat(False) 55 | self.groupBoxClip.setObjectName("groupBoxClip") 56 | self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBoxClip) 57 | self.verticalLayout.setObjectName("verticalLayout") 58 | self.cBoxMetronomeClip = QtWidgets.QCheckBox(self.groupBoxClip) 59 | self.cBoxMetronomeClip.setStyleSheet("/*color: rgb(0, 0, 0);\n" 60 | "background-color: rgb(242, 242, 242);\n" 61 | "font: 10pt \"Noto Sans\";") 62 | self.cBoxMetronomeClip.setObjectName("cBoxMetronomeClip") 63 | self.verticalLayout.addWidget(self.cBoxMetronomeClip) 64 | self.horizontalLayout = QtWidgets.QHBoxLayout() 65 | self.horizontalLayout.setObjectName("horizontalLayout") 66 | self.cBoxOneShotClip = QtWidgets.QCheckBox(self.groupBoxClip) 67 | self.cBoxOneShotClip.setStyleSheet("/*color: rgb(0, 0, 0);\n" 68 | "background-color: rgb(242, 242, 242);\n" 69 | "font: 10pt \"Noto Sans\";") 70 | self.cBoxOneShotClip.setObjectName("cBoxOneShotClip") 71 | self.horizontalLayout.addWidget(self.cBoxOneShotClip) 72 | self.cBoxLockRec = QtWidgets.QCheckBox(self.groupBoxClip) 73 | font = QtGui.QFont() 74 | font.setFamily("Noto Sans") 75 | font.setPointSize(10) 76 | font.setBold(False) 77 | font.setItalic(False) 78 | font.setWeight(50) 79 | self.cBoxLockRec.setFont(font) 80 | self.cBoxLockRec.setStyleSheet("/*color: rgb(0, 0, 0);\n" 81 | "background-color: rgb(242, 242, 242);\n" 82 | "font: 10pt \"Noto Sans\";") 83 | self.cBoxLockRec.setObjectName("cBoxLockRec") 84 | self.horizontalLayout.addWidget(self.cBoxLockRec) 85 | self.verticalLayout.addLayout(self.horizontalLayout) 86 | self.verticalLayout_2.addWidget(self.groupBoxClip) 87 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 88 | self.verticalLayout_2.addItem(spacerItem) 89 | self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) 90 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 91 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 92 | self.buttonBox.setObjectName("buttonBox") 93 | self.verticalLayout_2.addWidget(self.buttonBox) 94 | 95 | self.retranslateUi(Dialog) 96 | self.buttonBox.accepted.connect(Dialog.accept) 97 | self.buttonBox.rejected.connect(Dialog.reject) 98 | self.useButton.toggled['bool'].connect(self.fileList.setEnabled) 99 | QtCore.QMetaObject.connectSlotsByName(Dialog) 100 | 101 | def retranslateUi(self, Dialog): 102 | _translate = QtCore.QCoreApplication.translate 103 | Dialog.setWindowTitle(_translate("Dialog", "Create new clip")) 104 | self.newButton.setText(_translate("Dialog", "Load new file...")) 105 | self.useButton.setText(_translate("Dialog", "Use file:")) 106 | self.emptyButton.setText(_translate("Dialog", "Empty clip")) 107 | self.groupBoxClip.setTitle(_translate("Dialog", "Clip behaviour")) 108 | self.cBoxMetronomeClip.setAccessibleName(_translate("Dialog", "Help_Clip_Metronome")) 109 | self.cBoxMetronomeClip.setText(_translate("Dialog", "Create a Click (metronome) clip")) 110 | self.cBoxOneShotClip.setText(_translate("Dialog", "One-shot clip")) 111 | self.cBoxLockRec.setText(_translate("Dialog", "Lock clip (no recording)")) 112 | -------------------------------------------------------------------------------- /superboucle/add_clip_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 350 10 | 240 11 | 12 | 13 | 14 | 15 | 350 16 | 240 17 | 18 | 19 | 20 | 21 | 350 22 | 240 23 | 24 | 25 | 26 | Create new clip 27 | 28 | 29 | /* background-color: rgb(242, 242, 242); 30 | 31 | 32 | 33 | 34 | 35 | color: rgb(0, 0, 0); 36 | font: 10pt "Noto Sans"; 37 | 38 | 39 | Load new file... 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | /*color: rgb(0, 0, 0); 49 | font: 10pt "Noto Sans"; 50 | 51 | 52 | Use file: 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 250 61 | 0 62 | 63 | 64 | 65 | /*color: rgb(0, 0, 0); 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | /*color: rgb(0, 0, 0); 75 | font: 10pt "Noto Sans"; 76 | 77 | 78 | Empty clip 79 | 80 | 81 | 82 | 83 | 84 | 85 | Qt::Horizontal 86 | 87 | 88 | 89 | 90 | 91 | 92 | /*font: italic 9pt "Noto Sans"; 93 | color: rgb(0, 0, 0); 94 | border: 1; 95 | 96 | 97 | Clip behaviour 98 | 99 | 100 | false 101 | 102 | 103 | 104 | 105 | 106 | Help_Clip_Metronome 107 | 108 | 109 | /*color: rgb(0, 0, 0); 110 | background-color: rgb(242, 242, 242); 111 | font: 10pt "Noto Sans"; 112 | 113 | 114 | Create a Click (metronome) clip 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | /*color: rgb(0, 0, 0); 124 | background-color: rgb(242, 242, 242); 125 | font: 10pt "Noto Sans"; 126 | 127 | 128 | One-shot clip 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | Noto Sans 137 | 10 138 | 50 139 | false 140 | false 141 | 142 | 143 | 144 | /*color: rgb(0, 0, 0); 145 | background-color: rgb(242, 242, 242); 146 | font: 10pt "Noto Sans"; 147 | 148 | 149 | Lock clip (no recording) 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | Qt::Vertical 162 | 163 | 164 | 165 | 20 166 | 40 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | Qt::Horizontal 175 | 176 | 177 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | buttonBox 187 | accepted() 188 | Dialog 189 | accept() 190 | 191 | 192 | 248 193 | 254 194 | 195 | 196 | 157 197 | 274 198 | 199 | 200 | 201 | 202 | buttonBox 203 | rejected() 204 | Dialog 205 | reject() 206 | 207 | 208 | 316 209 | 260 210 | 211 | 212 | 286 213 | 274 214 | 215 | 216 | 217 | 218 | useButton 219 | toggled(bool) 220 | fileList 221 | setEnabled(bool) 222 | 223 | 224 | 70 225 | 67 226 | 227 | 228 | 144 229 | 67 230 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /superboucle/add_port.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QMessageBox 2 | from superboucle.add_port_ui import Ui_Dialog 3 | 4 | import common 5 | import settings 6 | 7 | class AddPortDialog(QDialog, Ui_Dialog): 8 | def __init__(self, gui, callback=None): 9 | super(AddPortDialog, self).__init__(gui) 10 | self.gui = gui 11 | self.callback = callback 12 | self.setupUi(self) 13 | self.accepted.connect(self.onOk) 14 | self.rejected.connect(self.onCancel) 15 | self.show() 16 | self.name.setFocus() 17 | 18 | def onOk(self): 19 | 20 | port_name = self.name.text().upper().strip() 21 | 22 | if (port_name == common.DEFAULT_PORT.upper() or port_name == common.MASTER_PORT.upper() 23 | or port_name == common.CLICK_PORT.upper() or port_name == common.SEND1_PORT.upper() 24 | or port_name == common.SEND2_PORT.upper()): 25 | 26 | message = QMessageBox(self) 27 | message.setWindowTitle("Can't create port " + self.name.text()) 28 | message.setText(self.name.text() + " is a reserved SpinTool system port") 29 | message.show() 30 | return 31 | 32 | if "_L" in port_name or "_R" in port_name: 33 | message = QMessageBox(self) 34 | message.setWindowTitle("Can't create port " + self.name.text()) 35 | message.setText("_L and _R are reserved by SpinTool and are not allowed in port name") 36 | message.show() 37 | return 38 | 39 | for existingPort in settings.output_ports.keys(): 40 | if port_name == existingPort.upper(): 41 | message = QMessageBox(self) 42 | message.setWindowTitle("Can't create port " + self.name.text()) 43 | message.setText(self.name.text() + " output port is already existing") 44 | message.show() 45 | return 46 | 47 | self.gui.addPort(self.name.text()) 48 | 49 | if self.callback: 50 | self.callback() 51 | 52 | 53 | def onCancel(self): 54 | if self.gui.last_clip: 55 | self.gui.output.setCurrentText(self.gui.last_clip.output) 56 | -------------------------------------------------------------------------------- /superboucle/add_port_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Applicazioni/SpinTool/spintool/add_port_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.12.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(301, 88) 17 | Dialog.setStyleSheet("background-color: rgb(242, 242, 242);") 18 | self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) 19 | self.buttonBox.setGeometry(QtCore.QRect(70, 50, 171, 32)) 20 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 21 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 22 | self.buttonBox.setCenterButtons(True) 23 | self.buttonBox.setObjectName("buttonBox") 24 | self.name = QtWidgets.QLineEdit(Dialog) 25 | self.name.setGeometry(QtCore.QRect(130, 10, 151, 23)) 26 | self.name.setStyleSheet("color: rgb(0, 0, 0);\n" 27 | "background-color: rgb(255, 255, 255);") 28 | self.name.setObjectName("name") 29 | self.label = QtWidgets.QLabel(Dialog) 30 | self.label.setGeometry(QtCore.QRect(20, 15, 111, 16)) 31 | self.label.setStyleSheet("color: rgb(0, 0, 0);") 32 | self.label.setObjectName("label") 33 | 34 | self.retranslateUi(Dialog) 35 | self.buttonBox.accepted.connect(Dialog.accept) 36 | self.buttonBox.rejected.connect(Dialog.reject) 37 | QtCore.QMetaObject.connectSlotsByName(Dialog) 38 | 39 | def retranslateUi(self, Dialog): 40 | _translate = QtCore.QCoreApplication.translate 41 | Dialog.setWindowTitle(_translate("Dialog", "Add new Port...")) 42 | self.label.setText(_translate("Dialog", "New Port name :")) 43 | -------------------------------------------------------------------------------- /superboucle/add_port_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 301 10 | 88 11 | 12 | 13 | 14 | Add new Port... 15 | 16 | 17 | background-color: rgb(242, 242, 242); 18 | 19 | 20 | 21 | 22 | 70 23 | 50 24 | 171 25 | 32 26 | 27 | 28 | 29 | Qt::Horizontal 30 | 31 | 32 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 33 | 34 | 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 130 42 | 10 43 | 151 44 | 23 45 | 46 | 47 | 48 | color: rgb(0, 0, 0); 49 | background-color: rgb(255, 255, 255); 50 | 51 | 52 | 53 | 54 | 55 | 20 56 | 15 57 | 111 58 | 16 59 | 60 | 61 | 62 | color: rgb(0, 0, 0); 63 | 64 | 65 | New Port name : 66 | 67 | 68 | 69 | 70 | 71 | 72 | buttonBox 73 | accepted() 74 | Dialog 75 | accept() 76 | 77 | 78 | 248 79 | 254 80 | 81 | 82 | 157 83 | 274 84 | 85 | 86 | 87 | 88 | buttonBox 89 | rejected() 90 | Dialog 91 | reject() 92 | 93 | 94 | 316 95 | 260 96 | 97 | 98 | 286 99 | 274 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /superboucle/add_scene.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import Qt 2 | from superboucle.assistant import Assistant 3 | from PyQt5.QtGui import QCursor 4 | import help 5 | from PyQt5.QtWidgets import QDialog, QApplication 6 | from superboucle.add_scene_ui import Ui_Dialog 7 | 8 | class AddSceneDialog(QDialog, Ui_Dialog): 9 | def __init__(self, gui, callback=None): 10 | super(AddSceneDialog, self).__init__(gui) 11 | self.gui = gui 12 | self.callback = callback 13 | self.setupUi(self) 14 | self.accepted.connect(self.onOk) 15 | 16 | if self.gui.song.selectedClips().__len__() > 0: 17 | # if there are some Selected clip in Song, including them is suggested: 18 | self.checkBoxIncludeSelected.setChecked(True) 19 | self.checkBoxIncludeStart.setChecked(False) 20 | else: 21 | # otherwise including starting / playing clips is suggested: 22 | self.checkBoxIncludeSelected.setChecked(False) 23 | self.checkBoxIncludeStart.setChecked(True) 24 | 25 | self.show() 26 | self.name.setFocus() 27 | 28 | def onOk(self): 29 | self.gui.song.addScene(self.name.text(), self.checkBoxIncludeStart.isChecked(), self.checkBoxIncludeSelected.isChecked()) 30 | if self.callback: 31 | self.callback() 32 | 33 | # HELP management --------------------------------------------------------- 34 | 35 | def keyPressEvent(self, event): 36 | 37 | if event.key() == Qt.Key_H: # if pressed key is H (help) 38 | 39 | pos = QCursor.pos() 40 | widget = QApplication.widgetAt(pos) # this is widget under cursor 41 | 42 | if widget is None: return 43 | accName = widget.accessibleName() # this is widget accessible name 44 | 45 | if accName != "": 46 | wantedHelp = help.Context(accName) # Conversion of string accessible name to Context enum 47 | self.showContextHelp(wantedHelp) 48 | 49 | 50 | def showContextHelp(self, wantedHelp): 51 | helpText = help.getContextHelp(wantedHelp) 52 | Assistant(self, helpText, Assistant.MODE_CONTEXT) -------------------------------------------------------------------------------- /superboucle/add_scene_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/add_scene_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(482, 171) 17 | self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) 18 | self.buttonBox.setGeometry(QtCore.QRect(360, 0, 111, 91)) 19 | self.buttonBox.setOrientation(QtCore.Qt.Vertical) 20 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 21 | self.buttonBox.setCenterButtons(True) 22 | self.buttonBox.setObjectName("buttonBox") 23 | self.name = QtWidgets.QLineEdit(Dialog) 24 | self.name.setGeometry(QtCore.QRect(130, 12, 211, 31)) 25 | self.name.setObjectName("name") 26 | self.label = QtWidgets.QLabel(Dialog) 27 | self.label.setGeometry(QtCore.QRect(17, 20, 111, 16)) 28 | self.label.setObjectName("label") 29 | self.checkBoxIncludeSelected = QtWidgets.QCheckBox(Dialog) 30 | self.checkBoxIncludeSelected.setGeometry(QtCore.QRect(10, 70, 341, 29)) 31 | self.checkBoxIncludeSelected.setObjectName("checkBoxIncludeSelected") 32 | self.checkBoxIncludeStart = QtWidgets.QCheckBox(Dialog) 33 | self.checkBoxIncludeStart.setGeometry(QtCore.QRect(10, 110, 341, 29)) 34 | self.checkBoxIncludeStart.setObjectName("checkBoxIncludeStart") 35 | 36 | self.retranslateUi(Dialog) 37 | self.buttonBox.accepted.connect(Dialog.accept) 38 | self.buttonBox.rejected.connect(Dialog.reject) 39 | QtCore.QMetaObject.connectSlotsByName(Dialog) 40 | 41 | def retranslateUi(self, Dialog): 42 | _translate = QtCore.QCoreApplication.translate 43 | Dialog.setWindowTitle(_translate("Dialog", "Add new Scene...")) 44 | self.label.setText(_translate("Dialog", "New Scene name :")) 45 | self.checkBoxIncludeSelected.setAccessibleName(_translate("Dialog", "Help_Scene_Include")) 46 | self.checkBoxIncludeSelected.setText(_translate("Dialog", "include selected clips")) 47 | self.checkBoxIncludeStart.setAccessibleName(_translate("Dialog", "Help_Scene_Include")) 48 | self.checkBoxIncludeStart.setText(_translate("Dialog", "include playing / starting clips")) 49 | -------------------------------------------------------------------------------- /superboucle/add_scene_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 482 10 | 171 11 | 12 | 13 | 14 | Add new Scene... 15 | 16 | 17 | 18 | 19 | 360 20 | 0 21 | 111 22 | 91 23 | 24 | 25 | 26 | Qt::Vertical 27 | 28 | 29 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 30 | 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 130 39 | 12 40 | 211 41 | 31 42 | 43 | 44 | 45 | 46 | 47 | 48 | 17 49 | 20 50 | 111 51 | 16 52 | 53 | 54 | 55 | New Scene name : 56 | 57 | 58 | 59 | 60 | 61 | 10 62 | 70 63 | 341 64 | 29 65 | 66 | 67 | 68 | Help_Scene_Include 69 | 70 | 71 | include selected clips 72 | 73 | 74 | 75 | 76 | 77 | 10 78 | 110 79 | 341 80 | 29 81 | 82 | 83 | 84 | Help_Scene_Include 85 | 86 | 87 | include playing / starting clips 88 | 89 | 90 | 91 | 92 | 93 | 94 | buttonBox 95 | accepted() 96 | Dialog 97 | accept() 98 | 99 | 100 | 248 101 | 254 102 | 103 | 104 | 157 105 | 274 106 | 107 | 108 | 109 | 110 | buttonBox 111 | rejected() 112 | Dialog 113 | reject() 114 | 115 | 116 | 316 117 | 260 118 | 119 | 120 | 286 121 | 274 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /superboucle/assistant.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog 2 | from superboucle.assistant_ui import Ui_Dialog 3 | import copy 4 | import settings 5 | 6 | class Assistant(QDialog, Ui_Dialog): 7 | 8 | MODE_MANUAL = 1 9 | MODE_CONTEXT = 2 10 | 11 | def __init__(self, parent, text, mode): 12 | super(Assistant, self).__init__(parent) 13 | self.gui = parent 14 | 15 | self.setupUi(self) 16 | self.updateText(text) 17 | 18 | self.txtHelp.setReadOnly(True) 19 | self.labelLogo.setDisabled(True) 20 | 21 | self.setLayout(self.verticalLayout) 22 | 23 | if mode == self.MODE_MANUAL: 24 | self.displayAsManual() 25 | elif mode == self.MODE_CONTEXT: 26 | self.displayAsContext() 27 | 28 | self.show() 29 | 30 | 31 | def updateText(self, text): 32 | self.txtHelp.setText(text) 33 | 34 | def displayAsContext(self): 35 | self.labelAssistant.setText("Assistant") 36 | self.resize(680, 460) 37 | 38 | def displayAsManual(self): 39 | self.labelAssistant.setText("User Manual") 40 | self.resize(790, 650) 41 | 42 | def onFinished(self): 43 | pass 44 | -------------------------------------------------------------------------------- /superboucle/assistant_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'assistant_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.setWindowModality(QtCore.Qt.WindowModal) 17 | Dialog.resize(774, 517) 18 | Dialog.setMinimumSize(QtCore.QSize(560, 360)) 19 | icon = QtGui.QIcon() 20 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 21 | Dialog.setWindowIcon(icon) 22 | Dialog.setStyleSheet("background-color: rgb(242, 242, 242);") 23 | self.verticalLayoutWidget = QtWidgets.QWidget(Dialog) 24 | self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 10, 771, 501)) 25 | self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") 26 | self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) 27 | self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) 28 | self.verticalLayout.setContentsMargins(0, 2, 0, 0) 29 | self.verticalLayout.setObjectName("verticalLayout") 30 | self.labelLogo = QtWidgets.QLabel(self.verticalLayoutWidget) 31 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 32 | sizePolicy.setHorizontalStretch(0) 33 | sizePolicy.setVerticalStretch(0) 34 | sizePolicy.setHeightForWidth(self.labelLogo.sizePolicy().hasHeightForWidth()) 35 | self.labelLogo.setSizePolicy(sizePolicy) 36 | self.labelLogo.setMinimumSize(QtCore.QSize(0, 0)) 37 | self.labelLogo.setMaximumSize(QtCore.QSize(16777215, 16777214)) 38 | self.labelLogo.setText("") 39 | self.labelLogo.setPixmap(QtGui.QPixmap(":/icons/icons/ST_Logo_Small_Transparent.png")) 40 | self.labelLogo.setScaledContents(False) 41 | self.labelLogo.setAlignment(QtCore.Qt.AlignCenter) 42 | self.labelLogo.setIndent(-1) 43 | self.labelLogo.setObjectName("labelLogo") 44 | self.verticalLayout.addWidget(self.labelLogo) 45 | self.labelAssistant = QtWidgets.QLabel(self.verticalLayoutWidget) 46 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 47 | sizePolicy.setHorizontalStretch(0) 48 | sizePolicy.setVerticalStretch(0) 49 | sizePolicy.setHeightForWidth(self.labelAssistant.sizePolicy().hasHeightForWidth()) 50 | self.labelAssistant.setSizePolicy(sizePolicy) 51 | self.labelAssistant.setStyleSheet("color: rgb(160, 160, 160);\n" 52 | "font: bold italic 18pt \"Noto Sans\";") 53 | self.labelAssistant.setAlignment(QtCore.Qt.AlignCenter) 54 | self.labelAssistant.setObjectName("labelAssistant") 55 | self.verticalLayout.addWidget(self.labelAssistant) 56 | self.horizontalLayout = QtWidgets.QHBoxLayout() 57 | self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) 58 | self.horizontalLayout.setContentsMargins(10, 10, 10, 10) 59 | self.horizontalLayout.setSpacing(5) 60 | self.horizontalLayout.setObjectName("horizontalLayout") 61 | self.txtHelp = QtWidgets.QTextBrowser(self.verticalLayoutWidget) 62 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 63 | sizePolicy.setHorizontalStretch(1) 64 | sizePolicy.setVerticalStretch(1) 65 | sizePolicy.setHeightForWidth(self.txtHelp.sizePolicy().hasHeightForWidth()) 66 | self.txtHelp.setSizePolicy(sizePolicy) 67 | self.txtHelp.setStyleSheet("color: rgb(0, 0, 0);\n" 68 | "background-color: rgb(242, 242, 242);\n" 69 | "font: 12pt \"Noto Sans\";\n" 70 | "border: None;") 71 | self.txtHelp.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 72 | self.txtHelp.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 73 | self.txtHelp.setDocumentTitle("") 74 | self.txtHelp.setReadOnly(False) 75 | self.txtHelp.setAcceptRichText(True) 76 | self.txtHelp.setObjectName("txtHelp") 77 | self.horizontalLayout.addWidget(self.txtHelp) 78 | self.verticalLayout.addLayout(self.horizontalLayout) 79 | 80 | self.retranslateUi(Dialog) 81 | QtCore.QMetaObject.connectSlotsByName(Dialog) 82 | 83 | def retranslateUi(self, Dialog): 84 | _translate = QtCore.QCoreApplication.translate 85 | Dialog.setWindowTitle(_translate("Dialog", "SpinTool Assistant")) 86 | self.labelAssistant.setText(_translate("Dialog", "assistant")) 87 | self.txtHelp.setHtml(_translate("Dialog", "\n" 88 | "\n" 91 | "


")) 92 | import gui_rc 93 | -------------------------------------------------------------------------------- /superboucle/assistant_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 774 13 | 517 14 | 15 | 16 | 17 | 18 | 560 19 | 360 20 | 21 | 22 | 23 | SpinTool Assistant 24 | 25 | 26 | 27 | :/icons/icons/icon.png:/icons/icons/icon.png 28 | 29 | 30 | background-color: rgb(242, 242, 242); 31 | 32 | 33 | 34 | 35 | 0 36 | 10 37 | 771 38 | 501 39 | 40 | 41 | 42 | 43 | QLayout::SetMaximumSize 44 | 45 | 46 | 2 47 | 48 | 49 | 50 | 51 | 52 | 0 53 | 0 54 | 55 | 56 | 57 | 58 | 0 59 | 0 60 | 61 | 62 | 63 | 64 | 16777215 65 | 16777214 66 | 67 | 68 | 69 | 70 | 71 | 72 | :/icons/icons/ST_Logo_Small_Transparent.png 73 | 74 | 75 | false 76 | 77 | 78 | Qt::AlignCenter 79 | 80 | 81 | -8 82 | 83 | 84 | -1 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 0 93 | 0 94 | 95 | 96 | 97 | color: rgb(160, 160, 160); 98 | font: bold italic 18pt "Noto Sans"; 99 | 100 | 101 | assistant 102 | 103 | 104 | Qt::AlignCenter 105 | 106 | 107 | 108 | 109 | 110 | 111 | 5 112 | 113 | 114 | QLayout::SetMaximumSize 115 | 116 | 117 | 10 118 | 119 | 120 | 121 | 122 | 123 | 1 124 | 1 125 | 126 | 127 | 128 | color: rgb(0, 0, 0); 129 | background-color: rgb(242, 242, 242); 130 | font: 12pt "Noto Sans"; 131 | border: None; 132 | 133 | 134 | Qt::ScrollBarAlwaysOn 135 | 136 | 137 | Qt::ScrollBarAlwaysOff 138 | 139 | 140 | 141 | 142 | 143 | false 144 | 145 | 146 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 147 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 148 | p, li { white-space: pre-wrap; } 149 | </style></head><body style=" font-family:'Noto Sans'; font-size:12pt; font-weight:400; font-style:normal;"> 150 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p></body></html> 151 | 152 | 153 | true 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /superboucle/cell_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/cell_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Cell(object): 14 | def setupUi(self, Cell): 15 | Cell.setObjectName("Cell") 16 | Cell.resize(255, 225) 17 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 18 | sizePolicy.setHorizontalStretch(1) 19 | sizePolicy.setVerticalStretch(1) 20 | sizePolicy.setHeightForWidth(Cell.sizePolicy().hasHeightForWidth()) 21 | Cell.setSizePolicy(sizePolicy) 22 | Cell.setMinimumSize(QtCore.QSize(120, 112)) 23 | font = QtGui.QFont() 24 | font.setFamily("Lato") 25 | Cell.setFont(font) 26 | self.cell_frame = QtWidgets.QFrame(Cell) 27 | self.cell_frame.setGeometry(QtCore.QRect(0, 0, 110, 102)) 28 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) 29 | sizePolicy.setHorizontalStretch(0) 30 | sizePolicy.setVerticalStretch(0) 31 | sizePolicy.setHeightForWidth(self.cell_frame.sizePolicy().hasHeightForWidth()) 32 | self.cell_frame.setSizePolicy(sizePolicy) 33 | self.cell_frame.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) 34 | self.cell_frame.setStyleSheet("#frame {border: 0px;\n" 35 | " background-color: rgb(190, 190, 190);\n" 36 | "border-radius: 10px;}") 37 | self.cell_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) 38 | self.cell_frame.setFrameShadow(QtWidgets.QFrame.Raised) 39 | self.cell_frame.setObjectName("cell_frame") 40 | self.clip_name = QtWidgets.QLabel(self.cell_frame) 41 | self.clip_name.setGeometry(QtCore.QRect(3, 2, 105, 46)) 42 | font = QtGui.QFont() 43 | font.setFamily("Noto Sans") 44 | font.setPointSize(14) 45 | font.setBold(True) 46 | font.setItalic(False) 47 | font.setWeight(75) 48 | self.clip_name.setFont(font) 49 | self.clip_name.setStyleSheet("font: bold 14pt \"Noto Sans\";\n" 50 | "color: rgb(0, 0, 0);\n" 51 | "text-align: center;") 52 | self.clip_name.setInputMethodHints(QtCore.Qt.ImhNone) 53 | self.clip_name.setText("") 54 | self.clip_name.setAlignment(QtCore.Qt.AlignCenter) 55 | self.clip_name.setWordWrap(True) 56 | self.clip_name.setObjectName("clip_name") 57 | self.clip_position = QtWidgets.QProgressBar(self.cell_frame) 58 | self.clip_position.setGeometry(QtCore.QRect(5, 85, 99, 13)) 59 | self.clip_position.setStyleSheet("color: rgb(0, 85, 255);\n" 60 | "background-color: rgb(190, 190, 190);") 61 | self.clip_position.setMaximum(97) 62 | self.clip_position.setProperty("value", 0) 63 | self.clip_position.setTextVisible(False) 64 | self.clip_position.setObjectName("clip_position") 65 | self.edit = QtWidgets.QPushButton(self.cell_frame) 66 | self.edit.setGeometry(QtCore.QRect(70, 49, 33, 33)) 67 | font = QtGui.QFont() 68 | font.setFamily("Noto Sans") 69 | font.setPointSize(10) 70 | font.setBold(False) 71 | font.setItalic(True) 72 | font.setWeight(50) 73 | self.edit.setFont(font) 74 | self.edit.setFocusPolicy(QtCore.Qt.NoFocus) 75 | self.edit.setStyleSheet("font: italic 10pt \"Noto Sans\";\n" 76 | "color: rgb(0, 0, 0);\n" 77 | "background-color: rgb(190, 190, 190);\n" 78 | "text-align: center;") 79 | self.edit.setText("") 80 | icon = QtGui.QIcon() 81 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/clip-add.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 82 | self.edit.setIcon(icon) 83 | self.edit.setIconSize(QtCore.QSize(35, 35)) 84 | self.edit.setAutoDefault(False) 85 | self.edit.setDefault(False) 86 | self.edit.setFlat(False) 87 | self.edit.setObjectName("edit") 88 | self.start_stop = QtWidgets.QPushButton(self.cell_frame) 89 | self.start_stop.setGeometry(QtCore.QRect(5, 49, 33, 33)) 90 | font = QtGui.QFont() 91 | font.setFamily("Noto Sans") 92 | font.setPointSize(10) 93 | font.setBold(False) 94 | font.setItalic(True) 95 | font.setWeight(50) 96 | self.start_stop.setFont(font) 97 | self.start_stop.setFocusPolicy(QtCore.Qt.NoFocus) 98 | self.start_stop.setStyleSheet("font: italic 10pt \"Noto Sans\";\n" 99 | "background-color: rgb(190, 190, 190);\n" 100 | "color: rgb(0, 0, 0);\n" 101 | "text-align: center;") 102 | self.start_stop.setText("") 103 | icon1 = QtGui.QIcon() 104 | icon1.addPixmap(QtGui.QPixmap(":/icons/icons/clip-start-stop.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 105 | self.start_stop.setIcon(icon1) 106 | self.start_stop.setIconSize(QtCore.QSize(27, 27)) 107 | self.start_stop.setObjectName("start_stop") 108 | self.labelVolume = QtWidgets.QLabel(self.cell_frame) 109 | self.labelVolume.setGeometry(QtCore.QRect(0, 49, 111, 33)) 110 | self.labelVolume.setStyleSheet("font: bold 12pt \"Noto Sans\";") 111 | self.labelVolume.setTextFormat(QtCore.Qt.PlainText) 112 | self.labelVolume.setScaledContents(False) 113 | self.labelVolume.setAlignment(QtCore.Qt.AlignCenter) 114 | self.labelVolume.setWordWrap(True) 115 | self.labelVolume.setIndent(0) 116 | self.labelVolume.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) 117 | self.labelVolume.setObjectName("labelVolume") 118 | self.labelVolume.raise_() 119 | self.clip_name.raise_() 120 | self.clip_position.raise_() 121 | self.edit.raise_() 122 | self.start_stop.raise_() 123 | 124 | self.retranslateUi(Cell) 125 | QtCore.QMetaObject.connectSlotsByName(Cell) 126 | 127 | def retranslateUi(self, Cell): 128 | _translate = QtCore.QCoreApplication.translate 129 | Cell.setWindowTitle(_translate("Cell", "Form")) 130 | self.cell_frame.setAccessibleName(_translate("Cell", "Help_Cell_Info")) 131 | self.clip_name.setAccessibleName(_translate("Cell", "Help_Cell_Info")) 132 | self.clip_position.setAccessibleName(_translate("Cell", "Help_Cell_Info")) 133 | self.edit.setAccessibleName(_translate("Cell", "Help_Cell_Info")) 134 | self.start_stop.setAccessibleName(_translate("Cell", "Help_Cell_Info")) 135 | self.labelVolume.setAccessibleName(_translate("Cell", "Help_Cell_Info")) 136 | self.labelVolume.setText(_translate("Cell", "5")) 137 | import gui_rc 138 | -------------------------------------------------------------------------------- /superboucle/cell_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cell 4 | 5 | 6 | 7 | 0 8 | 0 9 | 255 10 | 225 11 | 12 | 13 | 14 | 15 | 1 16 | 1 17 | 18 | 19 | 20 | 21 | 120 22 | 112 23 | 24 | 25 | 26 | 27 | Lato 28 | 29 | 30 | 31 | Form 32 | 33 | 34 | 35 | 36 | 0 37 | 0 38 | 110 39 | 102 40 | 41 | 42 | 43 | 44 | 0 45 | 0 46 | 47 | 48 | 49 | PointingHandCursor 50 | 51 | 52 | Help_Cell_Info 53 | 54 | 55 | #frame {border: 0px; 56 | background-color: rgb(190, 190, 190); 57 | border-radius: 10px;} 58 | 59 | 60 | QFrame::StyledPanel 61 | 62 | 63 | QFrame::Raised 64 | 65 | 66 | 67 | 68 | 3 69 | 2 70 | 105 71 | 46 72 | 73 | 74 | 75 | 76 | Noto Sans 77 | 14 78 | 75 79 | false 80 | true 81 | 82 | 83 | 84 | Help_Cell_Info 85 | 86 | 87 | font: bold 14pt "Noto Sans"; 88 | color: rgb(0, 0, 0); 89 | text-align: center; 90 | 91 | 92 | Qt::ImhNone 93 | 94 | 95 | 96 | 97 | 98 | Qt::AlignCenter 99 | 100 | 101 | true 102 | 103 | 104 | 105 | 106 | 107 | 5 108 | 85 109 | 99 110 | 13 111 | 112 | 113 | 114 | Help_Cell_Info 115 | 116 | 117 | color: rgb(0, 85, 255); 118 | background-color: rgb(190, 190, 190); 119 | 120 | 121 | 97 122 | 123 | 124 | 0 125 | 126 | 127 | false 128 | 129 | 130 | 131 | 132 | 133 | 70 134 | 49 135 | 33 136 | 33 137 | 138 | 139 | 140 | 141 | Noto Sans 142 | 10 143 | 50 144 | true 145 | false 146 | 147 | 148 | 149 | Qt::NoFocus 150 | 151 | 152 | Help_Cell_Info 153 | 154 | 155 | font: italic 10pt "Noto Sans"; 156 | color: rgb(0, 0, 0); 157 | background-color: rgb(190, 190, 190); 158 | text-align: center; 159 | 160 | 161 | 162 | 163 | 164 | 165 | :/icons/icons/clip-add.png:/icons/icons/clip-add.png 166 | 167 | 168 | 169 | 35 170 | 35 171 | 172 | 173 | 174 | false 175 | 176 | 177 | false 178 | 179 | 180 | false 181 | 182 | 183 | 184 | 185 | 186 | 5 187 | 49 188 | 33 189 | 33 190 | 191 | 192 | 193 | 194 | Noto Sans 195 | 10 196 | 50 197 | true 198 | false 199 | 200 | 201 | 202 | Qt::NoFocus 203 | 204 | 205 | Help_Cell_Info 206 | 207 | 208 | font: italic 10pt "Noto Sans"; 209 | background-color: rgb(190, 190, 190); 210 | color: rgb(0, 0, 0); 211 | text-align: center; 212 | 213 | 214 | 215 | 216 | 217 | 218 | :/icons/icons/clip-start-stop.png:/icons/icons/clip-start-stop.png 219 | 220 | 221 | 222 | 27 223 | 27 224 | 225 | 226 | 227 | 228 | 229 | 230 | 0 231 | 49 232 | 111 233 | 33 234 | 235 | 236 | 237 | Help_Cell_Info 238 | 239 | 240 | font: bold 12pt "Noto Sans"; 241 | 242 | 243 | 5 244 | 245 | 246 | Qt::PlainText 247 | 248 | 249 | false 250 | 251 | 252 | Qt::AlignCenter 253 | 254 | 255 | true 256 | 257 | 258 | 0 259 | 260 | 261 | 0 262 | 263 | 264 | Qt::NoTextInteraction 265 | 266 | 267 | labelVolume 268 | clip_name 269 | clip_position 270 | edit 271 | start_stop 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /superboucle/device.py: -------------------------------------------------------------------------------- 1 | from superboucle.clip import Clip 2 | from PyQt5.QtCore import QSettings 3 | from superboucle.preferences import Preferences 4 | import settings 5 | 6 | class DeviceOutput: 7 | def __init__(self, method, name=None): 8 | self.method = method 9 | self.name = name or method.__name__ 10 | self.__doc__ = method.__doc__ 11 | 12 | def get_mapping(self, inst): 13 | return inst.mapping 14 | 15 | def __get__(self, inst, cls=None): 16 | mapping = self.get_mapping(inst) 17 | return mapping.setdefault(self.name, self.method(inst)) 18 | 19 | def __set__(self, inst, value): 20 | mapping = self.get_mapping(inst) 21 | mapping[self.name] = value 22 | 23 | def __delete__(self, inst): 24 | mapping = self.get_mapping(inst) 25 | del mapping[self.name] 26 | 27 | 28 | class DeviceInput(DeviceOutput): 29 | pass 30 | 31 | 32 | class Device: 33 | 34 | NOTEON = 0x9 35 | 36 | def __init__(self, mapping={}): 37 | self.updateMapping(mapping) 38 | 39 | def updateMapping(self, new_mapping): 40 | self.note_to_coord = {} 41 | for key in new_mapping.keys(): 42 | new_mapping[key] = self._formatMapping(new_mapping[key]) 43 | self.mapping = new_mapping 44 | for y in range(len(self.start_stop)): 45 | line = self.start_stop[y] 46 | for x in range(len(line)): 47 | self.note_to_coord[line[x]] = (x, y) 48 | 49 | def _formatMapping(self, value): 50 | if type(value) is not list or not len(value): 51 | return value 52 | elif type(value[0]) is int: 53 | return tuple(value) 54 | elif type(value[0]) is list: 55 | return [self._formatMapping(v) for v in value] 56 | else: 57 | return value 58 | 59 | def generateNote(self, x, y, state): 60 | (msg_type, channel, pitch, velocity) = self.start_stop[y][x] 61 | return (0x90 + channel, pitch, self.getColor(state)) # note on : 0x90 62 | 63 | def getColor(self, state): 64 | 65 | if state is None: 66 | return self.black_vel 67 | 68 | elif state == Clip.STOP: 69 | if settings.rec_color == settings.COLOR_RED: 70 | return self.amber_vel 71 | else: 72 | return self.red_vel 73 | 74 | elif state == Clip.STARTING: 75 | return self.blink_green_vel 76 | 77 | elif state == Clip.START: 78 | return self.green_vel 79 | 80 | elif state == Clip.STOPPING: 81 | if settings.rec_color == settings.COLOR_RED: 82 | return self.blink_amber_vel 83 | else: 84 | return self.blink_red_vel 85 | 86 | elif state == Clip.PREPARE_RECORD: 87 | if settings.rec_color == settings.COLOR_RED: 88 | return self.blink_red_vel 89 | else: 90 | return self.blink_amber_vel 91 | 92 | elif state == Clip.RECORDING: 93 | if settings.rec_color == settings.COLOR_RED: 94 | return self.red_vel 95 | else: 96 | return self.amber_vel 97 | 98 | else: 99 | raise Exception("Invalid state") 100 | 101 | def setAllCellsColor(self, queue_out, color): 102 | # clips 103 | for line in self.start_stop: 104 | for data in line: 105 | (m, channel, pitch, velocity) = data 106 | note = ((self.NOTEON << 4) + channel, pitch, color) 107 | queue_out.put(note) 108 | 109 | # mute buttons 110 | for btn_key in self.mute_buttons: 111 | (msg_type, channel, pitch, velocity) = btn_key 112 | note = ((self.NOTEON << 4) + channel, pitch, color) 113 | queue_out.put(note) 114 | 115 | # scenes 116 | for scene_key in self.scene_buttons: 117 | (msg_type, channel, pitch, velocity) = scene_key 118 | note = ((self.NOTEON << 4) + channel, pitch, color) 119 | queue_out.put(note) 120 | 121 | # Shift 122 | if self.shift_btn: 123 | (msg_type, channel, pitch, velocity) = self.shift_btn 124 | note = ((self.NOTEON << 4) + channel, pitch, color) 125 | queue_out.put(note) 126 | 127 | # Unlink mixer stripes 128 | if self.unlink_stripes_btn: 129 | (msg_type, channel, pitch, velocity) = self.unlink_stripes_btn 130 | note = ((self.NOTEON << 4) + channel, pitch, color) 131 | queue_out.put(note) 132 | 133 | # custom reset 134 | if self.custom_reset_btn: 135 | (msg_type, channel, pitch, velocity) = self.custom_reset_btn 136 | note = ((self.NOTEON << 4) + channel, pitch, color) 137 | queue_out.put(note) 138 | 139 | 140 | #transport 141 | if self.play_btn: 142 | (msg_type, channel, pitch, velocity) = self.play_btn 143 | note = ((self.NOTEON << 4) + channel, pitch, color) 144 | queue_out.put(note) 145 | 146 | if self.pause_btn: 147 | (msg_type, channel, pitch, velocity) = self.pause_btn 148 | note = ((self.NOTEON << 4) + channel, pitch, color) 149 | queue_out.put(note) 150 | 151 | if self.rewind_btn: 152 | (msg_type, channel, pitch, velocity) = self.rewind_btn 153 | note = ((self.NOTEON << 4) + channel, pitch, color) 154 | queue_out.put(note) 155 | 156 | if self.stop_btn: 157 | (msg_type, channel, pitch, velocity) = self.stop_btn 158 | note = ((self.NOTEON << 4) + channel, pitch, color) 159 | queue_out.put(note) 160 | 161 | if self.goto_btn: 162 | (msg_type, channel, pitch, velocity) = self.goto_btn 163 | note = ((self.NOTEON << 4) + channel, pitch, color) 164 | queue_out.put(note) 165 | 166 | if self.record_btn: 167 | (msg_type, channel, pitch, velocity) = self.record_btn 168 | note = ((self.NOTEON << 4) + channel, pitch, color) 169 | queue_out.put(note) 170 | 171 | 172 | def getXY(self, note): 173 | return self.note_to_coord[note] 174 | 175 | @property 176 | def name(self): 177 | return self.mapping.get('name', '') 178 | 179 | @name.setter 180 | def name(self, name): 181 | self.mapping['name'] = name 182 | 183 | @property 184 | def description(self): 185 | return self.mapping.get('description', '') 186 | 187 | @description.setter 188 | def description(self, description): 189 | self.mapping['description'] = description 190 | 191 | @DeviceInput 192 | def ctrls(self): # this is for the mixer strip volumes 193 | return [] 194 | 195 | @DeviceInput 196 | def send1ctrls(self): # this is for the mixer strip send1 197 | return [] 198 | 199 | @DeviceInput 200 | def send2ctrls(self): # this is for the mixer strip send2 201 | return [] 202 | 203 | 204 | @DeviceInput 205 | def start_stop(self): 206 | return [] 207 | 208 | @DeviceInput 209 | def init_command(self): 210 | return [] 211 | 212 | @DeviceInput 213 | def mute_buttons(self): 214 | return [] 215 | 216 | @DeviceInput 217 | def scene_buttons(self): 218 | return [] 219 | 220 | @DeviceInput 221 | def song_volume_ctrl(self): 222 | return False 223 | 224 | @DeviceInput 225 | def play_btn(self): 226 | return False 227 | 228 | @DeviceInput 229 | def pause_btn(self): 230 | return False 231 | 232 | @DeviceInput 233 | def rewind_btn(self): 234 | return False 235 | 236 | @DeviceInput 237 | def goto_btn(self): 238 | return False 239 | 240 | @DeviceInput 241 | def stop_btn(self): 242 | return False 243 | 244 | @DeviceInput 245 | def shift_btn(self): 246 | return False 247 | 248 | @DeviceInput 249 | def unlink_stripes_btn(self): 250 | return False 251 | 252 | @DeviceInput 253 | def custom_reset_btn(self): 254 | return False 255 | 256 | @DeviceInput 257 | def record_btn(self): 258 | return False 259 | 260 | @DeviceOutput 261 | def black_vel(self): 262 | return 0 263 | 264 | @DeviceOutput 265 | def green_vel(self): 266 | return 0 267 | 268 | @DeviceOutput 269 | def blink_green_vel(self): 270 | return 0 271 | 272 | @DeviceOutput 273 | def red_vel(self): 274 | return 0 275 | 276 | @DeviceOutput 277 | def blink_red_vel(self): 278 | return 0 279 | 280 | @DeviceOutput 281 | def amber_vel(self): 282 | return 0 283 | 284 | @DeviceOutput 285 | def blink_amber_vel(self): 286 | return 0 287 | -------------------------------------------------------------------------------- /superboucle/device_manager.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QInputDialog, QLineEdit 2 | from superboucle.device_manager_ui import Ui_Dialog 3 | from superboucle.learn import LearnDialog 4 | from superboucle.device import Device 5 | from superboucle.clip import verify_ext 6 | import json 7 | import common 8 | import copy 9 | from PyQt5.QtWidgets import QMessageBox 10 | 11 | class ManageDialog(QDialog, Ui_Dialog): 12 | def __init__(self, parent): 13 | super(ManageDialog, self).__init__(parent) 14 | self.gui = parent 15 | self.setupUi(self) 16 | 17 | self.updateDeviceList() 18 | 19 | self.editButton.clicked.connect(self.onEdit) 20 | self.deleteButton.clicked.connect(self.onDelete) 21 | self.importButton.clicked.connect(self.onImport) 22 | self.exportButton.clicked.connect(self.onExport) 23 | self.cloneButton.clicked.connect(self.onClone) 24 | self.finished.connect(self.onFinished) 25 | 26 | self.setModal(True) 27 | self.show() 28 | 29 | 30 | def onEdit(self): 31 | if self.gui._jack_client.transport_state == 1: # ROLLING 32 | 33 | response = QMessageBox.question(self, "Enter device configuration?", "The song execution will be stopped") 34 | if response == QMessageBox.No: 35 | return 36 | 37 | self.gui._jack_client.transport_stop() 38 | self.gui._jack_client.transport_locate(0) 39 | 40 | if self.list.currentRow() != -1: 41 | device = self.gui.devices[self.list.currentRow() + 1] 42 | self.gui.learn_device = LearnDialog(self.gui, 43 | self.updateDevice, 44 | device) 45 | self.gui.is_learn_device_mode = True 46 | self.gui.update() 47 | 48 | def onClone(self): 49 | if self.list.currentRow() != -1: 50 | device = self.gui.devices[self.list.currentRow() + 1] 51 | 52 | currentName = str(self.list.currentItem().text()) 53 | newName, okPressed = QInputDialog.getText(self, "Clone MIDI configuration", "Enter new MIDI configuration name:", QLineEdit.Normal, currentName) 54 | 55 | if currentName.strip() == newName.strip(): 56 | print("Device won't be cloned since names are equals") 57 | return 58 | 59 | newDevice = copy.deepcopy(device) 60 | newDevice.name = newName 61 | newDevice.description = device.description + "\n\n(Cloned from " + currentName +")" 62 | 63 | self.gui.addDevice(newDevice) 64 | self.updateDeviceList() 65 | 66 | 67 | def onDelete(self): 68 | if self.list.currentRow() != -1: 69 | response = QMessageBox.question(self, "Delete MIDI configuration?", "Are you sure you want to delete this MIDI configuration?") 70 | if response == QMessageBox.No: 71 | return 72 | 73 | device = self.gui.devices[self.list.currentRow() + 1] 74 | self.gui.devices.remove(device) 75 | self.list.takeItem(self.list.currentRow()) 76 | 77 | 78 | 79 | def onImport(self): 80 | file_name, a = self.gui.getOpenFileName('Import MIDI configuration', 81 | common.DEVICE_MAPPING_TYPE, 82 | self) 83 | with open(file_name, 'r') as f: 84 | read_data = f.read() 85 | mapping = json.loads(read_data) 86 | self.list.addItem(mapping['name']) 87 | self.gui.devices.append(Device(mapping)) 88 | 89 | 90 | def onExport(self): 91 | device = self.gui.devices[self.list.currentRow() + 1] 92 | file_name, a = self.gui.getSaveFileName('Export MIDI configuration', 93 | common.DEVICE_MAPPING_TYPE, 94 | self) 95 | if file_name: 96 | file_name = verify_ext(file_name, common.DEVICE_MAPPING_EXT) 97 | with open(file_name, 'w') as f: 98 | f.write(json.dumps(device.mapping)) 99 | 100 | 101 | def onFinished(self): 102 | self.gui.updateDevices() 103 | 104 | 105 | def updateDevice(self, device): 106 | self.updateDeviceList() 107 | 108 | self.gui.is_learn_device_mode = False 109 | self.gui.redraw() 110 | 111 | 112 | def updateDeviceList(self): 113 | self.list.clear() 114 | for device in self.gui.devices[1:]: 115 | self.list.addItem(device.name) 116 | 117 | 118 | -------------------------------------------------------------------------------- /superboucle/device_manager_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/device_manager_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(491, 297) 17 | self.gridLayout = QtWidgets.QGridLayout(Dialog) 18 | self.gridLayout.setObjectName("gridLayout") 19 | self.list = QtWidgets.QListWidget(Dialog) 20 | self.list.setObjectName("list") 21 | self.gridLayout.addWidget(self.list, 0, 0, 1, 1) 22 | self.verticalLayout = QtWidgets.QVBoxLayout() 23 | self.verticalLayout.setObjectName("verticalLayout") 24 | self.editButton = QtWidgets.QPushButton(Dialog) 25 | self.editButton.setObjectName("editButton") 26 | self.verticalLayout.addWidget(self.editButton) 27 | self.deleteButton = QtWidgets.QPushButton(Dialog) 28 | self.deleteButton.setObjectName("deleteButton") 29 | self.verticalLayout.addWidget(self.deleteButton) 30 | self.cloneButton = QtWidgets.QPushButton(Dialog) 31 | self.cloneButton.setObjectName("cloneButton") 32 | self.verticalLayout.addWidget(self.cloneButton) 33 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 34 | self.verticalLayout.addItem(spacerItem) 35 | self.importButton = QtWidgets.QPushButton(Dialog) 36 | self.importButton.setObjectName("importButton") 37 | self.verticalLayout.addWidget(self.importButton) 38 | self.exportButton = QtWidgets.QPushButton(Dialog) 39 | self.exportButton.setObjectName("exportButton") 40 | self.verticalLayout.addWidget(self.exportButton) 41 | self.gridLayout.addLayout(self.verticalLayout, 0, 1, 1, 1) 42 | 43 | self.retranslateUi(Dialog) 44 | QtCore.QMetaObject.connectSlotsByName(Dialog) 45 | 46 | def retranslateUi(self, Dialog): 47 | _translate = QtCore.QCoreApplication.translate 48 | Dialog.setWindowTitle(_translate("Dialog", "Manage MIDI configurations")) 49 | self.editButton.setText(_translate("Dialog", "Edit")) 50 | self.deleteButton.setText(_translate("Dialog", "Delete")) 51 | self.cloneButton.setText(_translate("Dialog", "Clone")) 52 | self.importButton.setText(_translate("Dialog", "Import")) 53 | self.exportButton.setText(_translate("Dialog", "Export")) 54 | -------------------------------------------------------------------------------- /superboucle/device_manager_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 491 10 | 297 11 | 12 | 13 | 14 | Manage MIDI configurations 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Edit 26 | 27 | 28 | 29 | 30 | 31 | 32 | Delete 33 | 34 | 35 | 36 | 37 | 38 | 39 | Clone 40 | 41 | 42 | 43 | 44 | 45 | 46 | Qt::Vertical 47 | 48 | 49 | 50 | 20 51 | 40 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Import 60 | 61 | 62 | 63 | 64 | 65 | 66 | Export 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /superboucle/edit_clips.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QFileDialog, QMessageBox 2 | from superboucle.edit_clips_ui import Ui_Dialog 3 | from PyQt5 import QtGui 4 | import common 5 | import settings 6 | 7 | EDIT_ALL_SELECTED_CLIPS_MESSAGE = "Check the details you want to massively change, then fill the values and press Ok" 8 | EDIT_BY_OUTPUT_PORT_MESSAGE = "Select an output port, then check the details and fill the values you want to massively change for all the clips belonging to that port" 9 | EDIT_BY_MUTE_GROUP_MESSAGE = "Select a solo clip group, then check the details and fill the values you want to massively change for all the clips belonging to that solo clip group" 10 | 11 | EDIT_ALL_SELECTED_LABEL_STYLE = ('font: bold 11pt "Noto Sans";background-color: rgb(255, 255, 0);') 12 | EDIT_OTHER_LABEL_STYLE = ('font: bold 10pt "Noto Sans";') 13 | 14 | class EditClipsDialog(QDialog, Ui_Dialog): 15 | def __init__(self, parent, edit_mode = 0, selected_clips = None): 16 | super(EditClipsDialog, self).__init__(parent) 17 | 18 | self.gui = parent 19 | self.setupUi(self) 20 | self.initializeUI() 21 | 22 | self.proceed = False 23 | self.volume = 0 24 | self.volume_change_mode = common.SET_VOLUME 25 | self.accepted.connect(self.onOk) 26 | 27 | self.checkBoxEnableOutPortsChanges.clicked.connect(self.onEnablePortsChangeClicked) 28 | self.checkBoxEnableMutGroupChanges.clicked.connect(self.onEnableMuteGroupsChangeClicked) 29 | self.checkBoxEnableVolumeChanges.clicked.connect(self.onEnableVolumeChangeClicked) 30 | 31 | self.buttonCopyPortFromCurrentClip.clicked.connect(self.onButtonCopyPortClicked) 32 | self.buttonCopyMuteGroupFromCurrentClip.clicked.connect(self.onButtonCopyMuteGroupClicked) 33 | self.buttonCopyVolumeFromCurrentClip.clicked.connect(self.onButtonCopyVolumeClicked) 34 | 35 | self.radioButtonSetVolume.toggled.connect(self.updateVolumeRadioButtons) 36 | self.radioButtonIncreaseVolume.toggled.connect(self.updateVolumeRadioButtons) 37 | self.radioButtonDecreaseVolume.toggled.connect(self.updateVolumeRadioButtons) 38 | 39 | self.comboBoxOutputPorts.addItems(sorted(settings.output_ports.keys())) 40 | 41 | self.edit_mode = edit_mode 42 | if self.edit_mode == common.CLIPS_EDIT_MODE_ALL_SELECTED: 43 | 44 | self.checkBoxEnableOutPortsChanges.setVisible(True) 45 | self.checkBoxEnableMutGroupChanges.setVisible(True) 46 | self.checkBoxEnableVolumeChanges.setVisible(True) 47 | 48 | # I left to GUI the control that selected_clips contains some clips 49 | # so I won't check again here. 50 | text = "You have selected " + str(selected_clips.__len__()) + " clips. " 51 | self.labelMessage.setText(text + EDIT_ALL_SELECTED_CLIPS_MESSAGE) 52 | self.labelMessage.setStyleSheet(EDIT_ALL_SELECTED_LABEL_STYLE) 53 | 54 | elif edit_mode == common.CLIPS_EDIT_MODE_BY_MUTE_GROUP: 55 | 56 | self.checkBoxEnableOutPortsChanges.setVisible(True) 57 | self.checkBoxEnableMutGroupChanges.setVisible(False) 58 | self.checkBoxEnableVolumeChanges.setVisible(True) 59 | 60 | self.labelMessage.setText(EDIT_BY_MUTE_GROUP_MESSAGE) 61 | self.labelMessage.setStyleSheet(EDIT_OTHER_LABEL_STYLE) 62 | 63 | self.groupBoxMuteGroup.setEnabled(True) 64 | self.checkBoxUnselectClips.setVisible(False) 65 | 66 | self.framePorts.setGeometry(230, 70, 291, 111) 67 | self.frameMuteGroups.setGeometry(10, 70, 211, 111) 68 | 69 | elif edit_mode == common.CLIPS_EDIT_MODE_BY_OUTPUT_PORT: 70 | 71 | self.checkBoxEnableOutPortsChanges.setVisible(False) 72 | self.checkBoxEnableMutGroupChanges.setVisible(True) 73 | self.checkBoxEnableVolumeChanges.setVisible(True) 74 | 75 | self.labelMessage.setText(EDIT_BY_OUTPUT_PORT_MESSAGE) 76 | self.labelMessage.setStyleSheet(EDIT_OTHER_LABEL_STYLE) 77 | 78 | self.groupBoxPorts.setEnabled(True) 79 | self.checkBoxUnselectClips.setVisible(False) 80 | 81 | self.setFixedSize(self.size()) 82 | self.setModal(True) 83 | self.show() 84 | 85 | def initializeUI(self): 86 | self.groupBoxPorts.setEnabled(False) 87 | self.groupBoxMuteGroup.setEnabled(False) 88 | self.groupBoxVolume.setEnabled(False) 89 | self.checkBoxEnableMutGroupChanges.setChecked(False) 90 | self.checkBoxEnableOutPortsChanges.setChecked(False) 91 | self.checkBoxEnableVolumeChanges.setChecked(False) 92 | self.radioButtonSetVolume.setChecked(True) 93 | 94 | def showNoClipMessage(self): 95 | message = QMessageBox(self) 96 | message.setWindowTitle("Can't read clip details") 97 | message.setText("No clip is currently selected in Edit details in SpinTool window") 98 | message.show() 99 | 100 | def onEnablePortsChangeClicked(self): 101 | self.groupBoxPorts.setEnabled(self.checkBoxEnableOutPortsChanges.isChecked()) 102 | 103 | def onEnableMuteGroupsChangeClicked(self): 104 | self.groupBoxMuteGroup.setEnabled(self.checkBoxEnableMutGroupChanges.isChecked()) 105 | 106 | def onEnableVolumeChangeClicked(self): 107 | self.groupBoxVolume.setEnabled(self.checkBoxEnableVolumeChanges.isChecked()) 108 | 109 | def onButtonCopyPortClicked(self): 110 | if self.gui.last_clip: 111 | self.comboBoxOutputPorts.setCurrentText(self.gui.last_clip.output) 112 | else: 113 | self.showNoClipMessage() 114 | 115 | def onButtonCopyMuteGroupClicked(self): 116 | if self.gui.last_clip: 117 | self.spinBoxMuteGroups.setValue(self.gui.last_clip.mute_group) 118 | else: 119 | self.showNoClipMessage() 120 | 121 | def onButtonCopyVolumeClicked(self): 122 | if self.gui.last_clip: 123 | self.volume = self.gui.last_clip.volume 124 | self.spinBoxVolumeAmount.setValue(common.toDigitalVolumeValue(self.gui.last_clip.volume)) 125 | else: 126 | self.showNoClipMessage() 127 | 128 | def updateVolumeRadioButtons(self): 129 | self.buttonCopyVolumeFromCurrentClip.setVisible(self.radioButtonSetVolume.isChecked()) 130 | 131 | if self.radioButtonSetVolume.isChecked() == True: 132 | self.volume_change_mode = common.SET_VOLUME 133 | 134 | elif self.radioButtonIncreaseVolume.isChecked() == True: 135 | self.volume_change_mode = common.INCREASE_VOLUME 136 | 137 | elif self.radioButtonDecreaseVolume.isChecked() == True: 138 | self.volume_change_mode = common.DECREASE_VOLUME 139 | 140 | def getVolumeAmount(self): 141 | return self.volume, self.volume_change_mode 142 | 143 | def getOutputPort(self): 144 | return self.comboBoxOutputPorts.currentText() 145 | 146 | def getMuteGroup(self): 147 | return self.spinBoxMuteGroups.value() 148 | 149 | def getEditVolume(self): 150 | return self.checkBoxEnableVolumeChanges.isChecked() 151 | 152 | def getEditMuteGroup(self): 153 | return self.checkBoxEnableMutGroupChanges.isChecked() 154 | 155 | def getEditOutputPort(self): 156 | return self.checkBoxEnableOutPortsChanges.isChecked() 157 | 158 | def getUnselectClips(self): 159 | return self.checkBoxUnselectClips.isChecked() 160 | 161 | def onOk(self): 162 | self.volume = self.spinBoxVolumeAmount.value() 163 | self.proceed = True 164 | 165 | def onFinished(self): 166 | pass -------------------------------------------------------------------------------- /superboucle/export_samples.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QFileDialog 2 | from superboucle.export_samples_ui import Ui_Dialog 3 | 4 | class ExportAllSamplesDialog(QDialog, Ui_Dialog): 5 | def __init__(self, parent): 6 | super(ExportAllSamplesDialog, self).__init__(parent) 7 | 8 | self.gui = parent 9 | self.setupUi(self) 10 | self.accepted.connect(self.onOk) 11 | self.btnPath.clicked.connect(self.onPath) 12 | self.proceed = False 13 | 14 | self.setFixedSize(self.size()) 15 | self.setModal(True) 16 | self.show() 17 | 18 | def setPath(self, path): 19 | self.labelPath.setText(path) 20 | 21 | def getPath(self): 22 | return self.labelPath.text() 23 | 24 | def getNormalize(self): 25 | return self.cBoxNormalize.isChecked() 26 | 27 | def getX(self): 28 | return self.lineX.text() 29 | 30 | def getY(self): 31 | return self.lineY.text() 32 | 33 | def onPath(self): 34 | self.labelPath.setText(QFileDialog.getExistingDirectory(self, 'Select export directory')) 35 | 36 | def onOk(self): 37 | self.proceed = True 38 | 39 | def onFinished(self): 40 | pass -------------------------------------------------------------------------------- /superboucle/export_samples_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/export_samples_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.setWindowModality(QtCore.Qt.WindowModal) 17 | Dialog.resize(680, 338) 18 | Dialog.setStyleSheet("background-color: rgb(242, 242, 242);") 19 | self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) 20 | self.buttonBox.setGeometry(QtCore.QRect(10, 300, 661, 31)) 21 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 22 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 23 | self.buttonBox.setObjectName("buttonBox") 24 | self.cBoxNormalize = QtWidgets.QCheckBox(Dialog) 25 | self.cBoxNormalize.setGeometry(QtCore.QRect(10, 110, 281, 31)) 26 | font = QtGui.QFont() 27 | font.setFamily("Noto Sans") 28 | font.setPointSize(10) 29 | font.setBold(False) 30 | font.setItalic(False) 31 | font.setWeight(50) 32 | self.cBoxNormalize.setFont(font) 33 | self.cBoxNormalize.setStyleSheet("color: rgb(0, 0, 0);\n" 34 | "background-color: rgb(242, 242, 242);\n" 35 | "font: 10pt \"Noto Sans\";") 36 | self.cBoxNormalize.setObjectName("cBoxNormalize") 37 | self.labelChoosePath = QtWidgets.QLabel(Dialog) 38 | self.labelChoosePath.setGeometry(QtCore.QRect(10, 10, 381, 41)) 39 | self.labelChoosePath.setStyleSheet("color: rgb(0, 0, 0);\n" 40 | "background-color: rgb(242, 242, 242);\n" 41 | "font: 10pt \"Noto Sans\";") 42 | self.labelChoosePath.setObjectName("labelChoosePath") 43 | self.btnPath = QtWidgets.QPushButton(Dialog) 44 | self.btnPath.setGeometry(QtCore.QRect(10, 50, 51, 41)) 45 | self.btnPath.setStyleSheet("color: rgb(0, 0, 0);\n" 46 | "background-color: rgb(242, 242, 242);\n" 47 | "font: 10pt \"Noto Sans\";") 48 | self.btnPath.setText("") 49 | icon = QtGui.QIcon() 50 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/folder.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 51 | self.btnPath.setIcon(icon) 52 | self.btnPath.setIconSize(QtCore.QSize(27, 27)) 53 | self.btnPath.setObjectName("btnPath") 54 | self.labelPath = QtWidgets.QLabel(Dialog) 55 | self.labelPath.setGeometry(QtCore.QRect(70, 50, 591, 41)) 56 | self.labelPath.setStyleSheet("color: rgb(0, 0, 0);\n" 57 | "background-color: rgb(242, 242, 242);\n" 58 | "font: 10pt \"Noto Sans\";") 59 | self.labelPath.setObjectName("labelPath") 60 | self.labelX = QtWidgets.QLabel(Dialog) 61 | self.labelX.setGeometry(QtCore.QRect(20, 180, 101, 18)) 62 | self.labelX.setStyleSheet("color: rgb(0, 0, 0);\n" 63 | "background-color: rgb(242, 242, 242);\n" 64 | "font: 10pt \"Noto Sans\";") 65 | self.labelX.setObjectName("labelX") 66 | self.lineX = QtWidgets.QLineEdit(Dialog) 67 | self.lineX.setGeometry(QtCore.QRect(130, 170, 101, 41)) 68 | self.lineX.setStyleSheet("color: rgb(0, 0, 0);\n" 69 | "background-color: rgb(242, 242, 242);\n" 70 | "font: 10pt \"Noto Sans\";") 71 | self.lineX.setAlignment(QtCore.Qt.AlignCenter) 72 | self.lineX.setObjectName("lineX") 73 | self.labelY = QtWidgets.QLabel(Dialog) 74 | self.labelY.setGeometry(QtCore.QRect(20, 240, 101, 18)) 75 | self.labelY.setStyleSheet("color: rgb(0, 0, 0);\n" 76 | "background-color: rgb(242, 242, 242);\n" 77 | "font: 10pt \"Noto Sans\";") 78 | self.labelY.setObjectName("labelY") 79 | self.lineY = QtWidgets.QLineEdit(Dialog) 80 | self.lineY.setGeometry(QtCore.QRect(130, 230, 101, 41)) 81 | self.lineY.setStyleSheet("color: rgb(0, 0, 0);\n" 82 | "background-color: rgb(242, 242, 242);\n" 83 | "font: 10pt \"Noto Sans\";") 84 | self.lineY.setAlignment(QtCore.Qt.AlignCenter) 85 | self.lineY.setObjectName("lineY") 86 | 87 | self.retranslateUi(Dialog) 88 | self.buttonBox.accepted.connect(Dialog.accept) 89 | self.buttonBox.rejected.connect(Dialog.reject) 90 | QtCore.QMetaObject.connectSlotsByName(Dialog) 91 | 92 | def retranslateUi(self, Dialog): 93 | _translate = QtCore.QCoreApplication.translate 94 | Dialog.setWindowTitle(_translate("Dialog", "Export all samples")) 95 | self.cBoxNormalize.setText(_translate("Dialog", "Normalize samples when exporting")) 96 | self.labelChoosePath.setText(_translate("Dialog", "Export all clip samples to:")) 97 | self.labelPath.setText(_translate("Dialog", "(select a directory for exporting)")) 98 | self.labelX.setText(_translate("Dialog", "Column prefix:")) 99 | self.lineX.setText(_translate("Dialog", "X")) 100 | self.lineX.setPlaceholderText(_translate("Dialog", "X")) 101 | self.labelY.setText(_translate("Dialog", "Row prefix:")) 102 | self.lineY.setText(_translate("Dialog", "Y")) 103 | self.lineY.setPlaceholderText(_translate("Dialog", "Y")) 104 | import gui_rc 105 | -------------------------------------------------------------------------------- /superboucle/export_samples_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 680 13 | 338 14 | 15 | 16 | 17 | Export all samples 18 | 19 | 20 | background-color: rgb(242, 242, 242); 21 | 22 | 23 | 24 | 25 | 10 26 | 300 27 | 661 28 | 31 29 | 30 | 31 | 32 | Qt::Horizontal 33 | 34 | 35 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 36 | 37 | 38 | 39 | 40 | 41 | 10 42 | 110 43 | 281 44 | 31 45 | 46 | 47 | 48 | 49 | Noto Sans 50 | 10 51 | 50 52 | false 53 | false 54 | 55 | 56 | 57 | color: rgb(0, 0, 0); 58 | background-color: rgb(242, 242, 242); 59 | font: 10pt "Noto Sans"; 60 | 61 | 62 | Normalize samples when exporting 63 | 64 | 65 | 66 | 67 | 68 | 10 69 | 10 70 | 381 71 | 41 72 | 73 | 74 | 75 | color: rgb(0, 0, 0); 76 | background-color: rgb(242, 242, 242); 77 | font: 10pt "Noto Sans"; 78 | 79 | 80 | Export all clip samples to: 81 | 82 | 83 | 84 | 85 | 86 | 10 87 | 50 88 | 51 89 | 41 90 | 91 | 92 | 93 | color: rgb(0, 0, 0); 94 | background-color: rgb(242, 242, 242); 95 | font: 10pt "Noto Sans"; 96 | 97 | 98 | 99 | 100 | 101 | 102 | :/icons/icons/folder.png:/icons/icons/folder.png 103 | 104 | 105 | 106 | 27 107 | 27 108 | 109 | 110 | 111 | 112 | 113 | 114 | 70 115 | 50 116 | 591 117 | 41 118 | 119 | 120 | 121 | color: rgb(0, 0, 0); 122 | background-color: rgb(242, 242, 242); 123 | font: 10pt "Noto Sans"; 124 | 125 | 126 | (select a directory for exporting) 127 | 128 | 129 | 130 | 131 | 132 | 20 133 | 180 134 | 101 135 | 18 136 | 137 | 138 | 139 | color: rgb(0, 0, 0); 140 | background-color: rgb(242, 242, 242); 141 | font: 10pt "Noto Sans"; 142 | 143 | 144 | Column prefix: 145 | 146 | 147 | 148 | 149 | 150 | 130 151 | 170 152 | 101 153 | 41 154 | 155 | 156 | 157 | color: rgb(0, 0, 0); 158 | background-color: rgb(242, 242, 242); 159 | font: 10pt "Noto Sans"; 160 | 161 | 162 | X 163 | 164 | 165 | Qt::AlignCenter 166 | 167 | 168 | X 169 | 170 | 171 | 172 | 173 | 174 | 20 175 | 240 176 | 101 177 | 18 178 | 179 | 180 | 181 | color: rgb(0, 0, 0); 182 | background-color: rgb(242, 242, 242); 183 | font: 10pt "Noto Sans"; 184 | 185 | 186 | Row prefix: 187 | 188 | 189 | 190 | 191 | 192 | 130 193 | 230 194 | 101 195 | 41 196 | 197 | 198 | 199 | color: rgb(0, 0, 0); 200 | background-color: rgb(242, 242, 242); 201 | font: 10pt "Noto Sans"; 202 | 203 | 204 | Y 205 | 206 | 207 | Qt::AlignCenter 208 | 209 | 210 | Y 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | buttonBox 220 | accepted() 221 | Dialog 222 | accept() 223 | 224 | 225 | 248 226 | 254 227 | 228 | 229 | 157 230 | 274 231 | 232 | 233 | 234 | 235 | buttonBox 236 | rejected() 237 | Dialog 238 | reject() 239 | 240 | 241 | 316 242 | 260 243 | 244 | 245 | 286 246 | 274 247 | 248 | 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /superboucle/gui.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/clip-behaviour.png 4 | icons/clip-functions.png 5 | icons/clip-groups.png 6 | icons/clip-measures.png 7 | icons/edit-delete.png 8 | icons/warning.png 9 | icons/Superdirt_Logo_80.png 10 | icons/sliders.png 11 | icons/stop.png 12 | icons/shift.png 13 | icons/file.png 14 | icons/fader.png 15 | icons/copy.png 16 | icons/clip-edit.png 17 | icons/clip-start-stop.png 18 | icons/clip-add.png 19 | icons/logo_sb.png 20 | icons/MeltinPop_Logo_100.png 21 | icons/ST_Logo_Small_Transparent.png 22 | icons/record_24.png 23 | icons/ST_Logo_XSmall_Transparent.png 24 | icons/white_goto_24.png 25 | icons/white_pause_24.png 26 | icons/white_play_24.png 27 | icons/white_rewind_24.png 28 | icons/black_goto_24.png 29 | icons/black_pause_24.png 30 | icons/folder.png 31 | icons/black_play_24.png 32 | icons/black_rewind_24.png 33 | icons/icon.png 34 | 35 | 36 | -------------------------------------------------------------------------------- /superboucle/help_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/help_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.setWindowModality(QtCore.Qt.WindowModal) 17 | Dialog.resize(621, 372) 18 | icon = QtGui.QIcon() 19 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 20 | Dialog.setWindowIcon(icon) 21 | Dialog.setStyleSheet("background-color: rgb(242, 242, 242);") 22 | self.formLayoutWidget = QtWidgets.QWidget(Dialog) 23 | self.formLayoutWidget.setGeometry(QtCore.QRect(10, 10, 601, 351)) 24 | self.formLayoutWidget.setObjectName("formLayoutWidget") 25 | self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget) 26 | self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow) 27 | self.formLayout.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows) 28 | self.formLayout.setFormAlignment(QtCore.Qt.AlignCenter) 29 | self.formLayout.setContentsMargins(2, 2, 2, 2) 30 | self.formLayout.setObjectName("formLayout") 31 | self.txtHelp = QtWidgets.QTextBrowser(self.formLayoutWidget) 32 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 33 | sizePolicy.setHorizontalStretch(1) 34 | sizePolicy.setVerticalStretch(1) 35 | sizePolicy.setHeightForWidth(self.txtHelp.sizePolicy().hasHeightForWidth()) 36 | self.txtHelp.setSizePolicy(sizePolicy) 37 | self.txtHelp.setStyleSheet("color: rgb(0, 0, 0);\n" 38 | "background-color: rgb(242, 242, 242);\n" 39 | "font: 12pt \"Noto Sans\";\n" 40 | "border: None;") 41 | self.txtHelp.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 42 | self.txtHelp.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 43 | self.txtHelp.setDocumentTitle("") 44 | self.txtHelp.setReadOnly(False) 45 | self.txtHelp.setAcceptRichText(True) 46 | self.txtHelp.setObjectName("txtHelp") 47 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.txtHelp) 48 | 49 | self.retranslateUi(Dialog) 50 | QtCore.QMetaObject.connectSlotsByName(Dialog) 51 | 52 | def retranslateUi(self, Dialog): 53 | _translate = QtCore.QCoreApplication.translate 54 | Dialog.setWindowTitle(_translate("Dialog", "SpinTool assistant")) 55 | self.txtHelp.setHtml(_translate("Dialog", "\n" 56 | "\n" 59 | "


")) 60 | import gui_rc 61 | -------------------------------------------------------------------------------- /superboucle/icons/MeltinPop_Logo_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/MeltinPop_Logo_100.png -------------------------------------------------------------------------------- /superboucle/icons/ST_Logo_Small_Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/ST_Logo_Small_Transparent.png -------------------------------------------------------------------------------- /superboucle/icons/ST_Logo_XSmall_Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/ST_Logo_XSmall_Transparent.png -------------------------------------------------------------------------------- /superboucle/icons/Superdirt_Logo_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/Superdirt_Logo_80.png -------------------------------------------------------------------------------- /superboucle/icons/black_goto_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/black_goto_24.png -------------------------------------------------------------------------------- /superboucle/icons/black_pause_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/black_pause_24.png -------------------------------------------------------------------------------- /superboucle/icons/black_play_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/black_play_24.png -------------------------------------------------------------------------------- /superboucle/icons/black_rewind_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/black_rewind_24.png -------------------------------------------------------------------------------- /superboucle/icons/clip-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-add.png -------------------------------------------------------------------------------- /superboucle/icons/clip-behaviour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-behaviour.png -------------------------------------------------------------------------------- /superboucle/icons/clip-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-edit.png -------------------------------------------------------------------------------- /superboucle/icons/clip-functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-functions.png -------------------------------------------------------------------------------- /superboucle/icons/clip-groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-groups.png -------------------------------------------------------------------------------- /superboucle/icons/clip-measures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-measures.png -------------------------------------------------------------------------------- /superboucle/icons/clip-start-stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/clip-start-stop.png -------------------------------------------------------------------------------- /superboucle/icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/copy.png -------------------------------------------------------------------------------- /superboucle/icons/edit-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/edit-delete.png -------------------------------------------------------------------------------- /superboucle/icons/fader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/fader.png -------------------------------------------------------------------------------- /superboucle/icons/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/file.png -------------------------------------------------------------------------------- /superboucle/icons/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/folder.png -------------------------------------------------------------------------------- /superboucle/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/icon.png -------------------------------------------------------------------------------- /superboucle/icons/icon_dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/icon_dev.png -------------------------------------------------------------------------------- /superboucle/icons/info: -------------------------------------------------------------------------------- 1 | Icons and logos 2 | -------------------------------------------------------------------------------- /superboucle/icons/logo_sb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/logo_sb.png -------------------------------------------------------------------------------- /superboucle/icons/record_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/record_24.png -------------------------------------------------------------------------------- /superboucle/icons/shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/shift.png -------------------------------------------------------------------------------- /superboucle/icons/sliders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/sliders.png -------------------------------------------------------------------------------- /superboucle/icons/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/stop.png -------------------------------------------------------------------------------- /superboucle/icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/warning.png -------------------------------------------------------------------------------- /superboucle/icons/white_goto_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/white_goto_24.png -------------------------------------------------------------------------------- /superboucle/icons/white_pause_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/white_pause_24.png -------------------------------------------------------------------------------- /superboucle/icons/white_play_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/white_play_24.png -------------------------------------------------------------------------------- /superboucle/icons/white_rewind_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/icons/white_rewind_24.png -------------------------------------------------------------------------------- /superboucle/learn_cell_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Applicazioni/SuperBoucle-Manu/superboucle/learn_cell_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.12.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_LearnCell(object): 14 | def setupUi(self, LearnCell): 15 | LearnCell.setObjectName("LearnCell") 16 | LearnCell.resize(94, 40) 17 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 18 | sizePolicy.setHorizontalStretch(1) 19 | sizePolicy.setVerticalStretch(1) 20 | sizePolicy.setHeightForWidth(LearnCell.sizePolicy().hasHeightForWidth()) 21 | LearnCell.setSizePolicy(sizePolicy) 22 | LearnCell.setMinimumSize(QtCore.QSize(40, 40)) 23 | font = QtGui.QFont() 24 | font.setFamily("Lato") 25 | LearnCell.setFont(font) 26 | self.cell_frame = QtWidgets.QFrame(LearnCell) 27 | self.cell_frame.setGeometry(QtCore.QRect(0, 0, 40, 40)) 28 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) 29 | sizePolicy.setHorizontalStretch(0) 30 | sizePolicy.setVerticalStretch(0) 31 | sizePolicy.setHeightForWidth(self.cell_frame.sizePolicy().hasHeightForWidth()) 32 | self.cell_frame.setSizePolicy(sizePolicy) 33 | self.cell_frame.setFrameShape(QtWidgets.QFrame.NoFrame) 34 | self.cell_frame.setFrameShadow(QtWidgets.QFrame.Raised) 35 | self.cell_frame.setObjectName("cell_frame") 36 | self.label = QtWidgets.QLabel(self.cell_frame) 37 | self.label.setGeometry(QtCore.QRect(3, 2, 35, 39)) 38 | self.label.setText("") 39 | self.label.setAlignment(QtCore.Qt.AlignCenter) 40 | self.label.setObjectName("label") 41 | 42 | self.retranslateUi(LearnCell) 43 | QtCore.QMetaObject.connectSlotsByName(LearnCell) 44 | 45 | def retranslateUi(self, LearnCell): 46 | _translate = QtCore.QCoreApplication.translate 47 | LearnCell.setWindowTitle(_translate("LearnCell", "Form")) 48 | -------------------------------------------------------------------------------- /superboucle/learn_cell_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LearnCell 4 | 5 | 6 | 7 | 0 8 | 0 9 | 94 10 | 40 11 | 12 | 13 | 14 | 15 | 1 16 | 1 17 | 18 | 19 | 20 | 21 | 40 22 | 40 23 | 24 | 25 | 26 | 27 | Lato 28 | 29 | 30 | 31 | Form 32 | 33 | 34 | 35 | 36 | 0 37 | 0 38 | 40 39 | 40 40 | 41 | 42 | 43 | 44 | 0 45 | 0 46 | 47 | 48 | 49 | QFrame::NoFrame 50 | 51 | 52 | QFrame::Raised 53 | 54 | 55 | 56 | 57 | 3 58 | 2 59 | 35 60 | 39 61 | 62 | 63 | 64 | 65 | 66 | 67 | Qt::AlignCenter 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /superboucle/mixer.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import Qt 2 | from superboucle.assistant import Assistant 3 | from PyQt5.QtGui import QCursor 4 | import help 5 | from PyQt5.QtWidgets import QDialog, QListWidgetItem, QWidget, QSpacerItem, QSizePolicy, QApplication 6 | from superboucle.mixer_ui import Ui_Dialog 7 | from superboucle.mixerstrip import Mixerstrip 8 | from PyQt5 import QtGui 9 | import json 10 | import sys 11 | import copy 12 | import common 13 | import settings 14 | 15 | class Mixer(QDialog, Ui_Dialog): 16 | 17 | def __init__(self, parent): 18 | super(Mixer, self).__init__(parent) 19 | self.gui = parent 20 | self.setupUi(self) 21 | 22 | # signals 23 | self.song_vol_slider.valueChanged.connect(self.setSongVolume) 24 | 25 | self.masterport_vol_slider.valueChanged.connect(self.setMasterPortFinalVolume) 26 | self.masterport_mute_checkbox.clicked.connect(self.setMasterPortMute) 27 | 28 | self.custom_reset_btn.clicked.connect(self.onCustomReset) 29 | self.reset_gain_btn.clicked.connect(self.onResetGain) 30 | self.reset_send1_btn.clicked.connect(self.onResetSend1) 31 | self.reset_send2_btn.clicked.connect(self.onResetSend2) 32 | self.reset_vol_btn.clicked.connect(self.onResetVolume) 33 | self.reset_mute_btn.clicked.connect(self.onResetMute) 34 | self.unlink_stripes_btn.clicked.connect(self.onUnlinkStripes) 35 | 36 | self.advanced_checkbox.clicked.connect(self.onAdvancedMode) 37 | 38 | self.updateMasterMuteGui(settings.master_port_mute) 39 | self.updateSongVolumeGui(self.gui.song.volume * 256) 40 | self.updateMasterVolumeGui(settings.master_port_final_volume * 100) 41 | 42 | 43 | # overwrite finished signal? 44 | self.finished.connect(self.onFinished) 45 | 46 | 47 | def showEvent(self, event): 48 | if settings.mixer_geometry: 49 | self.restoreGeometry(settings.mixer_geometry) 50 | 51 | self.updateGui(settings.output_ports) 52 | self.UpdateStripesLinkGui() 53 | 54 | def moveEvent(self, event): 55 | self.geometry = self.saveGeometry() 56 | settings.mixer_geometry = copy.deepcopy(self.geometry) 57 | 58 | # setup the port mixer gui (create one mixerstrip for each port) 59 | def updateGui(self, output_ports): 60 | 61 | #reset / clear mixerstrip layout 62 | while self.mixerLayout.count(): 63 | child = self.mixerLayout.takeAt(0) 64 | if child.widget(): 65 | child.widget().deleteLater() 66 | 67 | index = 0 68 | 69 | self.portlist = common.toPortsList(output_ports) 70 | # create mixerstrips 71 | for i in self.portlist: 72 | # add one strip 73 | strip = Mixerstrip(self, i, index) 74 | self.mixerLayout.addWidget(strip) 75 | index = index + 1 76 | 77 | # add spacer 78 | spacer = QSpacerItem(1, 1, QSizePolicy.Expanding, QSizePolicy.Expanding) 79 | self.mixerLayout.addItem(spacer) 80 | 81 | # set dialog size according to mixerstrip amount 82 | mixerxsize = len(self.portlist)*(95) 83 | self.resize(220 + mixerxsize, 500) 84 | 85 | # update song volume fader 86 | self.updateSongVolumeGui(common.toControllerVolumeValue(self.gui.song.volume)) # = * 256 87 | 88 | 89 | def updateStripVolume(self, stripName): 90 | strip = self.findChild(Mixerstrip, stripName) 91 | if strip: 92 | strip.updateGuiVolume() 93 | 94 | def updateStripSend1(self, stripName): 95 | strip = self.findChild(Mixerstrip, stripName) 96 | if strip: 97 | strip.updateGuiSend1() 98 | 99 | def updateStripSend2(self, stripName): 100 | strip = self.findChild(Mixerstrip, stripName) 101 | if strip: 102 | strip.updateGuiSend2() 103 | 104 | def updateStripMute(self, stripName): 105 | 106 | strip = self.findChild(Mixerstrip, stripName) 107 | if strip: 108 | strip.updateGuiMute() 109 | 110 | # used by mixerstrip to notify mute value updated 111 | def muteUpdated(self, port, index): 112 | self.gui.updateMidiMute(port, index) 113 | 114 | # used by mixerstrip to notify send1 enabled value updated 115 | def send1Updated(self, port, index): 116 | self.gui.updateMidiSend1(port, index) #TODO: creare 117 | 118 | # used by mixerstrip to notify send2 enabled value updated 119 | def send2Updated(self, port, index): 120 | self.gui.updateMidiSend2(port, index) #TODO: creare 121 | 122 | def UpdateStripesLinkGui(self): 123 | if self.gui.mixer_stripes_midi_linked == True: 124 | self.unlink_stripes_btn.setIcon(QtGui.QIcon(":/icons/icons/sliders.png")) 125 | else: 126 | self.unlink_stripes_btn.setIcon(QtGui.QIcon(":/icons/icons/warning.png")) 127 | 128 | self.unlink_stripes_btn.update() 129 | 130 | 131 | # update SONG VOLUME GUI 132 | def updateSongVolumeGui(self, value): 133 | self.song_vol_slider.setValue(value) 134 | self.song_vol_label.setText(str(int(common.toAnalogVolumeValue(value)*100*2))) # = / 256 135 | 136 | 137 | # set SONG VOLUME 138 | def setSongVolume(self): 139 | self.gui.song.volume = common.toAnalogVolumeValue(self.song_vol_slider.value()) # = / 256 140 | self.gui.song_volume_knob.setValue(common.toControllerVolumeValue(self.gui.song.volume)) # = * 256) 141 | 142 | 143 | 144 | # MASTER PORT FINAL VOLUME 145 | def updateMasterVolumeGui(self, value): 146 | self.masterport_vol_slider.setValue(value) 147 | 148 | # MASTER PORT MUTE 149 | def updateMasterMuteGui(self, value): 150 | self.masterport_mute_checkbox.setChecked(not(value)) 151 | 152 | # set MASTER PORT FINAL VOLUME 153 | def setMasterPortFinalVolume(self): 154 | settings.master_port_final_volume = (self.masterport_vol_slider.value() / 100) 155 | 156 | # set MASTER PORT MUTE 157 | def setMasterPortMute(self): 158 | settings.master_port_mute = not(self.masterport_mute_checkbox.checkState()) 159 | 160 | 161 | # RESETS 162 | def onCustomReset(self): 163 | if settings.customreset_mixerstrip_gain == False: 164 | self.onResetGain() 165 | if settings.customreset_mixerstrip_send1 == False: 166 | self.onResetSend1() 167 | if settings.customreset_mixerstrip_send2 == False: 168 | self.onResetSend2() 169 | if settings.customreset_mixerstrip_volume == False: 170 | self.onResetVolume() 171 | if settings.customreset_mixerstrip_mute == False: 172 | self.onResetMute() 173 | 174 | def onResetGain(self): 175 | common.resetGain(settings.output_ports) 176 | self.updateGui(settings.output_ports) 177 | 178 | def onResetSend1(self): 179 | common.resetSend1(settings.output_ports) 180 | self.updateGui(settings.output_ports) 181 | 182 | def onResetSend2(self): 183 | common.resetSend2(settings.output_ports) 184 | self.updateGui(settings.output_ports) 185 | 186 | def onResetVolume(self): 187 | common.resetVolume(settings.output_ports) 188 | self.updateGui(settings.output_ports) 189 | 190 | def onResetMute(self): 191 | common.resetMute(settings.output_ports) 192 | self.updateGui(settings.output_ports) 193 | self.gui.resetMidiMute() 194 | 195 | 196 | def onUnlinkStripes(self): 197 | self.gui.mixer_stripes_midi_linked = not self.gui.mixer_stripes_midi_linked 198 | self.UpdateStripesLinkGui() 199 | 200 | 201 | def onAdvancedMode(self): 202 | common.mixer_mode = self.advanced_checkbox.isChecked() 203 | self.updateGui(settings.output_ports) 204 | 205 | 206 | 207 | # overwrite hideEvend method - can be also used with onFinished 208 | def hideEvent(self, event): 209 | self.gui.actionMixer.setEnabled(True) 210 | 211 | # triggered if dialog is closed 212 | def onFinished(self): 213 | pass 214 | 215 | 216 | # HELP management --------------------------------------------------------- 217 | 218 | def keyPressEvent(self, event): 219 | 220 | if event.key() == Qt.Key_H: # if pressed key is H (help) 221 | 222 | pos = QCursor.pos() 223 | widget = QApplication.widgetAt(pos) # this is widget under cursor 224 | 225 | if widget is None: return 226 | accName = widget.accessibleName() # this is widget accessible name 227 | 228 | if accName != "": 229 | wantedHelp = help.Context(accName) # Conversion of string accessible name to Context enum 230 | self.showContextHelp(wantedHelp) 231 | 232 | 233 | def showContextHelp(self, wantedHelp): 234 | helpText = help.getContextHelp(wantedHelp) 235 | Assistant(self, helpText, Assistant.MODE_CONTEXT) -------------------------------------------------------------------------------- /superboucle/mixerstrip.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QWidget 2 | from PyQt5.QtCore import QSettings 3 | from superboucle.preferences import Preferences 4 | 5 | from superboucle.mixerstrip_ui import Ui_Mixerstrip 6 | 7 | import settings 8 | import common 9 | 10 | 11 | 12 | class Mixerstrip(QWidget, Ui_Mixerstrip): 13 | 14 | def __init__(self, parent, portname, index): 15 | super(Mixerstrip, self).__init__(parent) 16 | self.mixer = parent 17 | self.setupUi(self) 18 | self.strip_index = index 19 | 20 | # set port name 21 | self.port_name_label.setText(portname) 22 | self.port_name = portname 23 | 24 | # ...and PyQt object name: 25 | self.setObjectName(portname) 26 | 27 | # set mixer mode 28 | self.setMixerMode() 29 | 30 | # read mixer strip values 31 | self.readMixerstrip() 32 | 33 | 34 | # signals 35 | self.gain_knob.valueChanged.connect(self.writeGainValue) 36 | 37 | self.send1_knob.valueChanged.connect(self.writeSend1Value) 38 | self.send2_knob.valueChanged.connect(self.writeSend2Value) 39 | 40 | self.vol_slider.valueChanged.connect(self.writeVolumeValue) 41 | self.mute_checkbox.clicked.connect(self.writeMuteValue) 42 | 43 | self.to_master_checkbox.clicked.connect(self.writeToMasterValue) 44 | 45 | 46 | # set mixer mode 47 | def setMixerMode(self): 48 | self.drop_checkbox.hide() # always hide until drops are implemented 49 | 50 | if common.mixer_mode == False: 51 | self.to_master_checkbox.hide() 52 | else: 53 | self.to_master_checkbox.show() 54 | 55 | # get and update each mixer values from mixer dict 56 | def readMixerstrip(self): 57 | # gain 58 | gain = settings.output_ports[self.port_name][common.PORT_GAIN_DEF] 59 | self.gain_knob.setValue(gain*200) 60 | self.gain_label.setText(str(round((gain*200 - 100)))) 61 | 62 | # send1 63 | self.send1_knob.setValue(settings.output_ports[self.port_name][common.PORT_SEND1_DEF] * 100) 64 | 65 | # send2 66 | self.send2_knob.setValue(settings.output_ports[self.port_name][common.PORT_SEND2_DEF] * 100) 67 | 68 | # vol 69 | self.vol_slider.setValue(settings.output_ports[self.port_name][common.PORT_VOLUME_DEF] * 100) 70 | 71 | # mute 72 | self.mute_checkbox.setChecked(settings.output_ports[self.port_name][common.PORT_MUTE_DEF]) 73 | 74 | # to_master 75 | self.to_master_checkbox.setChecked(settings.output_ports[self.port_name][common.PORT_TO_MASTER_DEF]) 76 | 77 | 78 | # Port name 79 | def getPortName(self): 80 | return self.port_name 81 | 82 | 83 | # write Gain into mixer dict 84 | def writeGainValue(self): 85 | gain = self.gain_knob.value() 86 | settings.output_ports[self.port_name][common.PORT_GAIN_DEF] = gain / 200 87 | self.gain_label.setText(str(round((gain-100)))) 88 | 89 | # write Send1 into mixer dict 90 | def writeSend1Value(self): 91 | settings.output_ports[self.port_name][common.PORT_SEND1_DEF] = self.send1_knob.value() / 100 92 | 93 | # write Send2 into mixer dict 94 | def writeSend2Value(self): 95 | settings.output_ports[self.port_name][common.PORT_SEND2_DEF] = self.send2_knob.value() / 100 96 | 97 | # write Volume into mixer dict 98 | def writeVolumeValue(self): 99 | settings.output_ports[self.port_name][common.PORT_VOLUME_DEF] = self.vol_slider.value() / 100 100 | 101 | # write Mute into mixer dict 102 | def writeMuteValue(self): 103 | settings.output_ports[self.port_name][common.PORT_MUTE_DEF] = bool(self.mute_checkbox.checkState()) 104 | self.mixer.muteUpdated(self.port_name, self.strip_index) 105 | 106 | 107 | def writeToMasterValue(self): 108 | settings.output_ports[self.port_name][common.PORT_TO_MASTER_DEF] = bool(self.to_master_checkbox.checkState()) 109 | 110 | def updateGuiVolume(self): 111 | self.vol_slider.setValue(settings.output_ports[self.port_name][common.PORT_VOLUME_DEF] * 100) 112 | 113 | def updateGuiSend1(self): 114 | self.send1_knob.setValue(settings.output_ports[self.port_name][common.PORT_SEND1_DEF] * 100) 115 | 116 | def updateGuiSend2(self): 117 | self.send2_knob.setValue(settings.output_ports[self.port_name][common.PORT_SEND2_DEF] * 100) 118 | 119 | def updateGuiMute(self): 120 | self.mute_checkbox.setChecked(settings.output_ports[self.port_name][common.PORT_MUTE_DEF]) 121 | 122 | -------------------------------------------------------------------------------- /superboucle/new_song.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog 2 | from superboucle.new_song_ui import Ui_Dialog 3 | from superboucle.clip import Song 4 | from PyQt5.QtCore import QSettings 5 | from superboucle.preferences import Preferences 6 | import settings 7 | import common 8 | 9 | class NewSongDialog(QDialog, Ui_Dialog): 10 | 11 | def __init__(self, parent): 12 | super(NewSongDialog, self).__init__(parent) 13 | self.gui = parent 14 | self.setupUi(self) 15 | 16 | self.widthSpinBox.setValue(settings.grid_columns) 17 | self.heightSpinBox.setValue(settings.grid_rows) 18 | self.spinBeats.setValue(settings.new_song_beats) 19 | self.spinBPM.setValue(settings.new_song_bpm) 20 | self.spinMasterVolume.setValue(settings.new_song_master_volume) 21 | 22 | self.show() 23 | 24 | def accept(self): 25 | volume = common.toStoredSongVolumeValue(self.spinMasterVolume.value()) 26 | self.gui.initUI(Song(self.widthSpinBox.value(), 27 | self.heightSpinBox.value(), 28 | volume, 29 | self.spinBPM.value(), 30 | self.spinBeats.value())) 31 | 32 | super(NewSongDialog, self).accept() 33 | -------------------------------------------------------------------------------- /superboucle/new_song_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'new_song_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.setWindowModality(QtCore.Qt.WindowModal) 17 | Dialog.resize(592, 206) 18 | self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) 19 | self.buttonBox.setGeometry(QtCore.QRect(10, 160, 571, 41)) 20 | self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 21 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 22 | self.buttonBox.setObjectName("buttonBox") 23 | self.labelGridSize = QtWidgets.QLabel(Dialog) 24 | self.labelGridSize.setGeometry(QtCore.QRect(20, 10, 121, 18)) 25 | self.labelGridSize.setStyleSheet("color: rgb(0, 0, 0);\n" 26 | "font: bold 10pt \"Noto Sans\";") 27 | self.labelGridSize.setObjectName("labelGridSize") 28 | self.labelMasterVolume = QtWidgets.QLabel(Dialog) 29 | self.labelMasterVolume.setGeometry(QtCore.QRect(180, 10, 111, 21)) 30 | self.labelMasterVolume.setStyleSheet("color: rgb(0, 0, 0);\n" 31 | "font: bold 10pt \"Noto Sans\";") 32 | self.labelMasterVolume.setObjectName("labelMasterVolume") 33 | self.spinMasterVolume = QtWidgets.QSpinBox(Dialog) 34 | self.spinMasterVolume.setGeometry(QtCore.QRect(180, 40, 71, 31)) 35 | self.spinMasterVolume.setStyleSheet("color: rgb(0, 0, 0);\n" 36 | "background-color: rgb(255, 255, 255);\n" 37 | "font: 9pt \"Noto Sans\";") 38 | self.spinMasterVolume.setMinimum(0) 39 | self.spinMasterVolume.setMaximum(200) 40 | self.spinMasterVolume.setProperty("value", 100) 41 | self.spinMasterVolume.setObjectName("spinMasterVolume") 42 | self.heightSpinBox = QtWidgets.QSpinBox(Dialog) 43 | self.heightSpinBox.setGeometry(QtCore.QRect(67, 80, 72, 31)) 44 | self.heightSpinBox.setStyleSheet("color: rgb(0, 0, 0);\n" 45 | "background-color: rgb(255, 255, 255);\n" 46 | "font: 9pt \"Noto Sans\";") 47 | self.heightSpinBox.setMaximum(16) 48 | self.heightSpinBox.setProperty("value", 8) 49 | self.heightSpinBox.setObjectName("heightSpinBox") 50 | self.widthSpinBox = QtWidgets.QSpinBox(Dialog) 51 | self.widthSpinBox.setGeometry(QtCore.QRect(67, 39, 72, 31)) 52 | self.widthSpinBox.setStyleSheet("color: rgb(0, 0, 0);\n" 53 | "background-color: rgb(255, 255, 255);\n" 54 | "font: 9pt \"Noto Sans\";") 55 | self.widthSpinBox.setMaximum(16) 56 | self.widthSpinBox.setProperty("value", 8) 57 | self.widthSpinBox.setObjectName("widthSpinBox") 58 | self.label = QtWidgets.QLabel(Dialog) 59 | self.label.setGeometry(QtCore.QRect(20, 39, 37, 25)) 60 | self.label.setStyleSheet("color: rgb(0, 0, 0);\n" 61 | "font: 10pt \"Noto Sans\";") 62 | self.label.setObjectName("label") 63 | self.label_2 = QtWidgets.QLabel(Dialog) 64 | self.label_2.setGeometry(QtCore.QRect(20, 80, 41, 31)) 65 | self.label_2.setStyleSheet("color: rgb(0, 0, 0);\n" 66 | "font: 10pt \"Noto Sans\";") 67 | self.label_2.setObjectName("label_2") 68 | self.labelBeats = QtWidgets.QLabel(Dialog) 69 | self.labelBeats.setGeometry(QtCore.QRect(470, 10, 101, 21)) 70 | self.labelBeats.setStyleSheet("color: rgb(0, 0, 0);\n" 71 | "font: bold 10pt \"Noto Sans\";") 72 | self.labelBeats.setObjectName("labelBeats") 73 | self.spinBeats = QtWidgets.QSpinBox(Dialog) 74 | self.spinBeats.setGeometry(QtCore.QRect(470, 40, 71, 31)) 75 | self.spinBeats.setStyleSheet("color: rgb(0, 0, 0);\n" 76 | "background-color: rgb(255, 255, 255);\n" 77 | "font: 9pt \"Noto Sans\";") 78 | self.spinBeats.setMinimum(1) 79 | self.spinBeats.setMaximum(99) 80 | self.spinBeats.setProperty("value", 4) 81 | self.spinBeats.setObjectName("spinBeats") 82 | self.spinBPM = QtWidgets.QSpinBox(Dialog) 83 | self.spinBPM.setGeometry(QtCore.QRect(345, 40, 71, 31)) 84 | self.spinBPM.setStyleSheet("color: rgb(0, 0, 0);\n" 85 | "background-color: rgb(255, 255, 255);\n" 86 | "font: 9pt \"Noto Sans\";") 87 | self.spinBPM.setMinimum(1) 88 | self.spinBPM.setMaximum(260) 89 | self.spinBPM.setProperty("value", 120) 90 | self.spinBPM.setObjectName("spinBPM") 91 | self.labelBPM = QtWidgets.QLabel(Dialog) 92 | self.labelBPM.setGeometry(QtCore.QRect(345, 10, 41, 21)) 93 | self.labelBPM.setStyleSheet("color: rgb(0, 0, 0);\n" 94 | "font: bold 10pt \"Noto Sans\";") 95 | self.labelBPM.setObjectName("labelBPM") 96 | 97 | self.retranslateUi(Dialog) 98 | self.buttonBox.accepted.connect(Dialog.accept) 99 | self.buttonBox.rejected.connect(Dialog.reject) 100 | QtCore.QMetaObject.connectSlotsByName(Dialog) 101 | 102 | def retranslateUi(self, Dialog): 103 | _translate = QtCore.QCoreApplication.translate 104 | Dialog.setWindowTitle(_translate("Dialog", "New Song")) 105 | self.labelGridSize.setText(_translate("Dialog", "Grid size")) 106 | self.labelMasterVolume.setText(_translate("Dialog", "Master volume")) 107 | self.label.setText(_translate("Dialog", "Width")) 108 | self.label_2.setText(_translate("Dialog", "Height")) 109 | self.labelBeats.setText(_translate("Dialog", "Beats per bar")) 110 | self.labelBPM.setText(_translate("Dialog", "BPM")) 111 | -------------------------------------------------------------------------------- /superboucle/new_song_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 592 13 | 206 14 | 15 | 16 | 17 | New Song 18 | 19 | 20 | 21 | 22 | 10 23 | 160 24 | 571 25 | 41 26 | 27 | 28 | 29 | Qt::Horizontal 30 | 31 | 32 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 33 | 34 | 35 | 36 | 37 | 38 | 20 39 | 10 40 | 121 41 | 18 42 | 43 | 44 | 45 | color: rgb(0, 0, 0); 46 | font: bold 10pt "Noto Sans"; 47 | 48 | 49 | Grid size 50 | 51 | 52 | 53 | 54 | 55 | 180 56 | 10 57 | 111 58 | 21 59 | 60 | 61 | 62 | color: rgb(0, 0, 0); 63 | font: bold 10pt "Noto Sans"; 64 | 65 | 66 | Master volume 67 | 68 | 69 | 70 | 71 | 72 | 180 73 | 40 74 | 71 75 | 31 76 | 77 | 78 | 79 | color: rgb(0, 0, 0); 80 | background-color: rgb(255, 255, 255); 81 | font: 9pt "Noto Sans"; 82 | 83 | 84 | 0 85 | 86 | 87 | 200 88 | 89 | 90 | 100 91 | 92 | 93 | 94 | 95 | 96 | 67 97 | 80 98 | 72 99 | 31 100 | 101 | 102 | 103 | color: rgb(0, 0, 0); 104 | background-color: rgb(255, 255, 255); 105 | font: 9pt "Noto Sans"; 106 | 107 | 108 | 16 109 | 110 | 111 | 8 112 | 113 | 114 | 115 | 116 | 117 | 67 118 | 39 119 | 72 120 | 31 121 | 122 | 123 | 124 | color: rgb(0, 0, 0); 125 | background-color: rgb(255, 255, 255); 126 | font: 9pt "Noto Sans"; 127 | 128 | 129 | 16 130 | 131 | 132 | 8 133 | 134 | 135 | 136 | 137 | 138 | 20 139 | 39 140 | 37 141 | 25 142 | 143 | 144 | 145 | color: rgb(0, 0, 0); 146 | font: 10pt "Noto Sans"; 147 | 148 | 149 | Width 150 | 151 | 152 | 153 | 154 | 155 | 20 156 | 80 157 | 41 158 | 31 159 | 160 | 161 | 162 | color: rgb(0, 0, 0); 163 | font: 10pt "Noto Sans"; 164 | 165 | 166 | Height 167 | 168 | 169 | 170 | 171 | 172 | 470 173 | 10 174 | 101 175 | 21 176 | 177 | 178 | 179 | color: rgb(0, 0, 0); 180 | font: bold 10pt "Noto Sans"; 181 | 182 | 183 | Beats per bar 184 | 185 | 186 | 187 | 188 | 189 | 470 190 | 40 191 | 71 192 | 31 193 | 194 | 195 | 196 | color: rgb(0, 0, 0); 197 | background-color: rgb(255, 255, 255); 198 | font: 9pt "Noto Sans"; 199 | 200 | 201 | 1 202 | 203 | 204 | 99 205 | 206 | 207 | 4 208 | 209 | 210 | 211 | 212 | 213 | 345 214 | 40 215 | 71 216 | 31 217 | 218 | 219 | 220 | color: rgb(0, 0, 0); 221 | background-color: rgb(255, 255, 255); 222 | font: 9pt "Noto Sans"; 223 | 224 | 225 | 1 226 | 227 | 228 | 260 229 | 230 | 231 | 120 232 | 233 | 234 | 235 | 236 | 237 | 345 238 | 10 239 | 41 240 | 21 241 | 242 | 243 | 244 | color: rgb(0, 0, 0); 245 | font: bold 10pt "Noto Sans"; 246 | 247 | 248 | BPM 249 | 250 | 251 | 252 | 253 | 254 | 255 | buttonBox 256 | accepted() 257 | Dialog 258 | accept() 259 | 260 | 261 | 248 262 | 254 263 | 264 | 265 | 157 266 | 274 267 | 268 | 269 | 270 | 271 | buttonBox 272 | rejected() 273 | Dialog 274 | reject() 275 | 276 | 277 | 316 278 | 260 279 | 280 | 281 | 286 282 | 274 283 | 284 | 285 | 286 | 287 | 288 | -------------------------------------------------------------------------------- /superboucle/playlist.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QFileDialog, QAbstractItemView, QMessageBox 2 | from superboucle.playlist_ui import Ui_Dialog 3 | from superboucle.clip import verify_ext 4 | import configparser, json 5 | from zipfile import ZipFile 6 | from os.path import basename, splitext 7 | from io import BytesIO, StringIO, TextIOWrapper 8 | from os import path 9 | import settings 10 | import common 11 | 12 | class PlaylistDialog(QDialog, Ui_Dialog): 13 | 14 | current_song_id = None 15 | 16 | def __init__(self, parent): 17 | super(PlaylistDialog, self).__init__(parent) 18 | self.gui = parent 19 | self.setupUi(self) 20 | self.updateList() 21 | self.removeSongBtn.clicked.connect(self.onRemove) 22 | self.addSongsBtn.clicked.connect(self.onAddSongs) 23 | self.loadPlaylistBtn.clicked.connect(self.onLoadPlaylist) 24 | self.savePlaylistBtn.clicked.connect(self.onSavePlaylist) 25 | self.loadSongBtn.clicked.connect(self.onLoadSong) 26 | self.playlistList.itemDoubleClicked.connect(self.onSongDoubleClick) 27 | self.playlistList.setDragDropMode(QAbstractItemView.InternalMove) 28 | self.playlistList.model().rowsMoved.connect(self.onMoveRows) 29 | self.cBoxBigFonts.setChecked(settings.use_big_fonts_playlist) 30 | self.cBoxBigFonts.stateChanged.connect(self.onBigFonts) 31 | 32 | self.geometry = settings.playlist_geometry 33 | 34 | if self.geometry: 35 | self.restoreGeometry(self.geometry) 36 | 37 | if self.isVisible() == False: 38 | self.show() 39 | 40 | self.useBigFonts(settings.use_big_fonts_playlist) 41 | 42 | def updateList(self): 43 | self.playlistList.clear() 44 | for i, song in enumerate(settings.playlist): 45 | name, ext = splitext(basename(song)) # song.file_name 46 | self.playlistList.addItem('{}. {}'.format(i + 1, name)) 47 | 48 | def onRemove(self): 49 | response = QMessageBox.question(self, "Remove Song?", "Are you sure you want to remove this song from the playlist?") 50 | if response == QMessageBox.No: 51 | return 52 | 53 | id = self.playlistList.currentRow() 54 | if id != -1: 55 | del settings.playlist[id] 56 | self.updateList() 57 | 58 | def onMoveRows(self, sourceParent, sourceStart, sourceEnd, 59 | destinationParent, destinationRow): 60 | l = settings.playlist 61 | destinationRow -= destinationRow > sourceStart 62 | l.insert(destinationRow, l.pop(sourceStart)) 63 | self.updateList() 64 | 65 | def onAddSongs(self): 66 | file_names, a = self.gui.getOpenFileName('Add Songs', 67 | common.SONG_FILE_TYPE, 68 | self, 69 | QFileDialog.getOpenFileNames) 70 | settings.playlist += file_names 71 | self.updateList() 72 | 73 | def onLoadPlaylist(self): 74 | 75 | file_name, a = self.gui.getOpenFileName('Import Playlist', 76 | common.PLAYLIST_FILE_TYPE, 77 | self) 78 | if not file_name: 79 | return 80 | 81 | common.updateSettingsPlaylist(file_name, settings) 82 | self.updateList() 83 | 84 | def onSavePlaylist(self): 85 | file_name, a = self.gui.getSaveFileName('Export Playlist', 86 | common.PLAYLIST_FILE_TYPE, 87 | self) 88 | 89 | if file_name: 90 | file_name = verify_ext(file_name, 'sbp') 91 | with open(file_name, 'w') as f: 92 | f.write(json.dumps(settings.playlist)) 93 | 94 | def onBigFonts(self): 95 | settings.use_big_fonts_playlist = self.cBoxBigFonts.isChecked() 96 | self.useBigFonts(settings.use_big_fonts_playlist) 97 | 98 | def onLoadSong(self): 99 | id = self.playlistList.currentRow() 100 | self.loadSong(id) 101 | 102 | def onSongDoubleClick(self, item): 103 | id = self.playlistList.row(item) 104 | self.loadSong(id) 105 | 106 | def loadSong(self, id): 107 | if id == -1: 108 | return 109 | file_name = settings.playlist[id] 110 | try: 111 | self.gui.openSongFromDisk(file_name) 112 | # self.current_song_id = id 113 | except Exception as e: 114 | print("could not load File {}.\nError: {}".format(file_name, e)) 115 | 116 | 117 | def useBigFonts(self, use = False): 118 | self.bigFontSize = settings.bigFontSize 119 | 120 | if use == False: 121 | stylesheet = 'font: 10pt "Noto Sans";' 122 | else: 123 | stylesheet = 'font: bold ' + str(self.bigFontSize) + 'pt "Noto Sans";' 124 | 125 | self.playlistList.setStyleSheet(stylesheet) 126 | 127 | # saving window position 128 | 129 | def resizeEvent(self, event): 130 | self.geometry = self.saveGeometry() 131 | settings.playlist_geometry = self.geometry 132 | 133 | def moveEvent(self, event): 134 | self.geometry = self.saveGeometry() 135 | settings.playlist_geometry = self.geometry 136 | 137 | def hideEvent(self, event): 138 | self.gui.actionPlaylist_Editor.setEnabled(True) -------------------------------------------------------------------------------- /superboucle/playlist_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/playlist_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(320, 335) 17 | icon = QtGui.QIcon() 18 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 19 | Dialog.setWindowIcon(icon) 20 | self.gridLayout = QtWidgets.QGridLayout(Dialog) 21 | self.gridLayout.setObjectName("gridLayout") 22 | self.horizontalLayout = QtWidgets.QHBoxLayout() 23 | self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) 24 | self.horizontalLayout.setObjectName("horizontalLayout") 25 | self.playlistList = QtWidgets.QListWidget(Dialog) 26 | self.playlistList.setMaximumSize(QtCore.QSize(16777215, 16777215)) 27 | self.playlistList.setSizeIncrement(QtCore.QSize(100, 100)) 28 | self.playlistList.setMovement(QtWidgets.QListView.Static) 29 | self.playlistList.setResizeMode(QtWidgets.QListView.Adjust) 30 | self.playlistList.setObjectName("playlistList") 31 | self.horizontalLayout.addWidget(self.playlistList) 32 | self.verticalLayout_3 = QtWidgets.QVBoxLayout() 33 | self.verticalLayout_3.setObjectName("verticalLayout_3") 34 | self.loadSongBtn = QtWidgets.QPushButton(Dialog) 35 | self.loadSongBtn.setObjectName("loadSongBtn") 36 | self.verticalLayout_3.addWidget(self.loadSongBtn) 37 | self.addSongsBtn = QtWidgets.QPushButton(Dialog) 38 | self.addSongsBtn.setObjectName("addSongsBtn") 39 | self.verticalLayout_3.addWidget(self.addSongsBtn) 40 | self.removeSongBtn = QtWidgets.QPushButton(Dialog) 41 | self.removeSongBtn.setObjectName("removeSongBtn") 42 | self.verticalLayout_3.addWidget(self.removeSongBtn) 43 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 44 | self.verticalLayout_3.addItem(spacerItem) 45 | self.loadPlaylistBtn = QtWidgets.QPushButton(Dialog) 46 | self.loadPlaylistBtn.setObjectName("loadPlaylistBtn") 47 | self.verticalLayout_3.addWidget(self.loadPlaylistBtn) 48 | self.savePlaylistBtn = QtWidgets.QPushButton(Dialog) 49 | self.savePlaylistBtn.setObjectName("savePlaylistBtn") 50 | self.verticalLayout_3.addWidget(self.savePlaylistBtn) 51 | self.horizontalLayout.addLayout(self.verticalLayout_3) 52 | self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) 53 | self.cBoxBigFonts = QtWidgets.QCheckBox(Dialog) 54 | self.cBoxBigFonts.setObjectName("cBoxBigFonts") 55 | self.gridLayout.addWidget(self.cBoxBigFonts, 1, 0, 1, 1) 56 | 57 | self.retranslateUi(Dialog) 58 | QtCore.QMetaObject.connectSlotsByName(Dialog) 59 | 60 | def retranslateUi(self, Dialog): 61 | _translate = QtCore.QCoreApplication.translate 62 | Dialog.setWindowTitle(_translate("Dialog", "Playlist")) 63 | self.loadSongBtn.setText(_translate("Dialog", "Load Song")) 64 | self.addSongsBtn.setText(_translate("Dialog", "Add Songs")) 65 | self.removeSongBtn.setText(_translate("Dialog", "Remove Song")) 66 | self.loadPlaylistBtn.setText(_translate("Dialog", "Import Playlist")) 67 | self.savePlaylistBtn.setText(_translate("Dialog", "Export Playlist")) 68 | self.cBoxBigFonts.setText(_translate("Dialog", "Use custom font size")) 69 | import gui_rc 70 | -------------------------------------------------------------------------------- /superboucle/playlist_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 320 10 | 335 11 | 12 | 13 | 14 | Playlist 15 | 16 | 17 | 18 | :/icons/icons/icon.png:/icons/icons/icon.png 19 | 20 | 21 | 22 | 23 | 24 | QLayout::SetNoConstraint 25 | 26 | 27 | 28 | 29 | 30 | 16777215 31 | 16777215 32 | 33 | 34 | 35 | 36 | 100 37 | 100 38 | 39 | 40 | 41 | QListView::Static 42 | 43 | 44 | QListView::Adjust 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Load Song 54 | 55 | 56 | 57 | 58 | 59 | 60 | Add Songs 61 | 62 | 63 | 64 | 65 | 66 | 67 | Remove Song 68 | 69 | 70 | 71 | 72 | 73 | 74 | Qt::Vertical 75 | 76 | 77 | 78 | 20 79 | 40 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Import Playlist 88 | 89 | 90 | 91 | 92 | 93 | 94 | Export Playlist 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | Use custom font size 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /superboucle/port_manager.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QListWidgetItem, QMessageBox 2 | from superboucle.port_manager_ui import Ui_Dialog 3 | from superboucle.add_port import AddPortDialog 4 | from superboucle.clip import verify_ext, Clip 5 | import json 6 | import settings 7 | import common 8 | 9 | class PortManager(QDialog, Ui_Dialog): 10 | def __init__(self, parent): 11 | super(PortManager, self).__init__(parent) 12 | self.gui = parent 13 | self.setupUi(self) 14 | self.backup_indixes = [] 15 | self.updateList() 16 | self.removePortBtn.clicked.connect(self.onRemove) 17 | self.addPortBtn.clicked.connect(self.onAddPort) 18 | self.loadPortlistBtn.clicked.connect(self.onLoadPortlist) 19 | self.savePortlistBtn.clicked.connect(self.onSavePortlist) 20 | self.autoconnectCBox.setChecked(settings.auto_connect_output) 21 | self.autoconnectCBox.stateChanged.connect(self.onCheckAutoconnect) 22 | self.finished.connect(self.onFinished) 23 | 24 | self.gui.updatePorts.connect(self.updateList) 25 | 26 | self.show() 27 | 28 | def updateList(self): 29 | self.portList.clear() 30 | self.backup_indixes = common.toPortsList(settings.output_ports) 31 | for name in self.backup_indixes: 32 | this_item = QListWidgetItem(name) 33 | this_item.setFlags(this_item.flags()) 34 | self.portList.addItem(this_item) 35 | 36 | def onAddPort(self): 37 | AddPortDialog(self.gui, callback=self.updateList) 38 | 39 | def onRemove(self): 40 | response = QMessageBox.question(self, "Delete Port?", "Are you sure you want to delete this output port?") 41 | if response == QMessageBox.No: 42 | return 43 | 44 | currentItem = self.portList.currentItem() 45 | 46 | if currentItem: 47 | 48 | port_name = currentItem.text() 49 | 50 | if port_name == common.DEFAULT_PORT: 51 | 52 | message = QMessageBox(self) 53 | message.setWindowTitle("Can't remove port " + port_name) 54 | message.setText("It's not possible to remove this port since it's \n" + 55 | "default clips output port") 56 | message.show() 57 | return 58 | 59 | # This condition should NEVER happen; however I keep the control here. 60 | if self.portList.count() == 1: 61 | 62 | message = QMessageBox(self) 63 | message.setWindowTitle("Can't remove port " + port_name) 64 | message.setText("SpinTool must have at least one output port defined \n" + 65 | "therefore it's not possible to remove this port") 66 | message.show() 67 | return 68 | 69 | self.gui.removePort(port_name) 70 | self.updateList() 71 | 72 | 73 | def onLoadPortlist(self): 74 | file_name, a = (self.gui.getOpenFileName('Open Portlist', common.PORTLIST_FILE_TYPE, self)) 75 | if not file_name: 76 | return 77 | 78 | result = QMessageBox.question(self, 79 | "JACK output ports", 80 | "Do you want to preserve connections, if some port already exixst? \n" + 81 | "If you choose 'No', existing ports will be reloaded and connections to these ports will be lost.", 82 | QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel) 83 | if result == QMessageBox.Cancel: 84 | return 85 | 86 | with open(file_name, 'r') as f: 87 | read_data = f.read() 88 | data = json.loads(read_data) 89 | 90 | ports = data["outputs"] # Getting from JSON data the output ports (set or list) 91 | settings.output_ports = common.toPortsDict(ports) 92 | 93 | # Updating Jack connections and Mixer layout 94 | # user could choose if deleting and creating from scratch all jack output ports, 95 | # or keeping existing jack output ports to preserve their connections 96 | if result == QMessageBox.Yes: 97 | common.updateOutputPorts(settings.output_ports, self.gui._jack_client) 98 | 99 | elif result == QMessageBox.No: 100 | common.createOutputPorts(settings.output_ports, self.gui._jack_client) 101 | 102 | 103 | self.gui.updatePorts.emit() 104 | if self.gui.output_mixer: 105 | self.gui.output_mixer.updateGui(settings.output_ports) 106 | 107 | def onSavePortlist(self): 108 | file_name, a = (self.gui.getSaveFileName('Save Portlist', common.PORTLIST_FILE_TYPE, self)) 109 | 110 | if file_name: 111 | file_name = verify_ext(file_name, 'sbl') 112 | 113 | with open(file_name, 'w') as f: 114 | #data = {"clips": [[c.output if isinstance(c, Clip) else common.DEFAULT_PORT 115 | # for c in cliprow] for cliprow in self.gui.song.clips_matrix], 116 | # "outputs": common.toPortsList(settings.output_ports)} 117 | 118 | data = {"outputs": common.toPortsList(settings.output_ports)} 119 | 120 | f.write(json.dumps(data)) 121 | 122 | def onCheckAutoconnect(self): 123 | settings.auto_connect_output = self.autoconnectCBox.isChecked() 124 | 125 | def hideEvent(self, event): 126 | self.gui.actionPort_Manager.setEnabled(True) 127 | 128 | def onFinished(self): 129 | pass 130 | -------------------------------------------------------------------------------- /superboucle/port_manager_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'port_manager_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(370, 308) 17 | icon = QtGui.QIcon() 18 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 19 | Dialog.setWindowIcon(icon) 20 | self.gridLayout = QtWidgets.QGridLayout(Dialog) 21 | self.gridLayout.setObjectName("gridLayout") 22 | self.horizontalLayout = QtWidgets.QHBoxLayout() 23 | self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) 24 | self.horizontalLayout.setObjectName("horizontalLayout") 25 | self.portList = QtWidgets.QListWidget(Dialog) 26 | self.portList.setMaximumSize(QtCore.QSize(16777215, 16777215)) 27 | self.portList.setSizeIncrement(QtCore.QSize(100, 100)) 28 | self.portList.setMovement(QtWidgets.QListView.Static) 29 | self.portList.setResizeMode(QtWidgets.QListView.Adjust) 30 | self.portList.setObjectName("portList") 31 | self.horizontalLayout.addWidget(self.portList) 32 | self.verticalLayout_3 = QtWidgets.QVBoxLayout() 33 | self.verticalLayout_3.setObjectName("verticalLayout_3") 34 | self.addPortBtn = QtWidgets.QPushButton(Dialog) 35 | self.addPortBtn.setObjectName("addPortBtn") 36 | self.verticalLayout_3.addWidget(self.addPortBtn) 37 | self.removePortBtn = QtWidgets.QPushButton(Dialog) 38 | self.removePortBtn.setObjectName("removePortBtn") 39 | self.verticalLayout_3.addWidget(self.removePortBtn) 40 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 41 | self.verticalLayout_3.addItem(spacerItem) 42 | self.loadPortlistBtn = QtWidgets.QPushButton(Dialog) 43 | self.loadPortlistBtn.setObjectName("loadPortlistBtn") 44 | self.verticalLayout_3.addWidget(self.loadPortlistBtn) 45 | self.savePortlistBtn = QtWidgets.QPushButton(Dialog) 46 | self.savePortlistBtn.setObjectName("savePortlistBtn") 47 | self.verticalLayout_3.addWidget(self.savePortlistBtn) 48 | self.horizontalLayout.addLayout(self.verticalLayout_3) 49 | self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) 50 | self.autoconnectCBox = QtWidgets.QCheckBox(Dialog) 51 | self.autoconnectCBox.setObjectName("autoconnectCBox") 52 | self.gridLayout.addWidget(self.autoconnectCBox, 1, 0, 1, 1) 53 | 54 | self.retranslateUi(Dialog) 55 | QtCore.QMetaObject.connectSlotsByName(Dialog) 56 | 57 | def retranslateUi(self, Dialog): 58 | _translate = QtCore.QCoreApplication.translate 59 | Dialog.setWindowTitle(_translate("Dialog", "Output Ports")) 60 | self.addPortBtn.setText(_translate("Dialog", "Add Port")) 61 | self.removePortBtn.setText(_translate("Dialog", "Remove Port")) 62 | self.loadPortlistBtn.setText(_translate("Dialog", "Load Portlist")) 63 | self.savePortlistBtn.setText(_translate("Dialog", "Save Portlist")) 64 | self.autoconnectCBox.setText(_translate("Dialog", "Autoconnect main output ports on program start")) 65 | import gui_rc 66 | -------------------------------------------------------------------------------- /superboucle/port_manager_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 370 10 | 308 11 | 12 | 13 | 14 | Output Ports 15 | 16 | 17 | 18 | :/icons/icons/icon.png:/icons/icons/icon.png 19 | 20 | 21 | 22 | 23 | 24 | QLayout::SetNoConstraint 25 | 26 | 27 | 28 | 29 | 30 | 16777215 31 | 16777215 32 | 33 | 34 | 35 | 36 | 100 37 | 100 38 | 39 | 40 | 41 | QListView::Static 42 | 43 | 44 | QListView::Adjust 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Add Port 54 | 55 | 56 | 57 | 58 | 59 | 60 | Remove Port 61 | 62 | 63 | 64 | 65 | 66 | 67 | Qt::Vertical 68 | 69 | 70 | 71 | 20 72 | 40 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Load Portlist 81 | 82 | 83 | 84 | 85 | 86 | 87 | Save Portlist 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Autoconnect main output ports on program start 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /superboucle/qsuperdial.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDial 2 | from PyQt5.QtGui import QPainter, QColor, QPen, QBrush 3 | from PyQt5.QtCore import Qt, QPointF 4 | from math import pi, sin, cos 5 | 6 | 7 | class QSuperDial(QDial): 8 | """Overload QDial with correct stylesheet support 9 | 10 | QSuperDial support background-color and color stylesheet 11 | properties and do NOT add "shadow" 12 | 13 | QSuperDial draw ellipse if width and height are different 14 | """ 15 | 16 | _degree270 = 1.5 * pi 17 | _degree225 = 1.25 * pi 18 | 19 | def __init__(self, parent, knobRadius=5, knobMargin=5): 20 | super(QSuperDial, self).__init__(parent) 21 | self.knobRadius = knobRadius 22 | self.knobMargin = knobMargin 23 | self.setRange(0, 100) 24 | 25 | def paintEvent(self, event): 26 | # From Peter, thanks ! 27 | # http://thecodeinn.blogspot.fr/2015/02/customizing-qdials-in-qt-part-1.html 28 | 29 | painter = QPainter(self) 30 | 31 | # So that we can use the background color 32 | painter.setBackgroundMode(1) 33 | 34 | # Smooth out the circle 35 | painter.setRenderHint(QPainter.Antialiasing) 36 | 37 | # Use background color 38 | painter.setBrush(painter.background()) 39 | 40 | # Store color from stylesheet, pen will be overriden 41 | pointColor = QColor(painter.pen().color()) 42 | 43 | # No border 44 | painter.setPen(QPen(Qt.NoPen)) 45 | 46 | # Draw first circle 47 | painter.drawEllipse(0, 0, self.width(), self.height()) 48 | 49 | # Reset color to pointColor from stylesheet 50 | painter.setBrush(QBrush(pointColor)) 51 | 52 | # Get ratio between current value and maximum to calculate angle 53 | ratio = self.value() / self.maximum() 54 | 55 | # The maximum amount of degrees is 270, offset by 225 56 | angle = ratio * self._degree270 - self._degree225 57 | 58 | # Radius of background circle 59 | rx = self.width() / 2 60 | ry = self.height() / 2 61 | 62 | # Add r to have (0,0) in center of dial 63 | y = sin(angle) * (ry - self.knobRadius - self.knobMargin) + ry 64 | x = cos(angle) * (rx - self.knobRadius - self.knobMargin) + rx 65 | 66 | # Draw the ellipse 67 | painter.drawEllipse(QPointF(x, y), 68 | self.knobRadius, 69 | self.knobRadius) 70 | -------------------------------------------------------------------------------- /superboucle/qtmodern/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.2.0' 2 | """ str: Package version. """ 3 | -------------------------------------------------------------------------------- /superboucle/qtmodern/_utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os.path import join, dirname, abspath 3 | import qtpy 4 | import platform 5 | 6 | QT_VERSION = tuple(int(v) for v in qtpy.QT_VERSION.split('.')) 7 | """ tuple: Qt version. """ 8 | 9 | PLATFORM = platform.system() 10 | 11 | 12 | def resource_path(relative_path): 13 | if hasattr(sys, '_MEIPASS'): 14 | return join(sys._MEIPASS, dirname(abspath(__file__)), relative_path) 15 | return join(dirname(abspath(__file__)), relative_path) 16 | -------------------------------------------------------------------------------- /superboucle/qtmodern/info: -------------------------------------------------------------------------------- 1 | theme for SpinTool 2 | -------------------------------------------------------------------------------- /superboucle/qtmodern/resources/frameless.qss: -------------------------------------------------------------------------------- 1 | #windowFrame { 2 | border-radius: 5px 5px 5px 5px; 3 | background-color: palette(Window); 4 | } 5 | 6 | #titleBar { 7 | border: 0px none palette(base); 8 | border-top-left-radius: 5px; 9 | border-top-right-radius: 5px; 10 | background-color: palette(Window); 11 | height: 24px; 12 | } 13 | 14 | #btnClose, #btnRestore, #btnMaximize, #btnMinimize { 15 | min-width: 14px; 16 | min-height: 14px; 17 | max-width: 14px; 18 | max-height: 14px; 19 | border-radius: 7px; 20 | margin: 4px; 21 | } 22 | 23 | #btnRestore, #btnMaximize { 24 | background-color: hsv(123, 204, 198); 25 | } 26 | 27 | #btnRestore::hover, #btnMaximize::hover { 28 | background-color: hsv(123, 204, 148); 29 | } 30 | 31 | #btnRestore::pressed, #btnMaximize::pressed { 32 | background-color: hsv(123, 204, 98); 33 | } 34 | 35 | #btnMinimize { 36 | background-color: hsv(38, 218, 253); 37 | } 38 | 39 | #btnMinimize::hover { 40 | background-color: hsv(38, 218, 203); 41 | } 42 | 43 | #btnMinimize::pressed { 44 | background-color: hsv(38, 218, 153); 45 | } 46 | 47 | #btnClose { 48 | background-color: hsv(0, 182, 252); 49 | } 50 | 51 | #btnClose::hover { 52 | background-color: hsv(0, 182, 202); 53 | } 54 | 55 | #btnClose::pressed { 56 | background-color: hsv(0, 182, 152); 57 | } 58 | 59 | #btnClose::disabled, #btnRestore::disabled, #btnMaximize::disabled, #btnMinimize::disabled { 60 | background-color: palette(midlight); 61 | } 62 | -------------------------------------------------------------------------------- /superboucle/qtmodern/resources/info: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /superboucle/qtmodern/resources/style (copy).qss: -------------------------------------------------------------------------------- 1 | /* 2 | * QGroupBox 3 | */ 4 | 5 | QGroupBox { 6 | background-color: palette(alternate-base); 7 | border: 1px solid palette(midlight); 8 | margin-top: 25px; 9 | } 10 | 11 | QGroupBox::title { 12 | background-color: transparent; 13 | } 14 | 15 | /* 16 | * QToolBar 17 | */ 18 | 19 | QToolBar { 20 | border: none; 21 | } 22 | 23 | /* 24 | * QTabBar 25 | */ 26 | 27 | QTabBar{ 28 | background-color: transparent; 29 | } 30 | 31 | QTabBar::tab{ 32 | padding: 4px 6px; 33 | background-color: transparent; 34 | border-bottom: 2px solid transparent; 35 | } 36 | 37 | QTabBar::tab:selected, QTabBar::tab:hover { 38 | color: palette(text); 39 | border-bottom: 2px solid palette(highlight); 40 | } 41 | 42 | QTabBar::tab:selected:disabled { 43 | border-bottom: 2px solid palette(light); 44 | } 45 | 46 | /* 47 | * QScrollBar 48 | */ 49 | 50 | QScrollBar:vertical { 51 | background: palette(base); 52 | border-top-right-radius: 2px; 53 | border-bottom-right-radius: 2px; 54 | width: 16px; 55 | margin: 0px; 56 | } 57 | 58 | QScrollBar::handle:vertical { 59 | background-color: palette(midlight); 60 | border-radius: 2px; 61 | min-height: 20px; 62 | margin: 2px 4px 2px 4px; 63 | } 64 | 65 | QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover, QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { 66 | background-color:palette(highlight); 67 | } 68 | 69 | QScrollBar::add-line:vertical { 70 | background: none; 71 | height: 0px; 72 | subcontrol-position: right; 73 | subcontrol-origin: margin; 74 | } 75 | 76 | QScrollBar::sub-line:vertical { 77 | background: none; 78 | height: 0px; 79 | subcontrol-position: left; 80 | subcontrol-origin: margin; 81 | } 82 | 83 | QScrollBar:horizontal{ 84 | background: palette(base); 85 | height: 16px; 86 | margin: 0px; 87 | } 88 | 89 | QScrollBar::handle:horizontal { 90 | background-color: palette(midlight); 91 | border-radius: 2px; 92 | min-width: 20px; 93 | margin: 4px 2px 4px 2px; 94 | } 95 | 96 | 97 | QScrollBar::add-line:horizontal { 98 | background: none; 99 | width: 0px; 100 | subcontrol-position: bottom; 101 | subcontrol-origin: margin; 102 | } 103 | 104 | QScrollBar::sub-line:horizontal { 105 | background: none; 106 | width: 0px; 107 | subcontrol-position: top; 108 | subcontrol-origin: margin; 109 | } 110 | 111 | /* 112 | * QScrollArea 113 | */ 114 | 115 | QScrollArea { 116 | border-style: none; 117 | } 118 | 119 | QScrollArea > QWidget > QWidget { 120 | background-color: palette(alternate-base); 121 | } 122 | 123 | /* 124 | * QSlider 125 | */ 126 | 127 | QSlider::handle:horizontal { 128 | border-radius: 5px; 129 | background-color: palette(light); 130 | max-height: 20px; 131 | } 132 | 133 | QSlider::add-page:horizontal { 134 | background: palette(base); 135 | } 136 | 137 | QSlider::sub-page:horizontal { 138 | background: palette(highlight); 139 | } 140 | 141 | QSlider::sub-page:horizontal:disabled { 142 | background-color: palette(light); 143 | } 144 | 145 | QTableView { 146 | background-color: palette(link-visited); 147 | alternate-background-color: palette(midlight); 148 | } 149 | -------------------------------------------------------------------------------- /superboucle/qtmodern/resources/style.qss: -------------------------------------------------------------------------------- 1 | /* 2 | * QGroupBox 3 | */ 4 | 5 | QGroupBox { 6 | background-color: palette(alternate-base); 7 | border: 1px solid palette(midlight); 8 | margin-top: 25px; 9 | } 10 | 11 | QGroupBox::title { 12 | background-color: transparent; 13 | } 14 | 15 | /* 16 | * QToolBar 17 | */ 18 | 19 | QToolBar { 20 | border: none; 21 | } 22 | 23 | /* 24 | * QTabBar 25 | */ 26 | 27 | QTabBar{ 28 | background-color: transparent; 29 | } 30 | 31 | QTabBar::tab{ 32 | padding: 4px 6px; 33 | background-color: transparent; 34 | border-bottom: 2px solid transparent; 35 | } 36 | 37 | QTabBar::tab:selected, QTabBar::tab:hover { 38 | color: palette(text); 39 | border-bottom: 2px solid palette(highlight); 40 | } 41 | 42 | QTabBar::tab:selected:disabled { 43 | border-bottom: 2px solid palette(light); 44 | } 45 | 46 | /* 47 | * QScrollBar 48 | */ 49 | 50 | QScrollBar:vertical { 51 | background: palette(base); 52 | border-top-right-radius: 2px; 53 | border-bottom-right-radius: 2px; 54 | width: 16px; 55 | margin: 0px; 56 | } 57 | 58 | QScrollBar::handle:vertical { 59 | background-color: palette(midlight); 60 | border-radius: 2px; 61 | min-height: 20px; 62 | margin: 2px 4px 2px 4px; 63 | } 64 | 65 | QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover, QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { 66 | background-color:palette(highlight); 67 | } 68 | 69 | QScrollBar::add-line:vertical { 70 | background: none; 71 | height: 0px; 72 | subcontrol-position: right; 73 | subcontrol-origin: margin; 74 | } 75 | 76 | QScrollBar::sub-line:vertical { 77 | background: none; 78 | height: 0px; 79 | subcontrol-position: left; 80 | subcontrol-origin: margin; 81 | } 82 | 83 | QScrollBar:horizontal{ 84 | background: palette(base); 85 | height: 16px; 86 | margin: 0px; 87 | } 88 | 89 | QScrollBar::handle:horizontal { 90 | background-color: palette(midlight); 91 | border-radius: 2px; 92 | min-width: 20px; 93 | margin: 4px 2px 4px 2px; 94 | } 95 | 96 | 97 | QScrollBar::add-line:horizontal { 98 | background: none; 99 | width: 0px; 100 | subcontrol-position: bottom; 101 | subcontrol-origin: margin; 102 | } 103 | 104 | QScrollBar::sub-line:horizontal { 105 | background: none; 106 | width: 0px; 107 | subcontrol-position: top; 108 | subcontrol-origin: margin; 109 | } 110 | 111 | /* 112 | * QScrollArea 113 | */ 114 | 115 | QScrollArea { 116 | border-style: none; 117 | } 118 | 119 | QScrollArea > QWidget > QWidget { 120 | background-color: palette(alternate-base); 121 | } 122 | 123 | /* 124 | * QSlider 125 | */ 126 | 127 | QSlider::handle:horizontal { 128 | border-radius: 5px; 129 | background-color: palette(light); 130 | max-height: 20px; 131 | } 132 | 133 | QSlider::add-page:horizontal { 134 | background: palette(base); 135 | } 136 | 137 | QSlider::sub-page:horizontal { 138 | background: palette(highlight); 139 | } 140 | 141 | QSlider::sub-page:horizontal:disabled { 142 | background-color: palette(light); 143 | } 144 | 145 | QTableView { 146 | background-color: palette(link-visited); 147 | alternate-background-color: palette(midlight); 148 | } 149 | -------------------------------------------------------------------------------- /superboucle/qtmodern/styles (copy).py: -------------------------------------------------------------------------------- 1 | from qtpy.QtGui import QPalette, QColor 2 | from ._utils import QT_VERSION, resource_path 3 | 4 | _STYLESHEET = resource_path('resources/style.qss') 5 | """ str: Main stylesheet. """ 6 | 7 | 8 | def _apply_base_theme(app): 9 | """ Apply base theme to the application. 10 | 11 | Args: 12 | app (QApplication): QApplication instance. 13 | """ 14 | 15 | if QT_VERSION < (5,): 16 | app.setStyle('plastique') 17 | else: 18 | app.setStyle('Fusion') 19 | 20 | with open(_STYLESHEET) as stylesheet: 21 | app.setStyleSheet(stylesheet.read()) 22 | 23 | 24 | def dark(app): 25 | """ Apply Dark Theme to the Qt application instance. 26 | 27 | Args: 28 | app (QApplication): QApplication instance. 29 | """ 30 | 31 | darkPalette = QPalette() 32 | 33 | # base 34 | darkPalette.setColor(QPalette.WindowText, QColor(180, 180, 180)) 35 | darkPalette.setColor(QPalette.Button, QColor(53, 53, 53)) 36 | darkPalette.setColor(QPalette.Light, QColor(180, 180, 180)) 37 | darkPalette.setColor(QPalette.Midlight, QColor(90, 90, 90)) 38 | darkPalette.setColor(QPalette.Dark, QColor(35, 35, 35)) 39 | darkPalette.setColor(QPalette.Text, QColor(180, 180, 180)) 40 | darkPalette.setColor(QPalette.BrightText, QColor(180, 180, 180)) 41 | darkPalette.setColor(QPalette.ButtonText, QColor(180, 180, 180)) 42 | darkPalette.setColor(QPalette.Base, QColor(42, 42, 42)) 43 | darkPalette.setColor(QPalette.Window, QColor(53, 53, 53)) 44 | darkPalette.setColor(QPalette.Shadow, QColor(20, 20, 20)) 45 | darkPalette.setColor(QPalette.Highlight, QColor(42, 130, 218)) 46 | darkPalette.setColor(QPalette.HighlightedText, QColor(180, 180, 180)) 47 | darkPalette.setColor(QPalette.Link, QColor(56, 252, 196)) 48 | darkPalette.setColor(QPalette.AlternateBase, QColor(66, 66, 66)) 49 | darkPalette.setColor(QPalette.ToolTipBase, QColor(53, 53, 53)) 50 | darkPalette.setColor(QPalette.ToolTipText, QColor(180, 180, 180)) 51 | darkPalette.setColor(QPalette.LinkVisited, QColor(80, 80, 80)) 52 | 53 | # disabled 54 | darkPalette.setColor(QPalette.Disabled, QPalette.WindowText, 55 | QColor(127, 127, 127)) 56 | darkPalette.setColor(QPalette.Disabled, QPalette.Text, 57 | QColor(127, 127, 127)) 58 | darkPalette.setColor(QPalette.Disabled, QPalette.ButtonText, 59 | QColor(127, 127, 127)) 60 | darkPalette.setColor(QPalette.Disabled, QPalette.Highlight, 61 | QColor(80, 80, 80)) 62 | darkPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, 63 | QColor(127, 127, 127)) 64 | 65 | app.setPalette(darkPalette) 66 | 67 | _apply_base_theme(app) 68 | 69 | 70 | def light(app): 71 | """ Apply Light Theme to the Qt application instance. 72 | 73 | Args: 74 | app (QApplication): QApplication instance. 75 | """ 76 | 77 | lightPalette = QPalette() 78 | 79 | # base 80 | lightPalette.setColor(QPalette.WindowText, QColor(0, 0, 0)) 81 | lightPalette.setColor(QPalette.Button, QColor(240, 240, 240)) 82 | lightPalette.setColor(QPalette.Light, QColor(180, 180, 180)) 83 | lightPalette.setColor(QPalette.Midlight, QColor(200, 200, 200)) 84 | lightPalette.setColor(QPalette.Dark, QColor(225, 225, 225)) 85 | lightPalette.setColor(QPalette.Text, QColor(0, 0, 0)) 86 | lightPalette.setColor(QPalette.BrightText, QColor(0, 0, 0)) 87 | lightPalette.setColor(QPalette.ButtonText, QColor(0, 0, 0)) 88 | lightPalette.setColor(QPalette.Base, QColor(237, 237, 237)) 89 | lightPalette.setColor(QPalette.Window, QColor(240, 240, 240)) 90 | lightPalette.setColor(QPalette.Shadow, QColor(20, 20, 20)) 91 | lightPalette.setColor(QPalette.Highlight, QColor(76, 163, 224)) 92 | lightPalette.setColor(QPalette.HighlightedText, QColor(0, 0, 0)) 93 | lightPalette.setColor(QPalette.Link, QColor(0, 162, 232)) 94 | lightPalette.setColor(QPalette.AlternateBase, QColor(225, 225, 225)) 95 | lightPalette.setColor(QPalette.ToolTipBase, QColor(240, 240, 240)) 96 | lightPalette.setColor(QPalette.ToolTipText, QColor(0, 0, 0)) 97 | lightPalette.setColor(QPalette.LinkVisited, QColor(222, 222, 222)) 98 | 99 | # disabled 100 | lightPalette.setColor(QPalette.Disabled, QPalette.WindowText, 101 | QColor(115, 115, 115)) 102 | lightPalette.setColor(QPalette.Disabled, QPalette.Text, 103 | QColor(115, 115, 115)) 104 | lightPalette.setColor(QPalette.Disabled, QPalette.ButtonText, 105 | QColor(115, 115, 115)) 106 | lightPalette.setColor(QPalette.Disabled, QPalette.Highlight, 107 | QColor(190, 190, 190)) 108 | lightPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, 109 | QColor(115, 115, 115)) 110 | 111 | app.setPalette(lightPalette) 112 | 113 | _apply_base_theme(app) 114 | 115 | -------------------------------------------------------------------------------- /superboucle/qtmodern/styles.py: -------------------------------------------------------------------------------- 1 | from qtpy.QtGui import QPalette, QColor 2 | from ._utils import QT_VERSION, resource_path 3 | 4 | _STYLESHEET = resource_path('resources/style.qss') 5 | """ str: Main stylesheet. """ 6 | 7 | 8 | def _apply_base_theme(app): 9 | """ Apply base theme to the application. 10 | 11 | Args: 12 | app (QApplication): QApplication instance. 13 | """ 14 | 15 | if QT_VERSION < (5,): 16 | app.setStyle('plastique') 17 | else: 18 | app.setStyle('Fusion') 19 | 20 | with open(_STYLESHEET) as stylesheet: 21 | app.setStyleSheet(stylesheet.read()) 22 | 23 | 24 | def dark(app): 25 | """ Apply Dark Theme to the Qt application instance. 26 | 27 | Args: 28 | app (QApplication): QApplication instance. 29 | """ 30 | 31 | darkPalette = QPalette() 32 | 33 | # base 34 | darkPalette.setColor(QPalette.WindowText, QColor(180, 180, 180)) 35 | darkPalette.setColor(QPalette.Button, QColor(53, 53, 53)) 36 | darkPalette.setColor(QPalette.Light, QColor(180, 180, 180)) 37 | darkPalette.setColor(QPalette.Midlight, QColor(90, 90, 90)) 38 | darkPalette.setColor(QPalette.Dark, QColor(35, 35, 35)) 39 | darkPalette.setColor(QPalette.Text, QColor(180, 180, 180)) 40 | darkPalette.setColor(QPalette.BrightText, QColor(180, 180, 180)) 41 | darkPalette.setColor(QPalette.ButtonText, QColor(180, 180, 180)) 42 | darkPalette.setColor(QPalette.Base, QColor(42, 42, 42)) 43 | 44 | darkPalette.setColor(QPalette.Window, QColor(53, 53, 53)) 45 | #darkPalette.setColor(QPalette.Window, QColor(66, 66, 66)) 46 | 47 | darkPalette.setColor(QPalette.Shadow, QColor(20, 20, 20)) 48 | darkPalette.setColor(QPalette.Highlight, QColor(42, 130, 218)) 49 | darkPalette.setColor(QPalette.HighlightedText, QColor(180, 180, 180)) 50 | darkPalette.setColor(QPalette.Link, QColor(56, 252, 196)) 51 | darkPalette.setColor(QPalette.AlternateBase, QColor(66, 66, 66)) 52 | darkPalette.setColor(QPalette.ToolTipBase, QColor(53, 53, 53)) 53 | darkPalette.setColor(QPalette.ToolTipText, QColor(180, 180, 180)) 54 | darkPalette.setColor(QPalette.LinkVisited, QColor(80, 80, 80)) 55 | 56 | # disabled 57 | darkPalette.setColor(QPalette.Disabled, QPalette.WindowText, 58 | QColor(127, 127, 127)) 59 | darkPalette.setColor(QPalette.Disabled, QPalette.Text, 60 | QColor(127, 127, 127)) 61 | darkPalette.setColor(QPalette.Disabled, QPalette.ButtonText, 62 | QColor(127, 127, 127)) 63 | darkPalette.setColor(QPalette.Disabled, QPalette.Highlight, 64 | QColor(80, 80, 80)) 65 | darkPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, 66 | QColor(127, 127, 127)) 67 | 68 | app.setPalette(darkPalette) 69 | 70 | _apply_base_theme(app) 71 | 72 | 73 | def light(app): 74 | """ Apply Light Theme to the Qt application instance. 75 | 76 | Args: 77 | app (QApplication): QApplication instance. 78 | """ 79 | 80 | lightPalette = QPalette() 81 | 82 | # base 83 | lightPalette.setColor(QPalette.WindowText, QColor(0, 0, 0)) 84 | lightPalette.setColor(QPalette.Button, QColor(240, 240, 240)) 85 | lightPalette.setColor(QPalette.Light, QColor(180, 180, 180)) 86 | lightPalette.setColor(QPalette.Midlight, QColor(200, 200, 200)) 87 | lightPalette.setColor(QPalette.Dark, QColor(225, 225, 225)) 88 | lightPalette.setColor(QPalette.Text, QColor(0, 0, 0)) 89 | lightPalette.setColor(QPalette.BrightText, QColor(0, 0, 0)) 90 | lightPalette.setColor(QPalette.ButtonText, QColor(0, 0, 0)) 91 | lightPalette.setColor(QPalette.Base, QColor(237, 237, 237)) 92 | lightPalette.setColor(QPalette.Window, QColor(240, 240, 240)) 93 | lightPalette.setColor(QPalette.Shadow, QColor(20, 20, 20)) 94 | lightPalette.setColor(QPalette.Highlight, QColor(76, 163, 224)) 95 | lightPalette.setColor(QPalette.HighlightedText, QColor(0, 0, 0)) 96 | lightPalette.setColor(QPalette.Link, QColor(0, 162, 232)) 97 | lightPalette.setColor(QPalette.AlternateBase, QColor(225, 225, 225)) 98 | lightPalette.setColor(QPalette.ToolTipBase, QColor(240, 240, 240)) 99 | lightPalette.setColor(QPalette.ToolTipText, QColor(0, 0, 0)) 100 | lightPalette.setColor(QPalette.LinkVisited, QColor(222, 222, 222)) 101 | 102 | # disabled 103 | lightPalette.setColor(QPalette.Disabled, QPalette.WindowText, 104 | QColor(115, 115, 115)) 105 | lightPalette.setColor(QPalette.Disabled, QPalette.Text, 106 | QColor(115, 115, 115)) 107 | lightPalette.setColor(QPalette.Disabled, QPalette.ButtonText, 108 | QColor(115, 115, 115)) 109 | lightPalette.setColor(QPalette.Disabled, QPalette.Highlight, 110 | QColor(190, 190, 190)) 111 | lightPalette.setColor(QPalette.Disabled, QPalette.HighlightedText, 112 | QColor(115, 115, 115)) 113 | 114 | app.setPalette(lightPalette) 115 | 116 | _apply_base_theme(app) 117 | 118 | -------------------------------------------------------------------------------- /superboucle/scene_manager_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Sviluppo/SpinTool/superboucle/scene_manager_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.14.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.resize(320, 333) 17 | icon = QtGui.QIcon() 18 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 19 | Dialog.setWindowIcon(icon) 20 | self.gridLayout = QtWidgets.QGridLayout(Dialog) 21 | self.gridLayout.setObjectName("gridLayout") 22 | self.horizontalLayout = QtWidgets.QHBoxLayout() 23 | self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) 24 | self.horizontalLayout.setObjectName("horizontalLayout") 25 | self.scenelistList = QtWidgets.QListWidget(Dialog) 26 | self.scenelistList.setMaximumSize(QtCore.QSize(16777215, 16777215)) 27 | self.scenelistList.setSizeIncrement(QtCore.QSize(100, 100)) 28 | self.scenelistList.setMovement(QtWidgets.QListView.Static) 29 | self.scenelistList.setResizeMode(QtWidgets.QListView.Adjust) 30 | self.scenelistList.setObjectName("scenelistList") 31 | self.horizontalLayout.addWidget(self.scenelistList) 32 | self.verticalLayout_3 = QtWidgets.QVBoxLayout() 33 | self.verticalLayout_3.setObjectName("verticalLayout_3") 34 | self.loadScenesBtn = QtWidgets.QPushButton(Dialog) 35 | self.loadScenesBtn.setObjectName("loadScenesBtn") 36 | self.verticalLayout_3.addWidget(self.loadScenesBtn) 37 | self.addScenesBtn = QtWidgets.QPushButton(Dialog) 38 | self.addScenesBtn.setObjectName("addScenesBtn") 39 | self.verticalLayout_3.addWidget(self.addScenesBtn) 40 | self.removeScenesBtn = QtWidgets.QPushButton(Dialog) 41 | self.removeScenesBtn.setObjectName("removeScenesBtn") 42 | self.verticalLayout_3.addWidget(self.removeScenesBtn) 43 | self.renameSceneBtn = QtWidgets.QPushButton(Dialog) 44 | self.renameSceneBtn.setObjectName("renameSceneBtn") 45 | self.verticalLayout_3.addWidget(self.renameSceneBtn) 46 | self.preview = QtWidgets.QGridLayout() 47 | self.preview.setSpacing(2) 48 | self.preview.setObjectName("preview") 49 | self.verticalLayout_3.addLayout(self.preview) 50 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 51 | self.verticalLayout_3.addItem(spacerItem) 52 | self.setInitialSceneBtn = QtWidgets.QPushButton(Dialog) 53 | self.setInitialSceneBtn.setObjectName("setInitialSceneBtn") 54 | self.verticalLayout_3.addWidget(self.setInitialSceneBtn) 55 | self.horizontalLayout.addLayout(self.verticalLayout_3) 56 | self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) 57 | self.cBoxBigFonts = QtWidgets.QCheckBox(Dialog) 58 | self.cBoxBigFonts.setObjectName("cBoxBigFonts") 59 | self.gridLayout.addWidget(self.cBoxBigFonts, 1, 0, 1, 1) 60 | 61 | self.retranslateUi(Dialog) 62 | QtCore.QMetaObject.connectSlotsByName(Dialog) 63 | 64 | def retranslateUi(self, Dialog): 65 | _translate = QtCore.QCoreApplication.translate 66 | Dialog.setWindowTitle(_translate("Dialog", "Scenes")) 67 | self.loadScenesBtn.setText(_translate("Dialog", "Trigger Scene")) 68 | self.addScenesBtn.setText(_translate("Dialog", "Add Scene")) 69 | self.removeScenesBtn.setText(_translate("Dialog", "Delete Scene")) 70 | self.renameSceneBtn.setText(_translate("Dialog", "Rename Scene")) 71 | self.setInitialSceneBtn.setText(_translate("Dialog", "Set initial Scene")) 72 | self.cBoxBigFonts.setText(_translate("Dialog", "Use custom font size")) 73 | import gui_rc 74 | -------------------------------------------------------------------------------- /superboucle/scene_manager_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 320 10 | 333 11 | 12 | 13 | 14 | Scenes 15 | 16 | 17 | 18 | :/icons/icons/icon.png:/icons/icons/icon.png 19 | 20 | 21 | 22 | 23 | 24 | QLayout::SetNoConstraint 25 | 26 | 27 | 28 | 29 | 30 | 16777215 31 | 16777215 32 | 33 | 34 | 35 | 36 | 100 37 | 100 38 | 39 | 40 | 41 | QListView::Static 42 | 43 | 44 | QListView::Adjust 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Trigger Scene 54 | 55 | 56 | 57 | 58 | 59 | 60 | Add Scene 61 | 62 | 63 | 64 | 65 | 66 | 67 | Delete Scene 68 | 69 | 70 | 71 | 72 | 73 | 74 | Rename Scene 75 | 76 | 77 | 78 | 79 | 80 | 81 | 2 82 | 83 | 84 | 85 | 86 | 87 | 88 | Qt::Vertical 89 | 90 | 91 | 92 | 20 93 | 40 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Set initial Scene 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Use custom font size 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /superboucle/settings.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manucontrovento/SpinTool/9664552342d742f1747022cf06d93d14fcc83ce3/superboucle/settings.pyc -------------------------------------------------------------------------------- /superboucle/song_annotation.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog 2 | from superboucle.song_annotation_ui import Ui_Dialog 3 | import copy 4 | import settings 5 | 6 | class SongAnnotation(QDialog, Ui_Dialog): 7 | 8 | def __init__(self, parent): 9 | super(SongAnnotation, self).__init__(parent) 10 | self.gui = parent 11 | self.setupUi(self) 12 | self.txtAnnotation.clear() 13 | self.txtAnnotation.setText(self.gui.song.annotation) 14 | self.txtAnnotation.textChanged.connect(self.onTextChanged) 15 | 16 | self.setLayout(self.formLayout) 17 | 18 | if settings.song_annotation_geometry: 19 | self.restoreGeometry(settings.song_annotation_geometry) 20 | 21 | self.show() 22 | 23 | # Saving window position 24 | 25 | def onTextChanged(self): 26 | self.gui.song.annotation = str(self.txtAnnotation.toPlainText()) 27 | 28 | def moveEvent(self, event): 29 | self.geometry = self.saveGeometry() 30 | settings.song_annotation_geometry = copy.deepcopy(self.geometry) 31 | 32 | def updateText(self, text): 33 | self.txtAnnotation.setText(text) 34 | 35 | def hideEvent(self, event): 36 | self.onHide() 37 | 38 | def onHide(self): 39 | self.gui.song.annotation = str(self.txtAnnotation.toPlainText()) 40 | self.gui.action_SongAnnotation.setEnabled(True) 41 | 42 | def onFinished(self): 43 | pass 44 | -------------------------------------------------------------------------------- /superboucle/song_annotation_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '/home/manu/Applicazioni/SpinTool/spintool/song_annotation_ui.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.12.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | 13 | class Ui_Dialog(object): 14 | def setupUi(self, Dialog): 15 | Dialog.setObjectName("Dialog") 16 | Dialog.setWindowModality(QtCore.Qt.NonModal) 17 | Dialog.resize(421, 372) 18 | icon = QtGui.QIcon() 19 | icon.addPixmap(QtGui.QPixmap(":/icons/icons/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 20 | Dialog.setWindowIcon(icon) 21 | Dialog.setStyleSheet("background-color: rgb(255, 255, 170);") 22 | self.formLayoutWidget = QtWidgets.QWidget(Dialog) 23 | self.formLayoutWidget.setGeometry(QtCore.QRect(10, 10, 401, 351)) 24 | self.formLayoutWidget.setObjectName("formLayoutWidget") 25 | self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget) 26 | self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow) 27 | self.formLayout.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows) 28 | self.formLayout.setFormAlignment(QtCore.Qt.AlignCenter) 29 | self.formLayout.setContentsMargins(2, 2, 2, 2) 30 | self.formLayout.setObjectName("formLayout") 31 | self.txtAnnotation = QtWidgets.QTextBrowser(self.formLayoutWidget) 32 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 33 | sizePolicy.setHorizontalStretch(1) 34 | sizePolicy.setVerticalStretch(1) 35 | sizePolicy.setHeightForWidth(self.txtAnnotation.sizePolicy().hasHeightForWidth()) 36 | self.txtAnnotation.setSizePolicy(sizePolicy) 37 | self.txtAnnotation.setStyleSheet("color: rgb(0, 0, 0);\n" 38 | "background-color: rgb(255, 255, 170);\n" 39 | "font: 11pt \"Noto Sans\";\n" 40 | "border: None;") 41 | self.txtAnnotation.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) 42 | self.txtAnnotation.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 43 | self.txtAnnotation.setDocumentTitle("") 44 | self.txtAnnotation.setReadOnly(False) 45 | self.txtAnnotation.setAcceptRichText(True) 46 | self.txtAnnotation.setObjectName("txtAnnotation") 47 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.txtAnnotation) 48 | 49 | self.retranslateUi(Dialog) 50 | QtCore.QMetaObject.connectSlotsByName(Dialog) 51 | 52 | def retranslateUi(self, Dialog): 53 | _translate = QtCore.QCoreApplication.translate 54 | Dialog.setWindowTitle(_translate("Dialog", "Song annotation")) 55 | self.txtAnnotation.setHtml(_translate("Dialog", "\n" 56 | "\n" 59 | "


")) 60 | import gui_rc 61 | -------------------------------------------------------------------------------- /superboucle/song_annotation_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 421 13 | 372 14 | 15 | 16 | 17 | Song annotation 18 | 19 | 20 | 21 | :/icons/icons/icon.png:/icons/icons/icon.png 22 | 23 | 24 | background-color: rgb(255, 255, 170); 25 | 26 | 27 | 28 | 29 | 10 30 | 10 31 | 401 32 | 351 33 | 34 | 35 | 36 | 37 | QFormLayout::ExpandingFieldsGrow 38 | 39 | 40 | QFormLayout::WrapLongRows 41 | 42 | 43 | Qt::AlignCenter 44 | 45 | 46 | 2 47 | 48 | 49 | 2 50 | 51 | 52 | 2 53 | 54 | 55 | 2 56 | 57 | 58 | 59 | 60 | 61 | 1 62 | 1 63 | 64 | 65 | 66 | color: rgb(0, 0, 0); 67 | background-color: rgb(255, 255, 170); 68 | font: 11pt "Noto Sans"; 69 | border: None; 70 | 71 | 72 | Qt::ScrollBarAlwaysOn 73 | 74 | 75 | Qt::ScrollBarAlwaysOff 76 | 77 | 78 | 79 | 80 | 81 | false 82 | 83 | 84 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 85 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 86 | p, li { white-space: pre-wrap; } 87 | </style></head><body style=" font-family:'Noto Sans'; font-size:11pt; font-weight:400; font-style:normal;"> 88 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p></body></html> 89 | 90 | 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | --------------------------------------------------------------------------------