Lab3_Gui code
Dependencies: mbed
mbed_beat_new.py
- Committer:
- dogcatfee
- Date:
- 2017-09-21
- Revision:
- 1:faa8aa177069
File content as of revision 1:faa8aa177069:
#*****************************************************************# # # # File: beatfactorygui.py # # Author: Ethan Takla # # Date Created: 10/6/14 # # Description: A python GUI for graphically creating songs on # # the FDRM-KL46z # # # #*****************************************************************# # Last edit: Matthew Sessions #System Imports import sys #Serial Imports import serial from serial.tools import list_ports #Signal Imports import signal #Sound Imports # Uncomment for sound #import winsound #Threading imports import thread #PyQt Imports from PyQt4.QtGui import QApplication, QDialog, QMainWindow, QFileDialog from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s #Setup text encoding try: _encoding = QtGui.QApplication.UnicodeUTF8 def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig) #Create Note Classs class Note: def __init__( self, note ): self.note = note #Main Window Class class Ui_MainWindow(QtGui.QMainWindow): def initializeUI(self, MainWindow): #Initialize main window properties MainWindow.setObjectName(_fromUtf8("FDRM-KL46z Beat Factory")) MainWindow.resize(1200, 848) MainWindow.setMaximumSize(QtCore.QSize(16777215, 848)) #Create central widget self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) #Create gridLayout_2 self.gridLayout_2 = QtGui.QGridLayout(self.centralwidget) self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) #Create and format a graphics view widget self.graphicsView = QtGui.QGraphicsView(self.centralwidget) self.graphicsView.setMinimumSize(QtCore.QSize(300, 120)) self.graphicsView.setMaximumSize(QtCore.QSize(300, 120)) self.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) #Set the graphics view image self.scene = QtGui.QGraphicsScene(); self.pixmap = QtGui.QPixmap( "TekBots.png" ) self.scene.addPixmap(self.pixmap) self.graphicsView.setScene( self.scene ) self.graphicsView.setObjectName(_fromUtf8("graphicsView")) #Add the graphics view to gridLayout_2 self.gridLayout_2.addWidget(self.graphicsView, 0, 1, 1, 1) #Create and format verticalLayout_2 self.verticalLayout_2 = QtGui.QVBoxLayout() self.verticalLayout_2.setSpacing(7) self.verticalLayout_2.setSizeConstraint(QtGui.QLayout.SetNoConstraint) self.verticalLayout_2.setContentsMargins(-1, -1, -1, 0) self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2")) #Create a table view for beat editing self.tableView = QtGui.QTableWidget(self.centralwidget) #Add the table view to verticalLayout2 self.verticalLayout_2.addWidget(self.tableView) #Add verticalLayout2 to gridLayout2 self.gridLayout_2.addLayout(self.verticalLayout_2, 0, 0, 7, 1) #Create gridLayout self.gridLayout = QtGui.QGridLayout() self.gridLayout.setObjectName(_fromUtf8("gridLayout")) #Create and format pushButton, add them to gridLayout self.pushButton = QtGui.QPushButton(self.centralwidget) self.pushButton.setObjectName(_fromUtf8("pushButton")) self.pushButton.setStyleSheet("color: black; background-color: red;") self.gridLayout.addWidget(self.pushButton, 0, 2, 1, 1) self.pushButton_2 = QtGui.QPushButton(self.centralwidget) self.pushButton_2.setObjectName(_fromUtf8("pushButton_2")) self.pushButton_2.setEnabled( False ) self.gridLayout.addWidget(self.pushButton_2, 3, 0, 1, 1) self.pushButton_3 = QtGui.QPushButton(self.centralwidget) self.pushButton_3.setObjectName(_fromUtf8("pushButton_3")) self.pushButton_3.setEnabled( False ) self.gridLayout.addWidget(self.pushButton_3, 3, 1, 1, 1) #Create labels and add them to gridLayout self.label = QtGui.QLabel(self.centralwidget) self.label.setObjectName(_fromUtf8("label")) self.gridLayout.addWidget(self.label, 0, 0, 1, 1) self.label2 = QtGui.QLabel(self.centralwidget) self.label2.setObjectName(_fromUtf8("label2")) self.gridLayout.addWidget(self.label2, 1, 0, 1, 1) self.label3 = QtGui.QLabel(self.centralwidget) self.label3.setObjectName(_fromUtf8("label3")) self.gridLayout.addWidget(self.label3, 2, 0, 1, 1) #Create comboBoxs and add them to gridLayout self.comboBox = QtGui.QComboBox(self.centralwidget) self.comboBox.setObjectName(_fromUtf8("comboBox")) self.gridLayout.addWidget(self.comboBox, 0, 1, 1, 1) self.comboBox2 = QtGui.QComboBox(self.centralwidget) self.comboBox2.setObjectName(_fromUtf8("comboBox2")) self.gridLayout.addWidget(self.comboBox2, 1, 1, 1, 1) self.comboBox3 = QtGui.QComboBox(self.centralwidget) self.comboBox3.setObjectName(_fromUtf8("comboBox3")) self.gridLayout.addWidget(self.comboBox3, 2, 1, 1, 1) #Add gridLayout to gridLayout_2 and format self.gridLayout_2.addLayout(self.gridLayout, 1, 1, 1, 1) MainWindow.setCentralWidget(self.centralwidget) #Create and configure the menu bar self.menubar = QtGui.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 972, 26)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) self.menuHelp = QtGui.QMenu(self.menubar) self.menuHelp.setObjectName(_fromUtf8("menuHelp")) MainWindow.setMenuBar(self.menubar) self.statusbar = QtGui.QStatusBar(MainWindow) self.statusbar.setObjectName(_fromUtf8("statusbar")) MainWindow.setStatusBar(self.statusbar) #Create a QAction for the Load and Save songs, and About button self.actionLoad_Song = QtGui.QAction(MainWindow) self.actionLoad_Song.setObjectName(_fromUtf8("actionLoad_Song")) self.actionSave_Song = QtGui.QAction(MainWindow) self.actionSave_Song.setObjectName(_fromUtf8("actionSave_Song")) self.actionAbout = QtGui.QAction(MainWindow) self.actionAbout.setObjectName(_fromUtf8("actionAbout")) self.menuFile.addAction(self.actionLoad_Song) self.menuFile.addAction(self.actionSave_Song) self.menuHelp.addAction(self.actionAbout) self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) #Set text of all UI elements self.retranslateUI(MainWindow) #Connect Slots QtCore.QMetaObject.connectSlotsByName(MainWindow) #Populate comboBox3 and configure tempo self.comboBox3.clear() self.comboBox3.addItems(self.tempoValues) self.comboBox3.setCurrentIndex( 14 ) self.tempo = 120 #Populate Combobox 2 self.comboBox2.clear() self.comboBox2.addItems(self.noteNumberValues) def retranslateUI(self, MainWindow): #Set the text of the UI elements MainWindow.setWindowTitle(_translate("FDRM-KL46z Beat Factory", "FDRM-KL46z Beat Factory", None)) self.pushButton.setText(_translate("FDRM-KL46z Beat Factory", "Scan", None)) self.pushButton_2.setText(_translate("FDRM-KL46z Beat Factory", "Play", None)) self.pushButton_3.setText(_translate("FDRM-KL46z Beat Factory", "Clear", None)) self.label.setText(_translate("FDRM-KL46z Beat Factory", "COM Port:", None)) self.label2.setText(_translate("FDRM-KL46z Beat Factory", "Number of Notes:", None)) self.label3.setText(_translate("FDRM-KL46z Beat Factory", "Tempo:", None)) self.menuFile.setTitle(_translate("FDRM-KL46z Beat Factory", "File", None)) self.menuHelp.setTitle(_translate("FDRM-KL46z Beat Factory", "Help", None)) self.actionLoad_Song.setText(_translate("FDRM-KL46z Beat Factory", "Load Song", None)) self.actionSave_Song.setText(_translate("FDRM-KL46z Beat Factory", "Save Song", None)) self.actionAbout.setText(_translate("FDRM-KL46z Beat Factory", "About", None)) def initializeVariables( self ): #Define cell selected/unselected variables self.cellSelected = 1 self.cellUnselected = 0 #Set initial comm. state self.comButtonState = "Scan" #Create the song array self.song_array = [] # delete all note elements #Set table row and column count variables self.tableViewRowCount = 24 self.tableViewColumnCount = 16 self.selectedCells = [[0 for x in xrange(self.tableViewColumnCount)] for x in xrange(self.tableViewRowCount)] self.noteFrequencies = [ 831, 784, 740, 698, 659, 622, 587, 554, 523, 494, 466, 440, 415, 392, 370, 349, 330, 311, 294, 277, 261, 247, 233, 220 ] #Define tempo values self.tempoValues = [ "50", "55", "60", "65", "70", "75", "80", "85", "90", "95", "100", "105", "110", "115", "120", "125", "130", "135", "140", "145", "150", "155", "160", "165", "170", "175", "180", "185", "190", "195", "200", "205", "210", "215", "220", "225", "230", "235", "240"] #Define note number values self.noteNumberValues = [ "16", "32", "48", "64" ] #Define serial ports that are to be 'detected' self.ports = [ "Select", 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'COM10', 'COM11', 'COM12', 'COM13', 'COM14', 'COM15', 'COM16', 'COM17', 'COM18', 'COM19', 'COM20', 'COM21', '/dev/ttyACM0', '/dev/ttyACM1', '/dev/ttyACM2', '/dev/ttyACM3'] def initializeTable( self ): #Initialize beat table columns and rows self.tableView.setColumnCount(self.tableViewColumnCount) self.tableView.setRowCount(self.tableViewRowCount) #Initialize beat table rows for notes self.verticalHeaderLabels = [ "G#", "G", "F#", "F", "E", "D#", "D", "C#", "C(High)", "B", "A#", "A", "G#", "G", "F#", "F", "E", "D#", "D", "C#", "C (Middle)", "B", "A#", "A" ] #Label vertical headers on table self.tableView.setVerticalHeaderLabels( self.verticalHeaderLabels ) #Make table ready only by creating items widgets in each cell and disabling them for i in xrange(self.tableView.rowCount()): for j in xrange(self.tableView.columnCount()): self.tableView.setItem(i,j, QtGui.QTableWidgetItem()) self.tableView.item(i,j).setFlags(QtCore.Qt.ItemIsEnabled) def initializeSerial( self ): #Configure the serial port for 8-N-1, baudrate doesn't matter since the FDRM-KL46z is running a VCP self.ser = serial.Serial() self.ser.timeout = 1000 self.ser.baudrate = 9600 def highlightColumn( self, column ): #Go through the unselected cells in each row and highlight them for i in xrange(self.tableView.rowCount()): if self.selectedCells[i][column] == self.cellUnselected: self.tableView.item(i, column).setBackground(QtGui.QColor(255,179,0)) def unhighlightColumn( self, column ): #Go through the unselected cells in each row and unhighlight them for i in xrange(self.tableView.rowCount()): if self.selectedCells[i][column] == self.cellUnselected: self.tableView.item(i, column).setBackground(QtGui.QColor(255,255,255)) def handlePlayButton( self ): #Disable play and clear buttons while song is playing self.pushButton_2.setEnabled( False ) self.pushButton_3.setEnabled( False ) #Empty song array del self.song_array[:] emptyCells = 0 for j in xrange(self.tableView.columnCount()): for i in xrange(self.tableView.rowCount()): #If a cell is selected, add the corresponding note to the song array if self.selectedCells[i][j] == self.cellSelected: #Store the note index note_name = i # note name is letter A-G # create Note class and populate note = Note( note_name ) self.song_array.append( note ) # add Note to array of Notes else: emptyCells += 1 #If an empty column is found, add a rest (index 24) to the song array if emptyCells == self.tableView.rowCount(): # store user-inputted note into a Note class note_name = 24 # note name is letter A-G # create Note class and populate note = Note( note_name ) self.song_array.append( note ) # add Note to array of Notes emptyCells = 0 try: self.ser.open() except: self.serialScan() return self.ser.write( "$NEW\n" ) #Send new song packet self.ser.write( "$T" ) #Send Tempo Packet self.ser.write( "%d" % (self.tempo*4) ) self.ser.write( '\n' ) self.ser.write( "$L" ) #Send song length packet self.ser.write( "%d" % (self.tableView.columnCount() ) ) self.ser.write( '\n' ) for i in range(self.tableView.columnCount()): self.ser.write( "$S" ) #Send note data packet self.ser.write( "%d" % (self.song_array[i].note) ) self.ser.write( '\n' ) self.ser.write( "$PLAY\n" ) #Send play song packet data = 0 while 1: #Wait for serial data to be available if self.ser.inWaiting() != 0: #Read a line of data data = int(self.ser.readline()) #Exit the while loop if a termination character (0xFF) is received if data == 255: break; #Highlight the column specified by the data self.highlightColumn( data ) #Scroll to the column so we can see it self.tableView.scrollToItem( self.tableView.item(0,data) ) #Unhighlight previous column if data > 0: self.unhighlightColumn( data - 1 ) #Force a GUI uptdate QApplication.processEvents() self.unhighlightColumn( self.tableView.columnCount()-1 ) self.ser.close() #Enable play and clear buttons after song is song playing self.pushButton_2.setEnabled( True ) self.pushButton_3.setEnabled( True ) def handleClearButton( self ): #Go through each cell, make it white, and set it as unselected for i in xrange(self.tableView.rowCount()): for j in xrange(self.tableView.columnCount()): self.tableView.item(i, j).setBackground(QtGui.QColor(255,255,255)) self.selectedCells[i][j] = self.cellUnselected def serialScan( self ): #Populate the combobox with ports self.comboBox.clear() self.comboBox.addItems( self.ports ) #Go through each port name to see if it exists for i in self.ports[1:]: self.ser.port = i try: self.ser.open() except Exception, e: #If the port name does not exist, remove it from the combobox remove_port = self.comboBox.findText( self.ser.port ) self.comboBox.removeItem( remove_port ) self.ser.close() def handleComButton( self ): #If the comm. button is is Scan mode, scan the serial ports if self.comButtonState == "Scan": self.serialScan() if self.comboBox.count() > 1: #If we have detected a serial port, change the button name to "connect" and change color self.pushButton.setText(_translate("MainWindow", "Connect", None)) self.pushButton.setStyleSheet("color: black; background-color: yellow;") self.comButtonState = "Connect" #Enable the play and clear buttons self.pushButton_2.setEnabled( True ) self.pushButton_3.setEnabled( True ) else: #Create a message box if there was no COM port detected QtGui.QMessageBox.information(self, 'Error',"A serial device was not detected, please ensure that it is plugged into your computer.") elif self.comButtonState == "Connect": #Create a new port self.ser.close() new_port = str(self.comboBox.currentText()) self.ser.port = new_port try: if self.comboBox.currentText() != "Select": self.ser.open() else: QtGui.QMessageBox.information(self, 'Error',"Please Select a COM port.") except Exception ,e: #If we can't connect, reset the button state to "scan" self.pushButton.setText(_translate("MainWindow", "Disconnect", None)) self.pushButton.setStyleSheet("color: black; background-color: red;") self.comButtonState = "Scan" if self.ser.isOpen(): #If we can open the serial port, change the button name to "disconnect" and change color self.pushButton.setText(_translate("MainWindow", "Disconnect", None)) self.pushButton.setStyleSheet("color: black; background-color: green;") self.comButtonState = "Disconnect" self.ser.close() elif self.comButtonState == "Disconnect": #Change the button text to "Scan" once disconnect it pressed, and change color self.pushButton.setText(_translate("MainWindow", "Scan", None)) self.pushButton.setStyleSheet("color: black; background-color: red;") self.comButtonState = "Scan" #Disable play and clear buttons self.pushButton_2.setEnabled( False ) self.pushButton_3.setEnabled( False ) def beepThread( self, tone ): print "No sound" #Produce a tone using winsound # Uncomment below for sound #winsound.Beep(tone, 150) def handleCellClicked(self, row, column): self.noteExists = False #Check to see if any notes are already selected in the row for i in xrange(self.tableView.rowCount()): if self.selectedCells[i][column] == self.cellSelected: self.noteExists = True #If a cell is clicked and unselected, change it's color, mark it as selected, and start the tone thread if self.selectedCells[row][column] == self.cellUnselected: #Do not select cell if a note already exists in the column if self.noteExists == False: self.tableView.item(row, column).setBackground(QtGui.QColor(100,100,150)) self.selectedCells[row][column] = self.cellSelected thread.start_new_thread(self.beepThread, (self.noteFrequencies[row],)) #If a cell is clicked and selected, change it's color and mark it as unselected else: self.tableView.item(row, column).setBackground(QtGui.QColor(255,255,255)) self.selectedCells[row][column] = self.cellUnselected def handleSaveSongButton( self ): #Create a save file dialog self.fileName = QFileDialog.getSaveFileName(self,"Save File", "", "FDRM-KL46z Song (*.song)"); try: #Create a file with the filename provided from the user self.f = open(self.fileName, 'w') #Write the note number index self.f.write( str(self.comboBox2.currentIndex()) + '\n' ) #Write each note as a coordinate on a newline for i in xrange(self.tableView.rowCount()): for j in xrange(self.tableView.columnCount()): if self.selectedCells[i][j] == self.cellSelected: self.f.write( str(i)+ ',' + str(j) + '\n') #Write the end of song marker self.f.write( "end song") self.f.close() except Exception ,e: print(e) def handleLoadSongButton( self ): #Create and open file dialog self.fileName = QFileDialog.getOpenFileName(self,"Open File", "", "FDRM-KL46z Song (*.song)"); try: #Open the user defined file for reading self.f = open(self.fileName, 'r') #cleat all notes from tableview self.handleClearButton(); #Read index number and change the note number combo box index = int(self.f.readline()) self.handleNoteNumberChange(index) self.comboBox2.setCurrentIndex(index) #Read the first cell location self.cellID = self.f.readline() #Continue reading cell ID's and activate corresponding cells while self.cellID != "end song": i = 0 columnString = "" rowString = "" #Wait until we get a comma while self.cellID[i] != ',': rowString += self.cellID[i] i += 1 #Go to the next data point after a newline character while self.cellID[i+1] != '\n': columnString += self.cellID[i+1] i += 1 #Update the table view self.tableView.item(int(rowString), int(columnString)).setBackground(QtGui.QColor(100,100,150)) self.selectedCells[int(rowString)][int(columnString)] = self.cellSelected self.cellID = self.f.readline() except Exception ,e: print(e) def handleNoteNumberChange( self, index ): #Change the number of columns in the table if the song length changes self.tableViewColumnCount = int(self.noteNumberValues[int(index)]) self.selectedCells = [[0 for x in xrange(self.tableViewColumnCount)] for x in xrange(self.tableViewRowCount)] #Reset the table self.initializeTable() self.handleClearButton() def handleTempoChange( self, index ): #Change necesarry variables if the tempo is chaged self.tempo = int(self.tempoValues[int(index)]) def handleAboutButton( self ): #Opens the about form if the about button is pressed self.dialog = QtGui.QDialog(self) self.dialog.ui = aboutForm() self.dialog.ui.setupUi(self.dialog) self.dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.dialog.show() def bind_events( self ): #Bind events to handler functions self.pushButton.clicked.connect( self.handleComButton ) self.tableView.cellClicked.connect( self.handleCellClicked ) self.pushButton_3.clicked.connect( self.handleClearButton ) self.actionSave_Song.triggered.connect( self.handleSaveSongButton ) self.actionLoad_Song.triggered.connect( self.handleLoadSongButton ) self.pushButton_2.clicked.connect( self.handlePlayButton ) self.comboBox2.currentIndexChanged.connect( self.handleNoteNumberChange ) self.comboBox3.currentIndexChanged.connect( self.handleTempoChange ) self.actionAbout.triggered.connect( self.handleAboutButton ) class aboutForm(object): def setupUi(self, Form): #Configure the form Form.setObjectName(_fromUtf8("Form")) Form.resize(381, 233) Form.setMinimumSize(QtCore.QSize(381, 233)) Form.setMaximumSize(QtCore.QSize(381, 233)) #Create label self.label = QtGui.QLabel(Form) self.label.setGeometry(QtCore.QRect(140, 160, 121, 16)) self.label.setObjectName(_fromUtf8("label")) #Create label_2 self.label_2 = QtGui.QLabel(Form) self.label_2.setGeometry(QtCore.QRect(140, 180, 121, 16)) self.label_2.setObjectName(_fromUtf8("label_2")) #Create label_3 self.label_3 = QtGui.QLabel(Form) self.label_3.setGeometry(QtCore.QRect(160, 200, 121, 16)) self.label_3.setObjectName(_fromUtf8("label_3")) #Create a graphics view self.graphicsView = QtGui.QGraphicsView(Form) self.graphicsView.setGeometry(QtCore.QRect(40, 20, 300, 120)) self.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.graphicsView.setObjectName(_fromUtf8("graphicsView")) #Set the graphics view image self.scene = QtGui.QGraphicsScene(); self.pixmap = QtGui.QPixmap( "TekBots.png" ) self.scene.addPixmap(self.pixmap) self.graphicsView.setScene( self.scene ) self.graphicsView.setObjectName(_fromUtf8("graphicsView")) #Set text of all UI elements self.retranslateUi(Form) #Connect Slots QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): #Set text of all UI elements Form.setWindowTitle(_translate("About", "About", None)) self.label.setText(_translate("About", "FDRM-KL46z Beat Factory", None)) self.label_2.setText(_translate("About", "Author: Ethan Takla", None)) self.label_3.setText(_translate("About", "Revision 0.1", None)) #Start the app app = QApplication(sys.argv) window = QMainWindow() ui = Ui_MainWindow() ui.initializeVariables() ui.initializeUI(window) ui.initializeTable() ui.initializeSerial() ui.bind_events() window.show() sys.exit(app.exec_())