Lab3_Gui code
Dependencies: mbed
Diff: mbed_beat_new.py
- Revision:
- 1:faa8aa177069
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_beat_new.py Thu Sep 21 22:16:15 2017 -0700 @@ -0,0 +1,662 @@ +#*****************************************************************# +# # +# 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_())