def build_ui(self): self.ui = Ui_MainWindow() self.ui.setupUi(self) # MIDI CHAIN GUI Widgets self.midi_chain_container = MidiChainContainer(self.model, self, self.main_ctrl) # Input Source "Set the input options for the input" input_options = self.model.get_input_options() for item in input_options: self.ui.midi_source_combo_box.addItem(item) # Output output_options = self.model.get_output_options() for item in output_options: self.ui.output_combo_box.addItem(item) "tempoView" self.tempo_view = TempoView(self.model, self.main_ctrl) self.ui.tempo_layout.addWidget(self.tempo_view) # KEYBOARD GUI self.keyboard = KeyBoard(self.model, self.main_ctrl, self) self.ui.keyboard_total_layout.addWidget(self.keyboard) "Metronome view" self.metronome_view = MetronomeWidget(self.model, self.main_ctrl, self) self.ui.metronome_layout.addWidget(self.metronome_view) "Input File Widget" self.ui.midi_source_settings_button.clicked.connect(self.open_input_settings_popup) "Output File Widget" self.ui.output_settings_button.clicked.connect(self.open_output_settings_popup) self.ui.record_file_button.setEnabled(False) self.ui.record_file_button.clicked.connect(self.record_save_midi_file) # Set additional properties for self.ui widgets self.ui.midi_chain_scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.ui.midi_chain_scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.ui.midi_chain_scroll_area.setWidgetResizable(True) self.ui.midi_chain_scroll_area.setWidget(self.midi_chain_container) # layout.addStretch() # Connect the signals to the required methods "File Menu Signals" self.ui.load_scales_action.triggered.connect(self.open_scale_file) self.ui.exit_action.triggered.connect(self.close) self.ui.scale_type_combo_box.currentIndexChanged.connect(self.scale_type_selected) self.ui.scale_root_combo_box.currentIndexChanged.connect(self.root_note_selected) "Configuration Menu Signals" self.ui.Undo_action.triggered.connect(self.undo_action) "Input Source Signals" self.ui.midi_source_combo_box.currentIndexChanged.connect(self.input_selected) "Output Source Signals" self.ui.output_combo_box.currentIndexChanged.connect(self.output_selected) "Midi Chain Signals" # Add block button self.ui.add_block_button.clicked.connect(self.midi_chain_container.add_block_to_chain) "MIDI INPUT FILE" self.ui.play_midi_file_button.setEnabled(False) self.ui.open_midi_file_button.clicked.connect(self.open_midi_file) self.ui.play_midi_file_button.clicked.connect(self.play_midi_file) "CONFIGURATION FILES" self.ui.new_config_button.clicked.connect(self.new_configuration) self.ui.save_config_button.clicked.connect(self.save_configuration) self.ui.load_config_button.clicked.connect(self.load_configuration)
class MainView(QtGui.QMainWindow): def __init__(self, model, main_ctrl): super(MainView, self).__init__() self.model = model self.main_ctrl = main_ctrl self._scroll_container_widget = None self.build_ui() def build_ui(self): self.ui = Ui_MainWindow() self.ui.setupUi(self) # MIDI CHAIN GUI Widgets self.midi_chain_container = MidiChainContainer(self.model, self, self.main_ctrl) # Input Source "Set the input options for the input" input_options = self.model.get_input_options() for item in input_options: self.ui.midi_source_combo_box.addItem(item) # Output output_options = self.model.get_output_options() for item in output_options: self.ui.output_combo_box.addItem(item) "tempoView" self.tempo_view = TempoView(self.model, self.main_ctrl) self.ui.tempo_layout.addWidget(self.tempo_view) # KEYBOARD GUI self.keyboard = KeyBoard(self.model, self.main_ctrl, self) self.ui.keyboard_total_layout.addWidget(self.keyboard) "Metronome view" self.metronome_view = MetronomeWidget(self.model, self.main_ctrl, self) self.ui.metronome_layout.addWidget(self.metronome_view) "Input File Widget" self.ui.midi_source_settings_button.clicked.connect(self.open_input_settings_popup) "Output File Widget" self.ui.output_settings_button.clicked.connect(self.open_output_settings_popup) self.ui.record_file_button.setEnabled(False) self.ui.record_file_button.clicked.connect(self.record_save_midi_file) # Set additional properties for self.ui widgets self.ui.midi_chain_scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.ui.midi_chain_scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.ui.midi_chain_scroll_area.setWidgetResizable(True) self.ui.midi_chain_scroll_area.setWidget(self.midi_chain_container) # layout.addStretch() # Connect the signals to the required methods "File Menu Signals" self.ui.load_scales_action.triggered.connect(self.open_scale_file) self.ui.exit_action.triggered.connect(self.close) self.ui.scale_type_combo_box.currentIndexChanged.connect(self.scale_type_selected) self.ui.scale_root_combo_box.currentIndexChanged.connect(self.root_note_selected) "Configuration Menu Signals" self.ui.Undo_action.triggered.connect(self.undo_action) "Input Source Signals" self.ui.midi_source_combo_box.currentIndexChanged.connect(self.input_selected) "Output Source Signals" self.ui.output_combo_box.currentIndexChanged.connect(self.output_selected) "Midi Chain Signals" # Add block button self.ui.add_block_button.clicked.connect(self.midi_chain_container.add_block_to_chain) "MIDI INPUT FILE" self.ui.play_midi_file_button.setEnabled(False) self.ui.open_midi_file_button.clicked.connect(self.open_midi_file) self.ui.play_midi_file_button.clicked.connect(self.play_midi_file) "CONFIGURATION FILES" self.ui.new_config_button.clicked.connect(self.new_configuration) self.ui.save_config_button.clicked.connect(self.save_configuration) self.ui.load_config_button.clicked.connect(self.load_configuration) # File Menu Methods def open_scale_file(self): file_path_q_string = QtGui.QFileDialog.getOpenFileName(self, "Open Scales File") file_path = str(file_path_q_string[0]) self.main_ctrl.read_in_scale_file(file_path) # if successful if len(self.model.get_scale_types()) > 0: "Scale types list has things loaded into it. There successful" self.add_scale_types_to_scale_type_combo_box() # Scale Combo Box Methods def add_scale_types_to_scale_type_combo_box(self): for scale_type in self.model.get_scale_types(): self.ui.scale_type_combo_box.addItem(scale_type) def add_root_notes_to_scale_root_combo_box(self): self.ui.scale_root_combo_box.clear() for root_note in self.model.get_root_notes(): self.ui.scale_root_combo_box.addItem(root_note) def scale_type_selected(self): self.model.select_scale_type(self.ui.scale_type_combo_box.currentText()) if len(self.model.get_root_notes()) > 0: "Root notes have been loaded" self.add_root_notes_to_scale_root_combo_box() def root_note_selected(self): self.model.select_root_note(self.ui.scale_root_combo_box.currentText()) # Need to change the keyboard keys self.keyboard.update_keyboard_key_text() self.keyboard.update_keyboard_key_palette() "If the output is to a USB Device, then send the new button config" if self.model.get_selected_output() == "USB Device": if self.main_ctrl.output_ctrl.serial is not None: self.main_ctrl.output_ctrl.output_button_configuration() # INPUT SOURCE FUNCTIONS def input_selected(self): if self.model.get_seletected_scale_flag() == False: if self.ui.midi_source_combo_box.currentIndex() != 0: "Create a QMessage Box" QtGui.QMessageBox.about( self, "Choose A Scale", "Please Load and Choose a Scale.\nTo load scales from a file: File - load scales.", ) self.ui.midi_source_combo_box.setCurrentIndex(0) elif self.model.get_selected_output_flag() == False: if self.ui.midi_source_combo_box.currentIndex() != 0: self.ui.midi_source_combo_box.setCurrentIndex(0) "Create a QMessage Box" QtGui.QMessageBox.about(self, "Choose An Ouput", "Please Choose an Output") elif ( self.model.get_selected_output() == "Output File" and self.model.get_output_file_paths_set_satus() == False ): if self.ui.midi_source_combo_box.currentIndex() != 0: self.ui.midi_source_combo_box.setCurrentIndex(0) "Create a QMessage Box" QtGui.QMessageBox.about(self, "Choose Output File Settings", "Please set the ouput file settings") else: index = self.ui.midi_source_combo_box.currentIndex() "Add to the undo actions list" "Action Tuple = ('Set Input Source', index)" self.model.add_to_undo_actions(("Set Input Source", self.model.get_most_recent_input_action_index())) self.model.add_to_input_action_index(index) self.model.set_selected_input(self.ui.midi_source_combo_box.currentText()) self.keyboard.update_keyboard_availability() "keyboard view updated in update_keyboard_availability" "Update the availabilit of the record file button" self.update_record_file_button_status() "Update the midi source input" self.main_ctrl.midi_source_ctrl.update_receive_input() "If the metronome is on, turn it off" self.main_ctrl.tempo_thread_ctrl.stopTimer() def undo_input_selected(self, index): "set the combox box to the previous index" self.ui.midi_source_combo_box.setCurrentIndex(index) self.model.set_selected_input(self.ui.midi_source_combo_box.currentText()) self.keyboard.update_keyboard_availability() # OUTPUT FUNCTIONS def output_selected(self): if self.model.get_seletected_scale_flag() == False: if self.ui.output_combo_box.currentIndex() != 0: "Create a QMessage Box" QtGui.QMessageBox.about( self, "Choose A Scale", "Please Load and Choose a Scale.\nTo load scales from a file: File - load scales.", ) self.ui.output_combo_box.setCurrentIndex(0) self.model.set_selected_output(self.ui.output_combo_box.currentText()) self.main_ctrl.output_ctrl.select_output() "Update the availabilit of the record file button" self.update_record_file_button_status() "If the metronome is on, turn it off" self.main_ctrl.tempo_thread_ctrl.stopTimer() "Undo Actions functions" def undo_action(self): "Get the most recent action" if self.model.get_undo_actions_length() > 0: "Have an action to undo" action_types = self.model.get_action_types() action = self.model.get_most_recent_action() if action[0] == action_types[0]: "Action Tuple = ('Add Block')" "Add Block action occured" "get the last index" index = self.model.get_block_widget_list_length() - 1 "Remove this block at that index" self.midi_chain_container.delete_block_at_index(index) self.model.remove_most_recent_action() elif action[0] == action_types[1]: "Action Tuple = ('Remove Block', index, block, proc_block)" "Remove Block action occured" self.midi_chain_container.add_block_at_index(action[1], action[2], action[3]) self.model.remove_most_recent_action() elif action[0] == action_types[2]: "Action Tuple = ('Clear Chain', model.block_widget_list, model._midi_block_chain)" "Clear Chain action occured" block_widget_list = action[1] midi_block_chain = action[2] for i in range(len(block_widget_list)): self.midi_chain_container.add_block_at_index(i, block_widget_list[i], midi_block_chain[i]) self.model.remove_most_recent_action() elif action[0] == action_types[3]: "Action Tuple = ('Change Block Type', index, block, proc_block)" "Change Block Type action occured" self.midi_chain_container.undo_change_block_type_at_index(action[1], action[2], action[3]) self.model.remove_most_recent_action() elif action[0] == action_types[4]: "Action Tuple = ('Change Pitch Shift Parameter', index, proc_block, value)" "Change Block Parameter action occured" self.main_ctrl.midi_chain_ctrl.remove_block_from_chain(action[1]) action[2].set_shift(action[3]) self.main_ctrl.midi_chain_ctrl.add_block_to_chain_at_index(action[1], action[2]) self.model.remove_most_recent_action() elif action[0] == action_types[5]: "Action Tuple = ('Change Arpeggiator Parameter', index, proc_block, pattern_index)" "Change Block Parameter action occured" self.main_ctrl.midi_chain_ctrl.remove_block_from_chain(action[1]) action[2].set_selected_pattern_index(action[3]) self.main_ctrl.midi_chain_ctrl.add_block_to_chain_at_index(action[1], action[2]) self.model.remove_most_recent_action() elif action[0] == action_types[6]: "Action Tuple = ('Move Block Left', index)" "Moved the block left" self.midi_chain_container.undo_move_block_at_index_left(action[1]) self.model.remove_most_recent_action() elif action[0] == action_types[7]: "Action Tuple = ('Move Block Right', index)" "Moved the block right" self.midi_chain_container.undo_move_block_at_index_right(action[1]) self.model.remove_most_recent_action() elif action[0] == action_types[8]: "Action Tuple = ('Set Input Source', index)" "Changed the input source" self.undo_input_selected(action[1]) self.model.remove_most_recent_action() self.model.remove_most_recent_action() else: print "Unable to undo action" "MIDI FILE INPUT FUNCTIONS" def open_midi_file(self): file_path_q_string = QtGui.QFileDialog.getOpenFileName(self, "Open Midi File") file_path = str(file_path_q_string[0]) self.main_ctrl.play_midi_file_ctrl.open_midi_file(file_path) "Check to see if midi file was opened" if self.model.get_midi_file_opened() == True: "Enable the play button" self.ui.play_midi_file_button.setEnabled(True) else: self.ui.play_midi_file_button.setEnabled(False) def play_midi_file(self): self.main_ctrl.play_midi_file_ctrl.play_midi_file() "Check to see if the file is playing" if self.model.get_midi_file_playing() == True: self.ui.play_midi_file_button.setText("Stop") else: self.ui.play_midi_file_button.setText("Play") "OUTPUT FUNCTIONS" def open_output_settings_popup(self): "Check to see if an output has been chosen" if self.model.get_selected_output_flag() == True: self.output_settings_popup_view = OutputSettingsPopupView(self.model, self.main_ctrl, self) self.output_settings_popup_view.show() def update_record_file_button_status(self): """ For the record_file_button to be enabled, the output needs to be 'Output File' and there needs to be a file_path and the input needs to be the virtual piano """ if ( self.model.get_selected_output() == "Output File" and self.model.get_output_file_paths_set_satus() == True and (self.model.get_selected_input() == "Virtual Piano" or self.model.get_selected_input() == "Midi Source") ): self.ui.record_file_button.setEnabled(True) else: self.ui.record_file_button.setEnabled(False) "Record button function" def record_save_midi_file(self): if self.model.get_record_output_file_commands() == False: self.main_ctrl.output_ctrl.record_save_midi_file() self.ui.record_file_button.setText("Save") else: self.main_ctrl.output_ctrl.record_save_midi_file() self.ui.record_file_button.setText("Record") "INPUT POPUP FUNCTIONS" def open_input_settings_popup(self): "Check to see if an input has been chosen" if self.model.get_selected_input() != "": self.input_settings_popup_view = InputSettingsPopupView(self.model, self.main_ctrl, self) self.input_settings_popup_view.show() "CONFIGURATION FUNCTIONS" def new_configuration(self): "To create a new configuration, clear the chosen input and the midi chain" "1. Clear the chosen input, the midi chain and the undo list" "Clear the input" self.ui.midi_source_combo_box.setCurrentIndex(0) self.keyboard.update_keyboard_availability() "keyboard view updated in update_keyboard_availability" "Update the availabilit of the record file button" self.update_record_file_button_status() "Clear the midi chain" self.midi_chain_container.clear_chain() "clear the undo list" self.model.set_undo_actions([]) "Reset the metronome and the timer" self.main_ctrl.tempo_thread_ctrl.stopTimer() self.model.set_tempo_timer_started(False) "reset the metronome beats" self.model.set_tempo_beat_number(0) def save_configuration(self): "Get the location to save the configuration file to." config_file_path_string = QtGui.QFileDialog.getSaveFileName(self, "Save Configuration", os.getcwd(), "*.csv") if config_file_path_string: config_file_path = str(config_file_path_string[0]) self.save_config_file(config_file_path) def save_config_file(self, file_path): with open(file_path, "wb") as csvfile: config_file_writer = csv.writer(csvfile, delimiter=",") "Save the input first" input_config_list = self.main_ctrl.config_file_input_list() config_file_writer.writerow(input_config_list) "Save the midi chain in order" "Get the length of the midi block chain" for i in range(self.model.get_midi_chain_length()): block_config_list = self.main_ctrl.config_file_midi_block_list_at_index(i) config_file_writer.writerow(block_config_list) def load_configuration(self): "Load a configuration file" "1. Get the filepath from the user" if self.model.get_seletected_scale_flag() == False: if self.ui.midi_source_combo_box.currentIndex() != 0: "Create a QMessage Box" QtGui.QMessageBox.about( self, "Choose A Scale", "Please Load and Choose a Scale.\nTo load scales from a file: File - load scales.", ) self.ui.midi_source_combo_box.setCurrentIndex(0) elif self.model.get_selected_output_flag() == False: if self.ui.midi_source_combo_box.currentIndex() != 0: self.ui.midi_source_combo_box.setCurrentIndex(0) "Create a QMessage Box" QtGui.QMessageBox.about(self, "Choose An Ouput", "Please Choose an Output") else: file_path_string = QtGui.QFileDialog.getOpenFileName(self, "Open Configuration File") if file_path_string: file_path = str(file_path_string[0]) "Check that the file is a valid configuration" if self.validate_config_file(file_path) == False: QtGui.QMessageBox.about(self, "Invalid Configuration", "Invalid Configuration File") else: "Valid configuration file" "Clear the previous configuration" "Clear the input" self.ui.midi_source_combo_box.setCurrentIndex(0) "Clear the midi chain" self.midi_chain_container.clear_chain() "clear the undo list" self.model.set_undo_actions([]) "Load the new configuration" "Load the input" self.load_config_file(file_path) self.keyboard.update_keyboard_availability() "keyboard view updated in update_keyboard_availability" "Update the availabilit of the record file button" self.update_record_file_button_status() "Reset the metronome and the timer" self.main_ctrl.tempo_thread_ctrl.stopTimer() self.model.set_tempo_timer_started(False) "reset the metronome beats" self.model.set_tempo_beat_number(0) def load_config_file(self, file_path): config_file_reader = csv.reader(open(file_path, "rb")) row_num = 0 for row in config_file_reader: if row[0] == "Input": "This is the input configuration" "Set the in_port" self.model.set_selected_in_port(row[2]) "Get the index of the required input" input_options = self.model.get_input_options() input_index = input_options.index(row[1]) self.ui.midi_source_combo_box.setCurrentIndex(input_index) else: "The row should be for a processing block" block_types = self.model.get_block_types() if row[0] == block_types[0]: "Pitch Shift Block" "add a Pitch Shift processing block" pitch_shift_type_index = block_types.index(row[0]) self.midi_chain_container.add_config_block_to_chain(pitch_shift_type_index) "Set the shift value" shift_value = int(row[1]) pitch_shift_block = self.model.get_block_at_midi_chain_index(row_num) pitch_shift_block.set_shift(shift_value) elif row[0] == block_types[1]: "Arpeggiator" "add a block widget and" arpeggiator_type_index = block_types.index(row[0]) self.midi_chain_container.add_config_block_to_chain(arpeggiator_type_index) "set the pattern index" pattern_index = int(row[1]) arpeggiator_block = self.model.get_block_at_midi_chain_index(row_num) arpeggiator_block.set_selected_pattern_index(pattern_index) elif row[0] == block_types[2]: "Monophonic" monophonic_type_index = block_types.index(row[0]) self.midi_chain_container.add_config_block_to_chain(monophonic_type_index) elif row[0] == block_types[3]: "Chordify" chordify_type_index = block_types.index(row[0]) self.midi_chain_container.add_config_block_to_chain(chordify_type_index) row_num += 1 def validate_config_file(self, file_path): """ Checks the file path and the file """ "1. Check that it is a .csv file" if file_path[-4:] != ".csv": return False else: "The file path is to a .csv file" config_file_reader = csv.reader(open(file_path, "rb")) for row in config_file_reader: if row[0] == "Input": "This is the input configuration" input_options = self.model.get_input_options() if row[1] not in input_options: "not a valid input option" return False else: "The row should be for a processing block" block_types = self.model.get_block_types() if row[0] == block_types[0]: "Pitch Shift Block" if row[1].isdigit() == False: return False elif row[0] == block_types[1]: "Arpeggiator" valid_patterns = ["Ascending", "Descending", "Ping Pong", "Random"] pattern_index = int(row[1]) if pattern_index < 0 or pattern_index > (len(valid_patterns) - 1): return False elif row[0] == block_types[2]: "Monophonic" continue elif row[0] == block_types[3]: "Chordify" continue else: "Don't recognise this block type" return False return True