class OrderItemsDialog(QDialog, Ui_OrderItemsDialog): """ This class is a dialog for changing the order of qualitative parameter's values The dialog lists the values and allow changing the order using Up and Down buttons which replace the selected value with the one before/after it in the list """ def __init__(self, parent, listToOrder: list): """ Initialize the change order dialog Args: parent : (QDialog) - The parent dialog listToOrder : (list) - The list to set the order for """ QDialog.__init__(self, parent) self.setupUi(self) self.listToOrder = listToOrder self.model = QStringListModel(self) self.model.setStringList(listToOrder) self.listView_items.setModel(self.model) self.pushButton_up.clicked.connect(self.pushButton_up_clicked) self.pushButton_down.clicked.connect(self.pushButton_down_clicked) def pushButton_up_clicked(self): """ Replace the selected item with the one before it """ selectedIndex = self.listView_items.currentIndex().row() self.replaceItems(selectedIndex, selectedIndex - 1) def pushButton_down_clicked(self): """ Replace the selected item with the one after it """ selectedIndex = self.listView_items.currentIndex().row() self.replaceItems(selectedIndex, selectedIndex + 1) def replaceItems(self, fromIndex, toIndex): """ Replace 2 items Args: fromIndex : (int) - the index of the selected item toIndex : (int) - the target index of the selected item """ if toIndex in range(len(self.listToOrder)): temp = self.listToOrder[toIndex] self.listToOrder[toIndex] = self.listToOrder[fromIndex] self.listToOrder[fromIndex] = temp self.model.setStringList(self.listToOrder) self.model.dataChanged.emit( self.listView_items.indexAt(QPoint(0, 0)), self.listView_items.indexAt(QPoint(len(self.listToOrder), 0))) index = self.model.createIndex(toIndex, 0) self.listView_items.setCurrentIndex(index)
class SleaZynth(QMainWindow): autoSaveFile = 'auto.save' texsize = 512 samplerate = 44100 def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.show() self.initModelView() self.initSignals() self.initState() self.autoLoad() self.initAMaySyn() self.initAudio() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.close() elif event.key() == Qt.Key_F1: self.debugOutput() if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_S: self.autoSave() elif event.key() == Qt.Key_L: self.autoLoad() elif event.key() == Qt.Key_T: self.renderWhateverWasLast() def closeEvent(self, event): QApplication.quit() def initSignals(self): self.ui.btnChooseFilename.clicked.connect(self.loadAndImportMayson) self.ui.btnImport.clicked.connect(self.importMayson) self.ui.btnExport.clicked.connect(self.exportChangedMayson) self.ui.checkAutoReimport.clicked.connect(self.toggleAutoReimport) self.ui.checkAutoRender.clicked.connect(self.toggleAutoRender) self.ui.editFilename.editingFinished.connect( partial(self.updateStateFromUI, only='maysonFile')) self.ui.editBPM.editingFinished.connect( partial(self.updateStateFromUI, only='BPM')) self.ui.spinBOffset.valueChanged.connect( partial(self.updateStateFromUI, only='B_offset')) self.ui.spinBStop.valueChanged.connect( partial(self.updateStateFromUI, only='B_stop')) self.ui.spinLevelSyn.valueChanged.connect( partial(self.updateStateFromUI, only='level_syn')) self.ui.spinLevelDrum.valueChanged.connect( partial(self.updateStateFromUI, only='level_drum')) self.ui.checkWriteWAV.clicked.connect( partial(self.updateStateFromUI, only='writeWAV')) self.ui.spinTimeShift.valueChanged.connect( partial(self.updateStateFromUI, only='extraTimeShift')) self.ui.editTrackName.textChanged.connect(self.trackSetName) self.ui.spinTrackVolume.valueChanged.connect(self.trackSetVolume) self.ui.checkTrackMute.stateChanged.connect(self.trackSetMute) self.ui.spinModOn.valueChanged.connect(self.moduleSetModOn) self.ui.spinModTranspose.valueChanged.connect(self.moduleSetTranspose) self.ui.btnApplyPattern.clicked.connect(self.moduleSetPattern) self.ui.btnApplyNote.clicked.connect(self.noteApplyChanges) self.ui.btnTrackClone.clicked.connect(self.trackClone) self.ui.btnTrackDelete.clicked.connect(self.trackDelete) self.ui.btnRandomSynth.clicked.connect(self.trackSetRandomSynth) self.ui.btnRandomizeSynth.clicked.connect(self.synthRandomize) self.ui.btnSaveSynth.clicked.connect(self.synthHardClone) self.ui.btnApplySynthName.clicked.connect(self.synthChangeName) self.ui.btnReloadSyn.clicked.connect(self.loadSynthsFromSynFile) self.ui.btnRenderModule.clicked.connect(self.renderModule) self.ui.btnRenderTrack.clicked.connect(self.renderTrack) self.ui.btnRenderSong.clicked.connect(self.renderSong) self.ui.btnStopPlayback.clicked.connect(self.stopPlayback) # model/view signals self.ui.trackList.selectionModel().currentChanged.connect( self.trackLoad) self.ui.patternCombox.currentIndexChanged.connect(self.patternLoad) self.ui.moduleList.selectionModel().currentChanged.connect( self.moduleLoad) self.ui.synthList.selectionModel().currentChanged.connect( self.trackSetSynth) self.ui.noteList.selectionModel().currentChanged.connect(self.noteLoad) self.ui.drumList.selectionModel().currentChanged.connect( self.noteSetDrum) def initModelView(self): self.trackModel = TrackModel() self.ui.trackList.setModel(self.trackModel) self.moduleModel = ModuleModel() self.ui.moduleList.setModel(self.moduleModel) self.patternModel = PatternModel() self.ui.patternCombox.setModel(self.patternModel) self.noteModel = NoteModel() self.ui.noteList.setModel(self.noteModel) self.synthModel = QStringListModel() self.ui.synthList.setModel(self.synthModel) self.drumModel = QStringListModel() self.ui.drumList.setModel(self.drumModel) self.noteModel.dataChanged.connect( self.updateModulesWithChangedPattern) self.noteModel.reloadNoteParameters.connect(self.noteLoad) def initState(self): self.state = { 'maysonFile': '', 'autoReimport': False, 'autoRender': False, 'lastRendered': '', 'writeWAV': False, 'selectedTrack': 0, 'selectedModule': 0, 'extraTimeShift': 0, } self.info = {} self.patterns = [] self.synths = [] self.drumkit = [] self.amaysyn = None self.fileObserver = None def loadAndImportMayson(self): name, _ = QFileDialog.getOpenFileName( self, 'Load MAYSON file', '', 'aMaySyn export *.mayson(*.mayson)') if name == '': return self.state['maysonFile'] = name self.state['title'], self.state[ 'synFile'] = self.getTitleAndSynFromMayson(name) self.autoSave() self.importMayson() def importMayson(self): maysonData = {} try: file = open(self.state['maysonFile'], 'r') maysonData = json.load(file) except FileNotFoundError: print( f"{self.state['maysonFile']} could not be imported. make sure that it exists, or choose another one." ) self.loadAndImportMayson() except json.decoder.JSONDecodeError: print( f"{self.state['maysonFile']} is changing right now, pause for 1 sec..." ) sleep(1) self.importMayson() finally: file.close() if maysonData == {}: return self.info = maysonData['info'] self.info.update({'title': self.state['title']}) if self.amaysyn is not None: self.amaysyn.updateState(info=self.info) self.trackModel.setTracks(maysonData['tracks']) self.patternModel.setPatterns(maysonData['patterns']) self.synthModel.setStringList(maysonData['synths']) self.drumModel.setStringList(maysonData['drumkit']) self.trackModel.layoutChanged.emit() if self.state['selectedTrack'] >= self.trackModel.rowCount(): self.state['selectedTrack'] = 0 self.selectIndex(self.ui.trackList, self.trackModel, self.state['selectedTrack']) if self.state['selectedModule'] >= self.moduleModel.rowCount(): self.state['selectedModule'] = 0 self.selectIndex(self.ui.moduleList, self.moduleModel, self.state['selectedModule']) self.noteModel.layoutChanged.emit() if self.noteModel.rowCount() > 0: self.selectIndex(self.ui.noteList, self.noteModel, 0) self.synthModel.layoutChanged.emit() self.drumModel.layoutChanged.emit() if self.drumIndex().isValid(): self.selectIndex(self.ui.drumList, self.drumModel, self.drumIndex().row()) self.applyStateToUI() def exportChangedMayson(self): name, _ = QFileDialog.getSaveFileName( self, 'Export with Changes', self.state['maysonFile'], 'aMaySyn export *.mayson(*.mayson)') if name == '': return data = { 'info': self.info, 'tracks': self.trackModel.tracks, 'patterns': self.patternModel.patterns, 'synths': self.synthModel.stringList(), 'drumkit': self.drumModel.stringList(), } file = open(name, 'w') json.dump(data, file) file.close() def updateStateFromUI(self, only=None): if only is None or only == 'maysonFile': self.state.update({'maysonFile': self.ui.editFilename.text()}) title, synFile = self.getTitleAndSynFromMayson( self.state['maysonFile']) self.state.update({'synFile': synFile}) self.state.update({'title': title}) self.info['title'] = title if only is None or only == 'BPM': self.info['BPM'] = self.ui.editBPM.text() if only is None or only == 'B_offset': self.info['B_offset'] = self.ui.spinBOffset.value() if only is None or only == 'B_stop': self.info['B_stop'] = self.ui.spinBStop.value() if only is None or only == 'level_syn': self.info['level_syn'] == self.ui.spinLevelSyn.value() if only is None or only == 'level_drum': self.info['level_drum'] == self.ui.spinLevelDrum.value() if only is None or only == 'writeWAV': self.state['writeWAV'] = self.ui.checkWriteWAV.isChecked() if only is None or only == 'extraTimeShift': self.state['extraTimeShift'] = self.ui.spinTimeShift.value() if self.amaysyn is not None: self.amaysyn.updateState(info=self.info, synFile=synFile) def applyStateToUI(self): self.ui.editFilename.setText(self.state['maysonFile']) # TODO: think about - do I want self.state['override']['BPM'] etc.?? self.ui.editBPM.setText(self.info['BPM']) self.ui.spinBOffset.setValue(self.info['B_offset']) self.ui.spinBStop.setValue(self.info['B_stop']) self.ui.spinLevelSyn.setValue(self.info['level_syn']) self.ui.spinLevelDrum.setValue(self.info['level_drum']) self.ui.checkAutoReimport.setChecked(self.state['autoReimport']) self.ui.checkAutoRender.setChecked(self.state['autoRender']) self.ui.checkWriteWAV.setChecked(self.state['writeWAV']) self.ui.spinTimeShift.setValue(self.state['extraTimeShift']) def autoSave(self): file = open(self.autoSaveFile, 'w') json.dump(self.state, file) file.close() def autoLoad(self): loadState = {} try: file = open(self.autoSaveFile, 'r') loadState = json.load(file) file.close() except FileNotFoundError: pass for key in loadState: self.state[key] = loadState[key] if 'autoReimport' in self.state: self.toggleAutoReimport(self.state['autoReimport']) if 'maysonFile' not in self.state or self.state['maysonFile'] == '': self.loadAndImportMayson() else: self.importMayson() def toggleAutoRender(self, checked): self.state['autoRender'] = checked self.autoSave() def toggleAutoReimport(self, checked): self.state['autoReimport'] = checked self.autoSave() if self.fileObserver is not None: self.fileObserver.stop() self.fileObserver.join() self.fileObserver = None if checked: file = self.state['maysonFile'] eventHandler = FileModifiedHandler(file) eventHandler.fileChanged.connect( self.importAndRender if self.state['autoRender'] else self. importMayson) self.fileObserver = Observer() self.fileObserver.schedule(eventHandler, path=path.dirname(file), recursive=False) self.fileObserver.start() def importAndRender(self): self.importMayson() if self.amaysyn is None: print( "You want to Reimport&Render, but why is aMaySyn not initialized? do some rendering first!" ) return self.renderWhateverWasLast() #################################### GENERAL HELPERS ########################################### def selectIndex(self, list, model, index): list.selectionModel().setCurrentIndex( model.createIndex(index, 0), QItemSelectionModel.SelectCurrent) def patternIndexOfName(self, name): patternNames = [p['name'] for p in self.patternModel.patterns] if name in patternNames: return patternNames.index(name) else: return None def getTitleAndSynFromMayson(self, maysonFile): synFile = '.'.join(maysonFile.split('.')[:-1]) + '.syn' title = '.'.join(path.basename(maysonFile).split('.')[:-1]) return title, synFile def placeholder(self): print("FUNCTION NOT IMPLEMENTED. Sorrriiiiiiieee! (not sorry.)") #################################### TRACK FUNCTIONALITY ####################################### def track(self): return self.trackModel.tracks[self.trackIndex().row( )] if self.trackModel.rowCount() > 0 else None def trackIndex(self): return self.ui.trackList.currentIndex() def trackModelChanged(self): self.trackModel.dataChanged.emit(self.trackIndex(), self.trackIndex()) def trackLoad(self, currentIndex): cTrack = self.trackModel.tracks[currentIndex.row()] self.ui.editTrackName.setText(cTrack['name']) self.ui.spinTrackVolume.setValue(100 * cTrack['par_norm']) self.ui.checkTrackMute.setChecked(not cTrack['mute']) self.moduleModel.setModules(cTrack['modules']) if len(cTrack['modules']) > 0: self.selectIndex(self.ui.moduleList, self.moduleModel, cTrack['current_module']) self.moduleLoad() self.selectIndex(self.ui.synthList, self.synthModel, cTrack['current_synth']) self.state['selectedTrack'] = currentIndex.row() def trackClone(self): self.trackModel.cloneRow(self.trackIndex().row()) def trackDelete(self): self.trackModel.removeRow(self.trackIndex().row()) def trackSetName(self, name): self.track()['name'] = name self.trackModelChanged() def trackSetVolume(self, value): self.track()['par_norm'] = round(value * .01, 3) self.trackModelChanged() def trackSetMute(self, state): self.track()['mute'] = (state != Qt.Checked) self.trackModelChanged() def trackSetSynth(self, index): self.track()['current_synth'] = self.synthModel.stringList().index( self.synthModel.data(index, Qt.DisplayRole)) self.ui.editSynthName.setText(self.synthName()) self.trackModelChanged() if self.synth()[0] == 'D': self.noteModel.useDrumkit(self.drumModel.stringList()) else: self.noteModel.useDrumkit(None) def trackSetRandomSynth(self): randomIndex = self.synthModel.createIndex( randint(0, len(self.instrumentSynths()) - 1), 0) self.ui.synthList.setCurrentIndex(randomIndex) #################################### MODULE FUNCTIONALITY ###################################### def module(self): return self.moduleModel.modules[self.moduleIndex().row( )] if self.moduleModel.rowCount() > 0 else None def moduleIndex(self): return self.ui.moduleList.currentIndex() def moduleModelChanged(self): self.moduleModel.dataChanged.emit(self.moduleIndex(), self.moduleIndex()) def moduleLoad(self, currentIndex=None): if currentIndex is None: cModule = self.module() else: cModule = self.moduleModel.modules[currentIndex.row()] self.state['selectedModule'] = currentIndex.row() self.ui.patternCombox.setCurrentIndex( self.patternIndexOfName(cModule['pattern']['name'])) self.ui.spinModOn.setValue(cModule['mod_on']) self.ui.spinModTranspose.setValue(cModule['transpose']) def moduleAssignPattern(self, pattern): self.module()['pattern'] = pattern # deepcopy(pattern) def moduleSetPattern(self): self.moduleAssignPattern(self.pattern()) self.moduleModelChanged() def moduleSetModOn(self, value): self.module()['mod_on'] = self.ui.spinModOn.value() self.moduleModelChanged() def moduleSetTranspose(self, value): self.module()['transpose'] = self.ui.spinModTranspose.value() self.moduleModelChanged() #################################### PATTERN FUNCTIONALITY ##################################### def pattern(self): return self.patternModel.patterns[ self.patternIndex()] if self.patternModel.rowCount() > 0 else None def patternIndex(self): return self.ui.patternCombox.currentIndex() def patternLoad(self, currentIndex): cPattern = self.patternModel.patterns[currentIndex] self.noteModel.setNotes(cPattern['notes']) def updateModulesWithChangedPattern(self, rowBegin, rowEnd): self.moduleAssignPattern(self.pattern()) self.moduleModelChanged() self.trackModel.updateModulesWithChangedPattern(self.pattern()) self.trackModelChanged() #################################### NOTE FUNCTIONALITY ######################################## def note(self): return self.noteModel.notes[ self.noteIndex().row()] if self.noteModel.rowCount() > 0 else None def noteIndex(self): return self.ui.noteList.currentIndex() def noteModelChanged(self): self.noteModel.dataChanged.emit(self.noteIndex(), self.noteIndex()) def noteLoad(self, currentIndex): self.ui.editNote.setText( self.noteModel.data(currentIndex, Qt.DisplayRole)) self.ui.editNote.setCursorPosition(0) def noteApplyChanges(self): self.noteModel.changeByString(self.noteIndex(), self.ui.editNote.text()) def noteSetDrum(self, currentIndex): if self.drum() is not None: self.noteModel.changeDrumTo(self.noteIndex(), self.drum()) ################################ SYNTH / DRUM FUNCTIONALITY #################################### def synth(self): return self.synthModel.data(self.ui.synthList.currentIndex(), Qt.DisplayRole) def synthName(self): return self.synth()[2:] def instrumentSynths(self): return [ I_synth for I_synth in self.synthModel.stringList() if I_synth[0] == 'I' ] def drum(self): if not self.drumIndex(): print("LOLOLOL DRUM INDEX IS NONE (should never happen)") if not self.drumIndex().isValid(): print("LOLOLOL DRUM INDEX NOT VALID") return self.drumModel.data(self.drumIndex(), Qt.DisplayRole) def drumIndex(self): return self.ui.drumList.currentIndex() def synthRandomize(self): self.amaysyn.aMaySynatize(reshuffle_randoms=True) def synthHardClone(self): if self.synth()[0] == 'D': self.synthHardCloneDrum(self) return else: count = 0 oldID = self.synthName() synths = self.instrumentSynths() while True: formID = oldID + '.' + str(count) print("TRYING", formID, synths) if 'I_' + formID not in synths: break count += 1 try: formTemplate = next( form for form in self.amaysyn.last_synatized_forms if form['id'] == oldID) formType = formTemplate['type'] formMode = formTemplate['mode'] formBody = ' '.join(key + '=' + formTemplate[key] for key in formTemplate if key not in ['type', 'id', 'mode']) if formMode: formBody += ' mode=' + ','.join(formMode) except StopIteration: print( "Current synth is not compiled yet. Do so and try again.") return except: print("could not CLONE HARD:", formID, formTemplate) raise else: with open(self.state['synFile'], mode='a') as filehandle: filehandle.write('\n' + formType + 4 * ' ' + formID + 4 * ' ' + formBody) self.loadSynthsFromSynFile() def synthHardDrum(self): print("NOT IMPLEMENTED YET") return count = 0 oldID = self.synthName() synths = self.instrumentSynths() while True: formID = oldID + '.' + str(count) print("TRYING", formID, synths) if 'I_' + formID not in synths: break count += 1 try: formTemplate = next(form for form in self.amaysyn.last_synatized_forms if form['id'] == oldID) formType = formTemplate['type'] formMode = formTemplate['mode'] formBody = ' '.join(key + '=' + formTemplate[key] for key in formTemplate if key not in ['type', 'id', 'mode']) if formMode: formBody += ' mode=' + ','.join(formMode) except StopIteration: print("Current synth is not compiled yet. Do so and try again.") return except: print("could not CLONE HARD:", formID, formTemplate) raise else: with open(self.state['synFile'], mode='a') as filehandle: filehandle.write('\n' + formType + 4 * ' ' + formID + 4 * ' ' + formBody) self.loadSynthsFromSynFile() def synthChangeName(self): if self.synth()[0] != 'I': print("Nah. Select an instrument synth (I_blabloo)") return newID = self.ui.editSynthName.text() if newID == '': return formID = self.synthName() tmpFile = self.state['synFile'] + '.tmp' move(self.state['synFile'], tmpFile) with open(tmpFile, mode='r') as tmp_handle: with open(self.state['synFile'], mode='w') as new_handle: for line in tmp_handle.readlines(): lineparse = line.split() if len(lineparse) > 2 and lineparse[0] in [ 'main', 'maindrum' ] and lineparse[1] == formID: new_handle.write( line.replace(' ' + formID + ' ', ' ' + newID + ' ')) else: new_handle.write(line) self.loadSynthsFromSynFile() def loadSynthsFromSynFile(self): self.amaysyn.aMaySynatize() self.synthModel.setStringList(self.amaysyn.synths) self.synthModel.dataChanged.emit( self.synthModel.createIndex(0, 0), self.synthModel.createIndex(self.synthModel.rowCount(), 0)) self.drumModel.setStringList(self.amaysyn.drumkit) self.drumModel.dataChanged.emit( self.drumModel.createIndex(0, 0), self.drumModel.createIndex(self.drumModel.rowCount(), 0)) self.trackModel.setSynthList(self.amaysyn.synths) self.trackModelChanged() # TODO: function to change drumkit order / assignment? ######################################## SleaZYNTHesizer ####################################### def initAMaySyn(self): self.amaysyn = aMaySynBuilder(self, self.state['synFile'], self.info) def initAudio(self): self.audioformat = QAudioFormat() self.audioformat.setSampleRate(self.samplerate) self.audioformat.setChannelCount(2) self.audioformat.setSampleSize(32) self.audioformat.setCodec('audio/pcm') self.audioformat.setByteOrder(QAudioFormat.LittleEndian) self.audioformat.setSampleType(QAudioFormat.Float) self.audiooutput = QAudioOutput(self.audioformat) self.audiooutput.setVolume(1.0) def stopPlayback(self): self.audiooutput.stop() def renderWhateverWasLast(self): if self.state['lastRendered'] == 'module': self.renderModule() elif self.state['lastRendered'] == 'track': self.renderTrack() else: self.renderSong() def renderModule(self): print(self.track(), self.module()) self.state['lastRendered'] = 'module' restoreMute = self.track()['mute'] self.track()['mute'] = False modInfo = deepcopy(self.info) modInfo['B_offset'] = self.module()['mod_on'] modInfo['B_stop'] = self.module()['mod_on'] + self.module( )['pattern']['length'] self.amaysyn.info = modInfo self.amaysyn.extra_time_shift = self.state['extraTimeShift'] shader = self.amaysyn.build(tracks=[self.track()], patterns=[self.module()['pattern']]) self.amaysyn.info = self.info self.track()['mute'] = restoreMute self.executeShader(shader) def renderTrack(self): self.state['lastRendered'] = 'track' restoreMute = self.track()['mute'] self.track()['mute'] = False self.amaysyn.extra_time_shift = self.state['extraTimeShift'] shader = self.amaysyn.build(tracks=[self.track()], patterns=self.patternModel.patterns) self.track()['mute'] = restoreMute self.executeShader(shader) def renderSong(self): self.state['lastRendered'] = 'song' self.amaysyn.extra_time_shift = self.state['extraTimeShift'] shader = self.amaysyn.build(tracks=self.trackModel.tracks, patterns=self.patternModel.patterns) self.executeShader(shader) def executeShader(self, shader): self.ui.codeEditor.clear() self.ui.codeEditor.insertPlainText( shader.replace(4 * ' ', '\t').replace(3 * ' ', '\t')) self.ui.codeEditor.ensureCursorVisible() sequenceLength = len( self.amaysyn.sequence) if self.amaysyn.sequence is not None else 0 if not self.amaysyn.useSequenceTexture and sequenceLength > pow(2, 14): QMessageBox.critical( self, "I CAN'T", f"Either switch to using the Sequence Texture (ask QM), or reduce the sequence size by limiting the offset/stop positions or muting tracks.\nCurrent sequence length is:\n{sequenceLength} > {pow(2,14)}" ) return self.bytearray = self.amaysyn.executeShader( shader, self.samplerate, self.texsize, renderWAV=self.state['writeWAV']) self.audiobuffer = QBuffer(self.bytearray) self.audiobuffer.open(QIODevice.ReadOnly) self.audiooutput.stop() self.audiooutput.start(self.audiobuffer) ###################################### DEBUG STUFF ############################################# def debugOutput(self): print("TRACKS:", self.trackModel.rowCount()) print("===== TRACK ACCUMULATION =====") track_accumulate = 0 for t in self.trackModel.tracks: delta = len(t['modules']) print( f"{t['name']:>20} {track_accumulate:>10} {track_accumulate + delta:>10}" ) track_accumulate += delta print("END AT", track_accumulate) print() print("PATTERNS:", self.patternModel.rowCount()) print("===== PATTERN ACCUMULATION =====") pattern_accumulate = 0 for p in self.patternModel.patterns: delta = len(p['notes']) print( f"{p['name']:>20} {pattern_accumulate:>10} {pattern_accumulate + delta:>10}" ) pattern_accumulate += delta print("END AT", pattern_accumulate) print()
def init(self, config): self.config = config redmineData = self.config.getRedminesData() redmineProjects = [] for (i, item) in enumerate(redmineData): redmineWorker = RedmineWorker(item[0]) redmineWorker.initWithKey(item[2]) projects = redmineWorker.getProjects() for project in projects: redmineProjects.append([project.id, project.name]) projectsModel = RedmineProjectsModel(redmineProjects) self.redmineProjectsView.setModel(projectsModel) evolutionData = self.config.getEvolutionData() evoWorker = EvoWorker(evolutionData[0], evolutionData[2]) evoProjects = evoWorker.getProjects() if evoProjects != False: self.evoProjectNames = [] self.evoProjectNamesToId = {} self.evoProjectIdToName = {} for project in evoProjects: self.evoProjectNames.append(project['title']) self.evoProjectNamesToId[project['title']] = project['id'] self.evoProjectIdToName[project['id']] = project['title'] completer = QCompleter() self.evoProject.setCompleter(completer) model = QStringListModel() completer.setModel(model) model.setStringList(self.evoProjectNames) else: msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Information) msgBox.setText("При получении данных из evo произошла ошибка") msgBox.exec_() self.selectedIndex = 0 self.redmineProjectsView.selectionModel().selectionChanged.connect(self.changeProject) self.saveRedmineEvoProject.clicked.connect(self.saveProject) Qindex = projectsModel.createIndex(0, 0) self.redmineProjectsView.selectionModel().select(Qindex, QItemSelectionModel.Select) self.hamsterWorker = HamsterWorker() self.hamsterProjects = self.hamsterWorker.getProjects() hamsterProjectsModel = QStringListModel() hamsterProjectsModel.setStringList(self.hamsterProjects) self.hamsterProjectView.setModel(hamsterProjectsModel) Qindex = hamsterProjectsModel.createIndex(0, 0) self.hamsterProjectView.selectionModel().select(Qindex, QItemSelectionModel.Select) redminesData = self.config.getRedminesData() for (i, item) in enumerate(redminesData): self.redmineCombo.addItem(item[0], i) self.hamsterProjectView.selectionModel().selectionChanged.connect(self.changeHamsterRedmine) self.saveHumsterProject.clicked.connect(self.saveHamsterRedmine) self.selectedIndexHamster = 0 self.changeHamsterRedmine()