def removeRecentProjectFile(self, projectFile): if not isinstance(projectFile, (QUrl, pyCompatibility.basestring)): raise TypeError("Unexpected data type: {}".format( projectFile.__class__)) if isinstance(projectFile, QUrl): projectFileNorm = projectFile.toLocalFile() if not projectFileNorm: projectFileNorm = projectFile.toString() else: projectFileNorm = QUrl(projectFile).toLocalFile() if not projectFileNorm: projectFileNorm = QUrl.fromLocalFile(projectFile).toLocalFile() projects = self._recentProjectFiles() # remove duplicates while preserving order from collections import OrderedDict uniqueProjects = OrderedDict.fromkeys(projects) projects = list(uniqueProjects) # remove previous usage of the value if projectFileNorm not in uniqueProjects: return projects.remove(projectFileNorm) settings = QSettings() settings.beginGroup("RecentFiles") size = settings.beginWriteArray("Projects") for i, p in enumerate(projects): settings.setArrayIndex(i) settings.setValue("filepath", p) settings.endArray() settings.sync() self.recentProjectFilesChanged.emit()
def _loadSettings(self): settings = QSettings() # Recent documents size = settings.beginReadArray("RecentDocuments") for idx in range(size-1, -1, -1): settings.setArrayIndex(idx) canonicalName = QFileInfo(settings.value("Document")).canonicalFilePath() self._updateRecentDocuments(canonicalName) settings.endArray() # Application properties: Geometry geometry = settings.value("Application/Geometry", QByteArray()) if self._preferences.restoreApplicationGeometry() else QByteArray() if not geometry.isEmpty(): self.restoreGeometry(geometry) else: availableGeometry = self.screen().availableGeometry() self.resize(availableGeometry.width() * 2/3, availableGeometry.height() * 2/3) self.move((availableGeometry.width() - self.width()) / 2, (availableGeometry.height() - self.height()) / 2) # Application properties: State state = settings.value("Application/State", QByteArray()) if self._preferences.restoreApplicationState() else QByteArray() if not state.isEmpty(): self.restoreState(state) else: self._toolbarApplication.setVisible(True) self._toolbarDocument.setVisible(True) self._toolbarEdit.setVisible(True) self._toolbarTools.setVisible(True) self._toolbarView.setVisible(False) self._toolbarHelp.setVisible(False)
def __init__(self, profiles, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(ProfilesDialog, self).__init__(parent) self.setupUi(self) self.model = ProfilesModel(profiles) self.view = self.table_view self.view.setModel(self.model) settings = QSettings() settings.beginGroup('ProfilesDialog') geom = settings.value('geom') if geom is not None: self.restoreGeometry(geom) sz = settings.beginReadArray('col_size') for i in range(sz): settings.setArrayIndex(i) w = settings.value('col') self.view.setColumnWidth(i, w) settings.endArray() settings.endGroup() self.view.horizontalHeader().setStretchLastSection(True)
def _rename_step(workflow_location, old_name, new_name, new_src_uri): updated = False workflow_conf_location = os.path.join(workflow_location, DEFAULT_WORKFLOW_PROJECT_FILENAME) workflow_conf = QSettings(workflow_conf_location, QSettings.IniFormat) workflow_conf.beginGroup('nodes') node_count = workflow_conf.beginReadArray('nodelist') name_indices = [] for i in range(node_count): workflow_conf.setArrayIndex(i) name = workflow_conf.value('name') if name == old_name: name_indices.append(i) workflow_conf.endArray() workflow_conf.beginWriteArray('nodelist') for i in range(node_count): workflow_conf.setArrayIndex(i) if i in name_indices: workflow_conf.setValue('name', new_name) workflow_conf.setValue('source_uri', new_src_uri) updated = True workflow_conf.endArray() workflow_conf.endGroup() return updated
def save(self) -> None: settings = QSettings(self.config_path) settings.beginWriteArray("downloads") for i in range(len(self.downloads)): settings.setArrayIndex(i) settings.setValue("download", Download.pack(self.downloads[i])) settings.endArray()
def load(self) -> None: settings = QSettings(self.config_path) size = settings.beginReadArray("downloads") for i in range(size): settings.setArrayIndex(i) self.add_download(Download.unpack(settings.value("download"))) settings.endArray()
def save(self) -> None: settings = QSettings(self.config_path, QSettings.NativeFormat) settings.beginWriteArray("predownloads") for i in range(len(self.predownloads)): settings.setArrayIndex(i) settings.setValue("predownload", PreDownload.pack(self.predownloads[i])) settings.endArray()
def load(self) -> None: settings = QSettings(self.config_path, QSettings.NativeFormat) size = settings.beginReadArray("predownloads") for i in range(size): settings.setArrayIndex(i) self.add_predownload( PreDownload.unpack(settings.value("predownload"))) settings.endArray()
def store(self, settings: QSettings): settings.beginGroup("gui") self.gui.store(settings) settings.endGroup() settings.beginWriteArray("repos") for i, repo in enumerate(self.repositories): settings.setArrayIndex(i) repo.store(settings) settings.endArray()
def done(self, r): settings = QSettings() settings.beginGroup('ProfilesDialog') settings.setValue('geom', self.saveGeometry()) settings.beginWriteArray('col_size') for i in range(3): settings.setArrayIndex(i) settings.setValue('col', self.view.columnWidth(i)) settings.endArray() settings.endGroup() super().done(r)
def _recentProjectFiles(self): projects = [] settings = QSettings() settings.beginGroup("RecentFiles") size = settings.beginReadArray("Projects") for i in range(size): settings.setArrayIndex(i) p = settings.value("filepath") if p: projects.append(p) settings.endArray() return projects
def test_reload(self, temp_settings): app, home = temp_settings settings = QSettings() settings.clear() settings.setValue('instrument', 123) settings.beginWriteArray('exercises') settings.setValue('sample1', ['1', '2', '3']) settings.setValue('sample2', ['4', '5', '6']) settings.endArray() app.reload_settings() assert app.instrument == 123 assert app.exercises == [['sample1', ['1', '2', '3']], ['sample2', ['4', '5', '6']]]
def temp_settings(): temp_home = tempfile.TemporaryDirectory() os.environ['HOME'] = temp_home.name QCoreApplication.setOrganizationName(APPLICATION_NAME) QCoreApplication.setApplicationName(APPLICATION_NAME) q_settings = QSettings() q_settings.setValue('instrument', 123) q_settings.beginWriteArray('exercises') q_settings.setValue('sample1', ['1', '2', '3']) q_settings.setValue('sample2', ['4', '5', '6']) q_settings.endArray() q_settings.sync() settings = Settings() yield settings
class NotebookManager: def __init__(self): self.settings = QSettings(ORGANIZATION_STR) self.allNotebooks = [] self.readConfig() def notebook(self, name): for notebook in self.allNotebooks: if notebook.name == name: return notebook raise KeyError(name) def notebooks(self): return self.allNotebooks def createNewNotebook(self, name, storagePath): ok = QDir(storagePath).mkpath(".") nb = Notebook(name, storagePath) self.allNotebooks.append(nb) self.writeConfig() return nb def readConfig(self): notebookCount = self.settings.beginReadArray("notebooks") for i in range(notebookCount): self.settings.setArrayIndex(i) name = self.settings.value("name") storagePath = self.settings.value("storagePath") self.allNotebooks.append(Notebook(name, storagePath)) self.settings.endArray() def writeConfig(self): #self.settings.beginGroup("notebooks") self.settings.beginWriteArray("notebooks") for i, notebook in enumerate(self.allNotebooks): self.settings.setArrayIndex(i) self.settings.setValue("name", notebook.name) self.settings.setValue("storagePath", notebook.localStoragePath) self.settings.endArray() self.settings.sync() #self.settings.endGroup()
def load(cls, settings: QSettings): settings.beginGroup("gui") gui = GuiConfig.load(settings) settings.endGroup() repo_count = settings.beginReadArray("repos") repos = [] for i in range(repo_count): settings.setArrayIndex(i) repos.append(RepositoryConfig.load(settings)) settings.endArray() return cls( gui=gui, repositories=repos, )
def save(self): settings = QSettings() settings.beginGroup(self.__class__.__name__) settings.beginWriteArray('col_size') for i in range(3): settings.setArrayIndex(i) w = self.columnWidth(i) if i != 0 or w != 0: settings.setValue('col', w) settings.endArray() if self.ini_check: settings.setValue('display_images', self.images_display.checkState() == Qt.Checked) settings.setValue('use_cache', self.use_cache) settings.setValue('cache_size', self.cache_size) settings.endGroup()
def test_save_settings(self, temp_settings): settings = temp_settings settings.exercises = [['sample3', [0, 0, 0]]] settings.instrument = 99 settings.save_settings() q_settings = QSettings() saved_instrument = q_settings.value('instrument') saved_exercises = [] q_settings.beginReadArray('exercises') for ex in q_settings.allKeys(): if ex == 'size': continue saved_exercises.append([ex, q_settings.value(ex)]) q_settings.endArray() assert saved_instrument == 99 assert saved_exercises == [['sample3', [0, 0, 0]]]
def _saveSettings(self): settings = QSettings() # Recent documents if not self._preferences.restoreRecentDocuments(): self._recentDocuments.clear() settings.remove("RecentDocuments") settings.beginWriteArray("RecentDocuments") for idx in range(len(self._recentDocuments)): settings.setArrayIndex(idx) settings.setValue("Document", self._recentDocuments[idx]) settings.endArray() # Application properties: Geometry geometry = self.saveGeometry() if self._preferences.restoreApplicationGeometry() else QByteArray() settings.setValue("Application/Geometry", geometry) # Application properties: State state = self.saveState() if self._preferences.restoreApplicationState() else QByteArray() settings.setValue("Application/State", state)
def update_path(self, signal_name): settings = QSettings("Nebula", self.project_name) settings.beginGroup("Project") settings.setValue("LastEdit", QDateTime.currentDateTime().toString()) settings.endGroup() settings.beginGroup("SignalFiles") before_list_path = settings.value("Path") before_list_path = before_list_path.split(',') print( f'Before list : {settings.fileName(), self.project_name, signal_name, before_list_path}' ) before_list_path.extend(self.list_path) print(f'\033[1;36;40m Before list path: {before_list_path} \033[0m \n') self.list_path = list( dict.fromkeys(before_list_path)) # Removes the repeated path names if 'None' in self.list_path: self.list_path.remove('None') actual_list_path = ','.join(self.list_path) print(f'\033[1;36;40m After list path: {actual_list_path} \033[0m \n') settings.setValue("Path", actual_list_path) settings.endGroup() self.info, self.sfreq = read_info(self.list_path) settings.beginGroup("Info") settings.beginWriteArray("sfreq") for i in range(len(self.sfreq)): settings.setArrayIndex(i) settings.setValue("sfreq", self.sfreq[i]) settings.endArray() settings.beginWriteArray("SubjectInfo") for i in range(len(self.info)): settings.setArrayIndex(i) for key, value in self.info[i].items(): settings.setValue(key, str(value)) settings.endArray() settings.endGroup() del before_list_path, actual_list_path
def load(self, ini_check): settings = QSettings() settings.beginGroup(self.__class__.__name__) sz = settings.beginReadArray('col_size') for i in range(sz): settings.setArrayIndex(i) w = settings.value('col', type=int) if w is not None: self.setColumnWidth(i, w) settings.endArray() if ini_check: display = settings.value('display_images', type=bool) self.images_display.setCheckState( Qt.Checked if display else Qt.Unchecked) self.use_cache = settings.value('use_cache', True, type=bool) self.cache_size = settings.value('cache_size', 1000) else: display = self.images_display if not display: self.setColumnHidden(0, True) settings.endGroup() delegate = ImageDelegate(self) self.setItemDelegateForColumn(0, delegate) self.pre_load(delegate)
class Process(QObject): def __init__(self, engine): QObject.__init__(self) self.m_engine = engine self.project_name = '' self.process_name = list() self.process_description = list() self.action_name = list() self.settings = QSettings("Nebula") self.dat = dict() @Slot(str, str, result='QString') def process_settings(self, project_name, action_name): self.project_name = project_name self.action_name.append(action_name) self.settings = QSettings("Nebula", self.project_name) self.settings.beginGroup("ProcessInfo") self.settings.beginWriteArray(action_name) s = [str(i) for i in self.dat[project_name]] res = ",".join(s) for i in range(len(self.process_name)): self.settings.setArrayIndex(i) self.settings.setValue("Name", self.process_name[i]) self.settings.setValue("Description", self.process_description[i]) self.settings.setValue("Value", res) self.settings.endArray() self.settings.endGroup() print(self.settings.fileName()) return self.settings.fileName() def process_info(self, name="", description=""): self.process_name.append(name) self.process_description.append(description) @Slot(str, result=bool) def generate_pdf(self, project_name): self.settings = QSettings("Nebula", project_name) pdf = CustomPDF() pdf.alias_nb_pages() pdf.add_page() pdf.set_font('Times', 'B', 15) pdf.cell(0, 5, 'Project details', ln=1) pdf.set_font('Times', '', 10) self.settings.beginGroup("Project") pdf.cell(0, 10, txt="Name: {}".format(self.settings.value("Name")), ln=1) pdf.cell(0, 10, txt="Project creation date: {}".format( self.settings.value("Date")), ln=1) pdf.cell(0, 10, txt="Inform creation date: {}".format( self.settings.value("LastEdit")), ln=1) pdf.cell(0, 10, txt=" ", ln=1) self.settings.endGroup() pdf.set_font('Times', 'B', 15) pdf.cell(0, 5, 'Signals', ln=1) pdf.set_font('Times', '', 10) self.settings.beginGroup("SignalFiles") pdf.cell(0, 10, txt="Signals path: {}".format(self.settings.value("Path")), ln=1) path = self.settings.value("Path") pdf.cell(0, 10, txt=" ", ln=1) self.settings.endGroup() pdf.set_font('Times', 'B', 15) pdf.cell(0, 5, 'Information', ln=1) pdf.set_font('Times', '', 10) self.settings.beginGroup("Info") size = self.settings.beginReadArray("sfreq") for i in range(size): self.settings.setArrayIndex(i) pdf.cell(0, 10, txt="{}. sfreq: {}".format(i, self.settings.value("sfreq")), ln=1) self.settings.endArray() size = self.settings.beginReadArray("SubjectInfo") for i in range(size): self.settings.setArrayIndex(i) pdf.cell(0, 10, txt="{}. id: {}".format(i, self.settings.value("id")), ln=1) pdf.cell(0, 10, txt="{}. First name: {}".format( i, self.settings.value("first_name")), ln=1) pdf.cell(0, 10, txt="{}. Last name: {}".format( i, self.settings.value("last_name")), ln=1) pdf.cell(0, 10, txt="{}. Hand: {}".format(i, self.settings.value("hand")), ln=1) pdf.cell(0, 10, txt="{}. Sex: {}".format(i, self.settings.value("sex")), ln=1) self.settings.endArray() self.settings.endGroup() pdf.cell(0, 10, txt=" ", ln=1) pdf.set_font('Times', 'B', 15) pdf.cell(0, 5, 'Process', ln=1) pdf.set_font('Times', '', 10) self.settings.beginGroup("ProcessInfo") for action_name in self.action_name: size = self.settings.beginReadArray(action_name) for i in range(size): self.settings.setArrayIndex(i) pdf.cell(0, 10, txt="{}. Name: {}".format( i, self.settings.value("Name")), ln=1) pdf.cell(0, 10, txt="{}. Description: {}".format( i, self.settings.value("Description")), ln=1) pdf.cell(0, 10, txt="{}. Value: {}".format( i, self.settings.value("Value")), ln=1) self.settings.endArray() self.settings.endGroup() pdf.cell(0, 10, txt=" ", ln=1) print(path) pdf.output('{}_report.pdf'.format(project_name)) print(f'PDF: {project_name}') return True @Slot(QObject, result="QVariantList") def add_chart_bar(self, chart_view): # https://stackoverflow.com/questions/57536401/how-to-add-qml-scatterseries-to-existing-qml-defined-chartview context = QQmlContext(self.m_engine.rootContext()) context.setContextProperty("chart_view", chart_view) # context.setContextProperty("type", SeriesType.SeriesTypeBar.value) context.setContextProperty("type", SeriesType.SeriesTypePie.value) script = """chart_view.createSeries(type, "Pie series");""" expression = QQmlExpression(context, chart_view, script) serie = expression.evaluate()[0] if expression.hasError(): print(expression.error()) return print(serie, expression) data = self.dat[self.project_name] print(f'FROM ADD PIE CHART {data} and type {type(data)}') serie.append("a", 10.0) serie.append("b", 80.0) # serie.append("a", data[0] * 1000) # serie.append("b", data[1] * 1000) # serie.append("c", data[2] * 1000) # serie.append("b", data) return data @Slot(QtCharts.QAbstractSeries) def fill_serie(self, serie): import random mx, Mx = 0, 10 my, My = -100, 100 data = self.dat[self.project_name] serie.append(1, data[0] * 1000) serie.append(2, data[1] * 1000) serie.append(3, data[2] * 1000) # for _ in range(100): # x = random.uniform(mx, Mx) # y = random.uniform(my, My) # serie.append(x, y) # https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#borderColor-prop serie.setProperty("borderColor", QtGui.QColor("salmon")) # https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#brush-prop serie.setProperty("brush", QtGui.QBrush(QtGui.QColor("green"))) # https://doc.qt.io/qt-5/qml-qtcharts-scatterseries.html#borderColor-prop serie.setProperty("borderWidth", 4.0) # Process Example @Slot(str, result="QVariantList") def my_process_1(self, project_name): import statistics self.process_info("process_1", "std, variance and mean from channel 0") data = get_data_path(project_name) print(data, data[0]) data = get_data(data[0]) print(data.shape) var = statistics.variance(data[0, :]) mean = statistics.mean(data[0, :]) std = statistics.stdev(data[0, :]) value = [std * 1000, float(var) * 1000, float(mean) * 1000] self.dat[project_name] = value print(value) return value
def test_exercises_exist(self): settings_reader = QSettings() settings_reader.beginReadArray('exercises') assert len(settings_reader.allKeys()) > 1 settings_reader.endArray()
class SettingsDialog(QtWidgets.QDialog): validator = QRegExpValidator(QRegExp("[A-Za-z0-9_]{6,16}")) def __init__(self, parent=None): super(SettingsDialog, self).__init__(parent) self.settings = QSettings( QStandardPaths.writableLocation(QStandardPaths.ConfigLocation) + "/settings.ini", QSettings.IniFormat) self.usernames = set() self.engines = set() self.newUsername = "" self.newEnginePath = "" self._loadSettings() self.mainLayout = QtWidgets.QFormLayout() self.usernameLineEdit = QtWidgets.QLineEdit(self.newUsername) self.usernameLineEdit.setPlaceholderText("Username (a-zA-Z0-9_)") self.usernameLineEdit.setMinimumHeight(35) self.usernameLineEdit.setValidator(self.validator) self.usernameLineEdit.textChanged.connect(self.validateFields) self.usernameLineEdit.selectAll() self.usernameLineEdit.setCompleter( QtWidgets.QCompleter(list(self.usernames), self)) self.engineEdit = _EngineEdit(self.newEnginePath) self.engineEdit.pathEdit.textChanged.connect(self.validateFields) self.engineEdit.pathEdit.selectAll() self.engineEdit.pathEdit.setCompleter( QtWidgets.QCompleter(list(self.engines), self)) self.resetButton = QtWidgets.QPushButton("Reset") self.resetButton.clicked.connect(self._reset) buttonBox = QtWidgets.QDialogButtonBox() self.okButton = buttonBox.addButton( "Ok", QtWidgets.QDialogButtonBox.AcceptRole) self.cancelButton = buttonBox.addButton( "Cancel", QtWidgets.QDialogButtonBox.RejectRole) self.okButton.clicked.connect(self._ok) self.cancelButton.clicked.connect(self.reject) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setAlignment(Qt.AlignBottom) self.mainLayout.addRow("Username", self.usernameLineEdit) self.mainLayout.addRow("Engine", self.engineEdit) self.mainLayout.addWidget(self.resetButton) self.mainLayout.addWidget(buttonBox) self.setLayout(self.mainLayout) self.validateFields() def _validateEnginePath(self, path): return QFile.exists(path) and QFileInfo(path).isFile() @Slot(str) def validateFields(self): usernameValid = self.validator.validate(self.usernameLineEdit.text(), 0) and \ 6 <= len(self.usernameLineEdit.text()) <= 16 pathValid = self._validateEnginePath(self.engineEdit.pathEdit.text()) if not usernameValid or not pathValid: if self.okButton.isEnabled(): self.okButton.setDisabled(True) else: if not self.okButton.isEnabled(): self.okButton.setDisabled(False) if not usernameValid: self.usernameLineEdit.setStyleSheet("border: 1px solid red;") else: self.usernameLineEdit.setStyleSheet("border: 1px solid green;") if not pathValid: self.engineEdit.pathEdit.setStyleSheet("border: 1px solid red;") else: self.engineEdit.pathEdit.setStyleSheet("border: 1px solid green;") def _loadSettings(self): i = self.settings.beginReadArray("usernames") currentUsername = "" for j in range(i): self.settings.setArrayIndex(j) currentUsername = self.settings.value("usernames", "") self.usernames.add(currentUsername) self.newUsername = currentUsername self.settings.endArray() i = self.settings.beginReadArray("engines") currentEngine = "" for j in range(i): self.settings.setArrayIndex(j) currentEngine = self.settings.value("engines", "") self.engines.add(currentEngine) self.newEnginePath = currentEngine self.settings.endArray() @Slot() def _reset(self): status = QtWidgets.QMessageBox.warning( self, "Reset", "Are you sure to reset the settings?", buttons=QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok, defaultButton=QtWidgets.QMessageBox.Ok) if status == QtWidgets.QMessageBox.Ok: self.usernameLineEdit.clear() self.engineEdit.pathEdit.clear() self.usernames.clear() self.engines.clear() self.usernameLineEdit.setCompleter(QtWidgets.QCompleter()) self.engineEdit.pathEdit.setCompleter(QtWidgets.QCompleter()) self.settings.clear() self._loadSettings() @Slot() def _ok(self): if self.validator.validate(self.usernameLineEdit.text(), 0): self.newUsername = self.usernameLineEdit.text() else: QtWidgets.QMessageBox.critical(self, "Error", "Invalid username") self.reject() if self._validateEnginePath(self.engineEdit.pathEdit.text()): self.newEnginePath = self.engineEdit.pathEdit.text() else: QtWidgets.QMessageBox.critical( self, "Error", "The specified engine's path does not exist.") self.reject() self.settings.beginWriteArray("usernames") self.usernames.add(self.newUsername) for i, username in enumerate(self.usernames): self.settings.setArrayIndex(i) self.settings.setValue("usernames", username) self.settings.endArray() self.settings.beginWriteArray("engines") self.engines.add(self.newEnginePath) for i, path in enumerate(self.engines): self.settings.setArrayIndex(i) self.settings.setValue("engines", path) self.settings.endArray() self.accept()
class Settings: def __init__(self): # Standard constructor stuff super().__init__() self.settings = QSettings() if not pygame.get_init(): pygame.init() # Instance variables self.exercises = [] self.instrument = int(self.settings.value('instrument')) # Load exercises from stored settings self.settings.beginReadArray('exercises') for ex in self.settings.allKeys(): if ex == 'size': continue self.exercises.append([ex, self.settings.value(ex)]) self.settings.endArray() def remove_exercise(self, exercise_name): # Replace self.exercises with a copy without the selected exercise new_exercises = [] for ex in self.exercises: if ex[0] != exercise_name: new_exercises.append(ex) self.exercises = new_exercises def reload_exercise(self, exercise_name, exercise_text): # Load all exercise names exercise_names = [] for ex in self.exercises: exercise_names.append(ex[0]) new_exercises = [] # If the reloaded exercise is existing then update it in memory, # otherwise just add it exercise_contents = exercise_text.split() if exercise_name in exercise_names: for ex in self.exercises: if ex[0] == exercise_name: new_exercises.append([ex[0], exercise_contents]) else: new_exercises.append([ex[0], ex[1]]) self.exercises = new_exercises else: self.exercises.append([exercise_name, exercise_contents]) def set_instrument(self, instrument_id): self.instrument = instrument_id def save_settings(self): self.settings.beginWriteArray('exercises') for key in self.settings.allKeys(): self.settings.remove(key) if key != 'size' else None for ex in self.exercises: self.settings.setValue(ex[0], ex[1]) self.settings.endArray() self.settings.setValue('instrument', self.instrument) self.settings.sync() def preview(self): pygame.mixer.music.stop() midi_file = tempfile.NamedTemporaryFile(delete=False) mid = MidiFile() track = MidiTrack() mid.tracks.append(track) instrument = self.instrument track.append(Message('program_change', program=instrument, time=0)) note = 60 track.append(Message('note_on', note=note, velocity=100, time=0)) track.append(Message('note_off', note=note, time=2000)) mid.save(file=midi_file) midi_file.flush() midi_file.close() pygame.mixer.music.load(midi_file.name) pygame.mixer.music.play() if 'WARMUPPY_KEEP_MIDI' in os.environ: copyfile(midi_file.name, os.environ['WARMUPPY_KEEP_MIDI']) os.remove(midi_file.name)
class Warmuppy: def __init__(self): super().__init__() pygame.init() # Instance variables self.settings = QSettings() self.timers = [] self.exercises = [] self.notes = NOTES # Load settings self.bpm = int(self.settings.value('bpm', DEFAULT_BPM)) self.cut = float(self.settings.value('cut', DEFAULT_CUT)) self.step = int(self.settings.value('step', DEFAULT_STEP)) prev = self.settings.value('preview', DEFAULT_PREVIEW) self.preview = prev in ['true', True] prolonged = self.settings.value('prolong', DEFAULT_PROLONG) self.prolong = prolonged in ['true', True] self.preview_time = int( self.settings.value('preview_time', DEFAULT_PREVIEW_TIME) ) self.prolong_time = int( self.settings.value('prolong_time', DEFAULT_PROLONG_TIME) ) self.note = int(self.settings.value('note', 0)) self.octave = int(self.settings.value('octave', 3)) self.instrument = int( self.settings.value('instrument', DEFAULT_INSTRUMENT) ) self.settings.beginReadArray('exercises') for ex in self.settings.allKeys(): if ex == 'size': continue exv = self.settings.value(ex) if isinstance(exv,str): exv = [exv] self.exercises.append([ ex, exv ]) self.settings.endArray() if not self.exercises: self.settings.beginWriteArray('exercises') for ex in DEFAULT_EXERCISES: self.exercises.append(ex) self.settings.setValue(ex[0], ex[1]) self.settings.endArray() self.exercise = DEFAULT_EXERCISE self.settings.setValue('instrument', self.instrument) def change_note(self, note): note_id = self.notes.index(note) logging.debug(f"New note is {note} ({note_id})") self.note = note_id self.settings.setValue('note', note_id) def change_octave(self, octave): logging.debug(f"New octave is {octave}") self.octave = octave self.settings.setValue('octave', octave) def change_exercise(self, i): if not self.exercises: logging.debug("No exercise to change to, skipping") return try: ex = self.exercises[i] self.exercise = i except IndexError: ex = self.exercises[0] self.exercise = 0 logging.debug(f"Selected exercise {ex[0]} ({ex[1]})") # Generate boilerplate setters for x in "bpm cut step preview_time prolong_time".split(): exec(f'''def change_{x}(self,{x}): logging.debug("New {x} : %s",{x}) self.settings.setValue('{x}',{x}) self.{x} = {x}''') def change_preview(self, p): logging.debug(f"Setting preview as {p}") self.preview = p self.settings.setValue('preview', p) def change_prolong(self, p): logging.debug(f"Setting prolong as {p}") self.prolong = p self.settings.setValue('prolong', p) # Go {step} semitones up or down def bump_note(self, up): # Beginning of 3rd octave + selected octave * semitones + note base_note = 24 + (self.octave - 1) * 12 + self.note logging.debug(f"Current note: {base_note}") # Highest playable note is the last of the 8th octave max_note = 8 * 12 + 24 # Lowest is C3 min_note = 25 # Compute new note if up: if base_note < max_note: base_note += self.step else: if base_note > min_note: base_note -= self.step logging.debug(f"New note: {base_note}") # Compute new octave octave = (base_note - 24) // 12 + 1 logging.debug(f"New octave should be #{octave}") # Compute relative note for the UI note_id = (base_note - 24) % 12 note = self.notes[note_id] logging.debug(f"New note should be #{note}") return octave, note # Generate a midi with the exercise and then play it def play_exercise(self): self.stop() # Load selected exercise ex = self.exercises[self.exercise] name = ex[0] seq = ex[1] logging.debug(f"Starting exercise '{name}' (pattern: {seq})") # Init midi file midi_file = tempfile.NamedTemporaryFile(delete=False) mid = MidiFile() track = MidiTrack() mid.tracks.append(track) track.append( Message('program_change', program=self.instrument, time=0) ) # Compute starting note: octave * semitones + relative note base_note = 24 + (self.octave - 1) * 12 + self.note # Seconds per beat seconds = 60 / self.bpm # Note duration is one beat minus the cut, in milliseconds base_duration = (seconds - self.cut) * 1000 # Prepend the base note to the midi if the preview is selected timer_delay = 0 if self.preview: timer_delay = int(base_duration*self.preview_time) track.append( Message('note_on', note=base_note, velocity=100, time=0) ) track.append( Message('note_off', note=base_note, time=timer_delay) ) timer_data = [] # Add the rest of the notes for idx, p in enumerate(seq): # Normalise the note step as a string item = str(p) # Extract the step step = int(sub(r'[^0-9]','',item)) # If the number has dashes or dots, add half a beat or a quarter beat for each, respectively duration = base_duration * (1 + item.count('-')*0.5 + item.count('.')*0.25) # Calculate percentage of current step current_index = idx + 1 percent = (current_index / len(seq)) * 100 # If this is the last note and the user wants to, prolong it if current_index == len(seq) and self.prolong: logging.debug(f"prolonging {step}") delay = int(base_duration*self.prolong_time) else: delay = int(duration) # Append the note to the midi track.append( Message('note_on', note=(base_note+step), velocity=100, time=0) ) track.append( Message('note_off', note=(base_note+step), time=delay) ) timer_data.append([percent, timer_delay]) timer_delay += duration # Save midi file and load it with pygame separately, # to avoid race conditions mid.save(file=midi_file) midi_file.flush() midi_file.close() pygame.mixer.music.load(midi_file.name) pygame.mixer.music.play() # Cleanup if 'WARMUPPY_KEEP_MIDI' in os.environ: copyfile(midi_file.name, os.environ['WARMUPPY_KEEP_MIDI']) os.remove(midi_file.name) return timer_data def stop(self): # Stop the music pygame.mixer.music.stop() # Settings window told us that something changed, so reload everything def reload_settings(self): logging.debug("Settings saved, reloading") self.instrument = int( self.settings.value('instrument', DEFAULT_INSTRUMENT) ) self.exercises = [] self.settings.beginReadArray('exercises') for ex in self.settings.allKeys(): if ex == 'size': continue self.exercises.append([ ex, self.settings.value(ex) ]) self.settings.endArray()