def currentIndexChanged(self): self.commitData.emit(self.sender()) #if a type from the components table was just set then fill in the component name unless it is already named if (self.name == 'component_type') & (self.parent.objectName() == 'components'): #get the table view object tv = self.parent #combo is the combo box that is sending these data combo = self.sender() #current row currentRow = tv.indexAt(combo.pos()).row() #check if there is already a component name currentName = tv.model().data(tv.model().index(currentRow, 4)) if (currentName == '') | (currentName is None) | (currentName == 'NA') | ( currentName[0:3] != self.sender().currentText()): #get the number of components of this type - handler = ProjectSQLiteHandler() i = handler.getTypeCount(self.sender().currentText()) name = self.sender().currentText() + str(i) tv.model().setData(tv.model().index(currentRow, 4), name) tv.model().submitAll() tv.model().select() handler.closeDatabase() return
def fillSetInfo(self, set='default'): databaseHandler = ProjectSQLiteHandler() # dictionary of set info setInfo = databaseHandler.getSetInfo(set) databaseHandler.closeDatabase() if type(setInfo['component_names']) == str: self.componentDefault = setInfo['component_names'].split(',') else: self.componentDefault = setInfo['component_names'] start = datetime.datetime.strptime(setInfo['min_date'], '%Y-%m-%d') end = datetime.datetime.strptime(setInfo['max_date'], '%Y-%m-%d') #dates are strings here but they need to be datetimes self.startDate = setInfo.get('date_start') self.endDate = setInfo.get('date_end') self.getDefaultDates(start=self.startDate, end=self.endDate) #fillSetInfo the widget values self.setDateSelectorProperties( self.findChild(QtWidgets.QDateEdit, 'startDate')) self.setDateSelectorProperties( self.findChild(QtWidgets.QDateEdit, 'endDate'), False) self.findChild(QtWidgets.QDateEdit, 'startDate').setDateRange(start, end) self.findChild(QtWidgets.QDateEdit, 'endDate').setDateRange(start, end) self.findChild(QtWidgets.QLineEdit, 'componentNames').setText(','.join( self.componentDefault)) self.updateComponentDelegate(self.componentDefault) return
def getDefaultDates(self, **kwargs): #tuples start = kwargs.get('start') end = kwargs.get('end') handler = ProjectSQLiteHandler() if start == None: start = handler.cursor.execute( "select date_start from setup where set_name = 'default'" ).fetchone() if end == None: end = handler.cursor.execute( "select date_end from setup where set_name = 'default'" ).fetchone() handler.closeDatabase() #format the tuples from database output to datetime objects if type(start) == str: start = datetime.datetime.strptime(start, '%Y-%m-%d') end = datetime.datetime.strptime(end, '%Y-%m-%d') else: start = datetime.datetime.strptime(start[0], '%Y-%m-%d') end = datetime.datetime.strptime(end[0], '%Y-%m-%d') self.startDate = start self.endDate = end return
def makeComponentList(): import pandas as pd sqlhandler = ProjectSQLiteHandler('project_manager') components = pd.read_sql_query( "select component_name from components", sqlhandler.connection) components = list(components['component_name']) sqlhandler.closeDatabase() return components
def updateValues(self): #find the data input grid myGrid = self.findChild(QtWidgets.QWidget, 'inputGrid') #get a soup of values that have changed newSoup, changes = update(myGrid) #TODO something with new soup and changes #should this be written to an xml file for optimizer input? #write changes to the database dbHandler = ProjectSQLiteHandler() for k in changes.keys(): #if we return false upldate the existing parameter if not dbHandler.insertRecord('optimize_input', ['parameter', 'parameter_value'], [k, changes[k]]): dbHandler.updateRecord('optimize_input', ['parameter'], [k], ['parameter_value'], [changes[k]]) return
def clearProjectDatabase(caller=None): handler = ProjectSQLiteHandler() # get the name of the last project worked on lastProjectPath = handler.getProjectPath() handler.makeDatabase() print(handler.dataCheck('components')) handler.closeDatabase() #the forms need to be cleared or data will get re-written to database if caller is not None: clearAppForms(caller) return lastProjectPath
def createFileTab(self): self.dbhandler = ProjectSQLiteHandler() windowLayout = QtWidgets.QVBoxLayout() self.createTopBlock('Setup', self.assignFileBlock) l = self.FileBlock.findChild(QtWidgets.QWidget, 'inputFileDirvalue') l.clicked.connect(self.lineclicked) windowLayout.addWidget(self.FileBlock) self.createTableBlock('Components', 'components', self.assignComponentBlock) windowLayout.addWidget(self.componentBlock) # the bottom block is disabled until a setup file is created or loaded self.createTableBlock('Environment Data', 'environment', self.assignEnvironmentBlock) windowLayout.addWidget(self.environmentBlock) return windowLayout
def updateSetsSql(set, setupModel): uihandler = UIToHandler() xmlfile = setupModel.getSetAttributeXML(set) soup = uihandler.getSetAttributeXML(xmlfile) setupTags = soup.findChild('setupTag')['value'].split(' ') setupValue = soup.findChild('setupValue')['value'].split(' ') if setupValue[setupTags.index("runTimeSteps")].split(',') != 'all': start = integerToTimeIndex(setupModel.data.fixed, setupValue[setupTags.index("runTimeSteps")].split(',')[0]) end = integerToTimeIndex(setupModel.data.fixed, setupValue[setupTags.index("runTimeSteps")].split(',')[1]) else: start = '' end = '' timestep = setupValue[setupTags.index("timeStep")] components = setupValue[setupTags.index("componentNames")] updateTuple = (start, end, timestep, components, set) # check if the setup information exists in the database sqlHandler = ProjectSQLiteHandler() dataTuple = sqlHandler.cursor.execute("select date_start, date_end, timestep, component_names, _id from setup where set_name = '" + set + "'").fetchone() # update setup table database columns with xml attribute information if it exists otherwise create a record if dataTuple is not None: #update sqlHandler.cursor.execute("UPDATE setup set date_start = ?, date_end = ?, timestep = ?,component_names = ? where set_name = ?", updateTuple) else: #insert sqlHandler.cursor.execute( "INSERT INTO setup (date_start, date_end, timestep, component_names, set_name) Values(?,?,?,?,?) ", updateTuple) # update the set table also compNames = soup.findChild('compName')['value'].split(' ') compTags = soup.findChild('compTag')['value'].split(' ') compAttrs = soup.findChild('compAttr')['value'].split(' ') compValues = soup.findChild('compValue')['value'].split(' ') for i,c in enumerate(compNames): dataTuple = (set,c,compTags[i],compValues[i]) #this will result in a new row if a value has changed directly in the xml but not in the project database if len(sqlHandler.cursor.execute("SELECT * from sets where set_name = ? AND component = ? AND change_tag = ? AND to_value = ?", dataTuple).fetchall()) < 1: sqlHandler.cursor.execute("INSERT INTO sets (set_name, component, change_tag, to_value) VALUES (?,?,?,?)", dataTuple) sqlHandler.connection.commit() sqlHandler.closeDatabase() return
def createInputFiles(self): import os self.addProgressBar() self.progress.setRange(0, 0) self.sendSetupData() # check all the required fields are filled dbhandler = ProjectSQLiteHandler() if not dbhandler.dataComplete(): #if required fields are not filled in return to setup page. msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Missing Required Fields", "Please fill in all required fields before generating input files." ) msg.setStandardButtons(QtWidgets.QMessageBox.Ok) msg.exec() dbhandler.closeDatabase() return dbhandler.closeDatabase() # write all the xml files # start with the setupxml self.model.writeNewXML() # import datafiles handler = UIToHandler() cleaned_data, components = handler.loadFixData( os.path.join(model.setupFolder, model.project + 'Setup.xml')) self.updateModelPage(cleaned_data) # pickled data to be used later if needed handler.storeData( cleaned_data, os.path.join(model.setupFolder, model.project + 'Setup.xml')) handler.storeComponents( components, os.path.join(model.setupFolder, model.project + 'Setup.xml')) self.dataLoaded.setText('data loaded') self.progress.setRange(0, 1) # generate netcdf files msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, "Time Series loaded", "Do you want to generate netcdf files?.") msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) result = msg.exec() # if yes create netcdf files, Otherwise this can be done after the data is reviewed. if result == QtWidgets.QMessageBox.Ok: d = {} for c in components: d[c.column_name] = c.toDictionary() handler.createNetCDF( cleaned_data.fixed, d, os.path.join(model.setupFolder, model.project + 'Setup.xml')) return
def makeAttributeXML(currentSet, compmodel): from UserInterface.ProjectSQLiteHandler import ProjectSQLiteHandler from PyQt5 import QtWidgets soup = readTemplateAttributeXML() #fillSetInfo the soup to reflect the model #for each row in model compName = '' compTag = '' compAttr = '' compValue = '' for i in range(compmodel.rowCount()): compName = ' '.join([compName, compmodel.data(compmodel.index(i, 2))]) compTag = ' '.join([ compTag, '.'.join(compmodel.data(compmodel.index(i, 3)).split('.')[:-1]) ]) compAttr = ' '.join( [compAttr, compmodel.data(compmodel.index(i, 3)).split('.')[-1]]) compValue = ' '.join( [compValue, compmodel.data(compmodel.index(i, 4))]) tag = soup.find('compName') tag.attrs['value'] = compName.lstrip() tag = soup.find('compTag') tag.attrs['value'] = compTag.lstrip() tag = soup.find('compAttr') tag.attrs['value'] = compAttr.lstrip() tag = soup.find('compValue') tag.attrs['value'] = compValue.lstrip() #fillSetInfo the set information handler = ProjectSQLiteHandler() dataTuple = handler.cursor.execute( "SELECT set_name, date_start, date_end, timestep, component_names from setup where set_name = '" + currentSet.lower() + "'").fetchone() tag = soup.find('setupTag') tag.attrs['value'] = "componentNames runTimeSteps timeStep" tag = soup.find('setupAttr') tag.attrs['value'] = "value value value" tag = soup.find('setupValue') df = compmodel.parent().window().findChild(QtWidgets.QWidget, 'setupDialog').model.data.fixed tag.attrs['value'] = " ".join([ dataTuple[4], timeStepsToInteger(dataTuple[1], dataTuple[2], df), str(dataTuple[3]) ]) return soup
def componentCellClicked(self): from UserInterface.DialogComponentList import ComponentSetListForm from UserInterface.ProjectSQLiteHandler import ProjectSQLiteHandler import pandas as pd handler = ProjectSQLiteHandler('project_manager') # get the cell, and open a listbox of possible components for this project checked = pd.read_sql_query("select component_name from components", handler.connection) checked = list(checked['component_name']) handler.closeDatabase() # checked is a comma seperated string but we need a list #checked = checked.split(',') listDialog = ComponentSetListForm(checked) components = listDialog.checkedItems() # format the list to be inserted into a text field in a datatable str1 = ','.join(components) widg = self.findChild(QtWidgets.QLineEdit, 'componentNames') widg.setText(str1) self.updateComponentDelegate(components)
def makeListWidget(self): import pandas as pd from UserInterface.ProjectSQLiteHandler import ProjectSQLiteHandler sqlhandler = ProjectSQLiteHandler('project_manager') self.components = pd.read_sql_query( "select component_name from components", sqlhandler.connection) self.components = list(self.components['component_name']) checked = [x in self.checked for x in self.components] sqlhandler.closeDatabase() listWidget = QtWidgets.QListWidget() for i in range(len(self.components)): item = QtWidgets.QListWidgetItem(self.components[i]) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) if checked[i]: item.setCheckState(QtCore.Qt.Checked) else: item.setCheckState(QtCore.Qt.unchecked) listWidget.addItem(item) listWidget.itemClicked.connect(self.on_listWidget_itemClicked) return listWidget
def runSet(self): # currentSet currentSet = self.set #set info needs to be updated in the database setInfo = (currentSet, self.findChild(QtWidgets.QDateEdit, 'startDate').text(), self.findChild(QtWidgets.QDateEdit, 'endDate').text(), self.findChild(QtWidgets.QLineEdit, 'timestep').text(), self.findChild(QtWidgets.QLineEdit, 'componentNames').text()) sqlhandler = ProjectSQLiteHandler() try: sqlhandler.cursor.execute( "INSERT INTO setup(set_name, date_start, date_end, timestep, component_names) VALUES(?,?,?,?,?)", setInfo) except: sqlhandler.cursor.execute( "UPDATE setup set date_start = ?, date_end=?, timestep=?, component_names=? WHERE set_name = '" + setInfo[0] + "'", setInfo[1:]) sqlhandler.connection.commit() sqlhandler.closeDatabase() uihandler = UIToHandler() # component table is the table associated with the button componentTable = self.findChild(SetTableView).model() if componentTable.rowCount() > 0: uihandler.runModels( currentSet, componentTable, self.window().findChild(QtWidgets.QWidget, 'setupDialog').model) else: msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Add components", "You need to select component attributes to alter before running sets." ) msg.setStandardButtons(QtWidgets.QMessageBox.Ok) msg.exec()
def replaceDefaultDatabase(projectdb): from UserInterface.ProjectSQLiteHandler import ProjectSQLiteHandler import pandas as pd tables = ['environment', 'components', 'sets', 'runs'] for t in tables: h = ProjectSQLiteHandler(projectdb) # project data becomes a dataframe try: projectTable = pd.read_sql_query("select * from " + t, h.connection) h.closeDatabase() # the _id field is always the index for all tables projectTable.set_index(projectTable['_id']) projectTable = projectTable.drop('_id', 1) projectTable.index.names = ['_id'] # connect to the active database and overwrite the table h = ProjectSQLiteHandler('project_manager') #data gets appended into empty tables created in default database projectTable.to_sql(t, h.connection, if_exists='append') h.closeDatabase() except: #TODO print some message to the console h.closeDatabase()
class FileBlock(QtWidgets.QGroupBox): def __init__(self, parent, input): super().__init__(parent) #integer -> FileBlock self.init(input) # creates a single form for entering individual file type information def init(self, input): self.input = input windowLayout = self.createFileTab() self.setLayout(windowLayout) self.model = self.window().findChild(QtWidgets.QWidget, 'setupDialog').model self.setFocusPolicy(QtCore.Qt.StrongFocus) # -> QVBoxLayout def createFileTab(self): self.dbhandler = ProjectSQLiteHandler() windowLayout = QtWidgets.QVBoxLayout() self.createTopBlock('Setup', self.assignFileBlock) l = self.FileBlock.findChild(QtWidgets.QWidget, 'inputFileDirvalue') l.clicked.connect(self.lineclicked) windowLayout.addWidget(self.FileBlock) self.createTableBlock('Components', 'components', self.assignComponentBlock) windowLayout.addWidget(self.componentBlock) # the bottom block is disabled until a setup file is created or loaded self.createTableBlock('Environment Data', 'environment', self.assignEnvironmentBlock) windowLayout.addWidget(self.environmentBlock) return windowLayout # creates a horizontal layout containing gridlayouts for data input @QtCore.pyqtSlot() def lineclicked(self): '''opens a folder dialog and returns the string value of the pathway selected''' #if the directory has already been set then open the dialog to there otherwise default to current working directory curdir = self.findChild(QtWidgets.QWidget, 'inputFileDirvalue').text() if curdir == '': curdir = os.getcwd() folderDialog = QtWidgets.QFileDialog.getExistingDirectory( None, 'Select a directory.', curdir) if (folderDialog != ''): #once selected folderDialog gets set to the input box self.findChild(QtWidgets.QWidget, 'inputFileDirvalue').setText(folderDialog) #save the input to the setup data model and into the database self.saveInput() #update the filedir path if self.dbhandler.getInputPath(str(self.input)) is None: self.dbhandler.insertRecord('input_files', ['inputfiledirvalue'], [folderDialog]) else: self.dbhandler.updateRecord('input_files', ['_id'], [str(self.input)], ['inputfiledirvalue'], [folderDialog]) #filter the component and environemnt input tables to the current input directory self.filterTables() self.saveInput() self.model.writeNewXML() return folderDialog def createTopBlock(self, title, fn): '''The top block is where file information is set (format, date and time channels and file type) :param title: [String] :param fn: [method] used to assign the layout to a property''' # create a horizontal grouping to contain the top portion of the form gb = QtWidgets.QGroupBox(title) hlayout = QtWidgets.QHBoxLayout() hlayout.setObjectName("setup") # add the setup grids g1 = { 'headers': [1, 2, 3, 4], 'rowNames': [1, 2, 3, 4], 'columnWidths': [1, 1, 1, 3], 1: { 1: { 'widget': 'lbl', 'name': 'File Type:', 'default': 'File Type' }, 2: { 'widget': 'combo', 'name': 'inputFileTypevalue', 'items': ['csv', 'MET'] }, 3: { 'widget': 'lbl', 'name': 'File Directory', 'default': 'Directory' }, 4: { 'widget': 'lncl', 'name': 'inputFileDirvalue' } }, 2: { 1: { 'widget': 'lbl', 'name': 'Date Channel', 'default': 'Date Channel' }, 2: { 'widget': 'txt', 'name': 'dateChannelvalue' }, 3: { 'widget': 'lbl', 'name': 'Date Format', 'default': 'Date Format' }, 4: { 'widget': 'combo', 'items': [ 'mm/dd/yy', 'mon-dd YYYY', 'mm/dd/YYYY', 'mm-dd-YYYY', 'dd/mm/YYYY' ], 'name': 'dateChannelformat' } }, 3: { 1: { 'widget': 'lbl', 'name': 'Time Channel', 'default': 'Time Channel' }, 2: { 'widget': 'txt', 'name': 'timeChannelvalue' }, 3: { 'widget': 'lbl', 'name': 'Time Format', 'default': 'Time Format' }, 4: { 'widget': 'combo', 'items': ['HH:MM:SS'], 'name': 'timeChannelformat' } }, 4: { 1: { 'widget': 'lbl', 'name': 'Time Zone', 'default': 'Time Zone' }, 2: { 'widget': 'combo', 'items': pytz.all_timezones, 'name': 'timeZonevalue', 'default': 'America/Anchorage' }, 3: { 'widget': 'lbl', 'name': 'Use DST', 'default': 'Use DST' }, 4: { 'widget': 'chk', 'name': 'useDSTvalue', 'default': False } } } grid = setupGrid(g1) hlayout.addLayout(grid) hlayout.addStretch(1) gb.setLayout(hlayout) fn(gb) # layout for tables def createTableBlock(self, title, table, fn): gb = QtWidgets.QGroupBox(title) tableGroup = QtWidgets.QVBoxLayout() tableGroup.addWidget(self.dataButtons(table)) if table == 'components': tv = T.ComponentTableView(self) tv.setObjectName('components') m = T.ComponentTableModel(self) tv.hideColumn(1) tv.setModel(m) tv.hideColumn(0) tableGroup.addWidget(tv, 1) else: tv = E.EnvironmentTableView(self) tv.setObjectName('environment') m = E.EnvironmentTableModel(self) tv.setModel(m) tv.hideColumn(0) tableGroup.addWidget(tv, 1) self.filterTables() gb.setLayout(tableGroup) gb.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) fn(gb) return # Load an existing descriptor file and populate the component table # -> None def functionForLoadDescriptor(self): msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, 'Load Descriptor', 'If the component descriptor file you are loading has the same name as an existing component it will not load' ) msg.setStandardButtons(QtWidgets.QMessageBox.Ok) msg.exec() tableView = self.findChild((QtWidgets.QTableView), 'components') model = tableView.model() # identify the xml descriptorFile = QtWidgets.QFileDialog.getOpenFileName( self, "Select a descriptor file", None, "*xml") if (descriptorFile == ('', '')) | (descriptorFile is None): return fieldName, ok = QtWidgets.QInputDialog.getText( self, 'Field Name', 'Enter the name of the channel that contains data for this component.' ) # if a field was entered add it to the table model and database if ok: record = model.record() record.setValue('original_field_name', fieldName) handler = UIToHandler() record = handler.copyDescriptor(descriptorFile[0], self.model.componentFolder, record) # add a row into the database model.insertRowIntoTable(record) # refresh the table model.select() return # Add an empty record to the specified datatable # String -> None def functionForNewRecord(self, table): # add an empty record to the table handler = TableHandler(self) filedir = self.FileBlock.findChild(QtWidgets.QWidget, 'inputFileDirvalue').text() handler.functionForNewRecord(table, fields=[1], values=[filedir]) # delete the selected record from the specified datatable # String -> None def functionForDeleteRecord(self, table): # get selected rows tableView = self.findChild((QtWidgets.QTableView), table) model = tableView.model() # selected is the indices of the selected rows selected = tableView.selectionModel().selection().indexes() if len(selected) == 0: msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, 'Select Rows', 'Select rows before attempting to delete') msg.setStandardButtons(QtWidgets.QMessageBox.Ok) msg.exec() else: msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, 'Confirm Delete', 'Are you sure you want to delete the selected records?') msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) result = msg.exec() if result == QtWidgets.QMessageBox.Ok: handler = UIToHandler() removedRows = [] for r in selected: if r.row() not in removedRows: if table == 'components': # remove the xml files too handler.removeDescriptor( model.data(model.index(r.row(), 3)), self.model.componentFolder) removedRows.append(r.row()) model.removeRows(r.row(), 1) # Delete the record from the database and refresh the tableview model.submitAll() model.select() # string -> QGroupbox def dataButtons(self, table): buttonBox = QtWidgets.QGroupBox() buttonRow = QtWidgets.QHBoxLayout() if table == 'components': buttonRow.addWidget( makeButtonBlock( self, self.functionForLoadDescriptor, None, 'SP_DialogOpenButton', 'Load a previously created component xml file.')) buttonRow.addWidget( makeButtonBlock(self, lambda: self.functionForNewRecord(table), '+', None, 'Add a component')) buttonRow.addWidget( makeButtonBlock(self, lambda: self.functionForDeleteRecord(table), None, 'SP_TrashIcon', 'Delete a component')) buttonRow.addStretch(3) buttonBox.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) buttonBox.setLayout(buttonRow) return buttonBox # TODO this needs to change if its populating from a table # inserts data from the data model into corresponding boxes on the screen # SetupInfo -> None def fillData(self, model, i): # dictionary of attributes of the class SetupTag belonging to a SetupInformation Model d = model.getSetupTags() # for every key in d find the corresponding textbox or combo box for k in d.keys(): #values in d are setup tags and can contain list values #each key in the setuptag has its own display slot on the form #this fills the topblock tag_keys = d[k].keys() for t in tag_keys: if t != 'name': edit_field = self.findChild( (QtWidgets.QLineEdit, QtWidgets.QComboBox), k + t) if type(edit_field) is QtWidgets.QLineEdit: if len(d[k][t]) > 0: edit_field.setText(d[k][t][self.input - 1]) elif type(edit_field) is ClickableLineEdit: if len(d[k][t]) > 0: edit_field.setText(d[k][t][self.input - 1]) elif type(edit_field) is QtWidgets.QComboBox: if len(d[k][t]) > 0: edit_field.setCurrentIndex( edit_field.findText(d[k][t][self.input - 1])) def getDefault(l, i): try: l[i] return l[i] except IndexError: return 'NA' # refresh the tables self.filterTables() return # Setters #(String, number, list or Object) -> def assignEnvironmentBlock(self, value): self.environmentBlock = value def assignComponentBlock(self, value): self.componentBlock = value def assignFileBlock(self, value): self.FileBlock = value self.FileBlock.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.FileBlock.sizePolicy().retainSizeWhenHidden() self.FileBlock.setObjectName('fileInput') # if the fileblock looses focus update database information def focusOutEvent(self, event): if 'projectFolder' in self.model.__dict__.keys(): #input to model self.saveInput() #input to database setupFields, setupValues = self.getSetupInfo() # update database table if not self.dbhandler.insertRecord('input_files', setupFields, setupValues): self.dbhandler.updateRecord('input_files', ['_id'], [str(setupValues[0])], setupFields[1:], setupValues[1:]) # on leave save the xml files self.model.writeNewXML() return #reads data from an file input top block and returns a list of fields and values def getSetupInfo(self): fieldNames = ['_id'] #values = [re.findall(r'\d+',self.input)[0]] values = [self.input] for child in self.FileBlock.findChildren( (QtWidgets.QLineEdit, QtWidgets.QComboBox)): if type(child) is QtWidgets.QLineEdit: fieldNames.append(child.objectName()) values.append(child.text()) elif type(child) is ClickableLineEdit: fieldNames.append(child.objectName()) values.append(child.text()) else: fieldNames.append(child.objectName()) values.append(child.itemText(child.currentIndex())) return fieldNames, values #save the form input to the form setup data model def saveInput(self): #update model info from fileblock self.model.assignTimeChannel(SetupTag.assignValue, self.FileBlock.findChild( QtWidgets.QWidget, 'timeChannelvalue').text(), position=int(self.input) - 1) self.model.assignTimeChannel(SetupTag.assignFormat, self.FileBlock.findChild( QtWidgets.QWidget, 'timeChannelformat').currentText(), position=int(self.input) - 1) self.model.assignDateChannel(SetupTag.assignValue, self.FileBlock.findChild( QtWidgets.QWidget, 'dateChannelvalue').text(), position=int(self.input) - 1) self.model.assignDateChannel(SetupTag.assignFormat, self.FileBlock.findChild( QtWidgets.QWidget, 'dateChannelformat').currentText(), position=int(self.input) - 1) self.model.assignInputFileDir(SetupTag.assignValue, self.FileBlock.findChild( QtWidgets.QWidget, 'inputFileDirvalue').text(), position=int(self.input) - 1) self.model.assignInputFileType(SetupTag.assignValue, self.FileBlock.findChild( QtWidgets.QWidget, 'inputFileTypevalue').currentText(), position=int(self.input) - 1) self.model.assignTimeZone(SetupTag.assignValue, self.FileBlock.findChild( QtWidgets.QWidget, 'timeZonevalue').currentText(), position=int(self.input) - 1) self.model.assignUseDST(SetupTag.assignValue, str( self.FileBlock.findChild( QtWidgets.QWidget, 'useDSTvalue').isChecked()), position=int(self.input) - 1) self.saveTables() return # calls the specified function connected to a button onClick event @QtCore.pyqtSlot() def onClick(self, buttonFunction): buttonFunction() def filterTables(self): tables = self.findChildren(QtWidgets.QTableView) filedir = self.FileBlock.findChild(QtWidgets.QWidget, 'inputFileDirvalue').text() self.filter = filedir for t in tables: m = t.model() m.setFilter("inputfiledir = '" + filedir + "'") def saveTables(self): '''get data from component and environment tables and update the setupInformation model components within a single directory are seperated with commas component info comes from the database not the tableview component names, units, scale, offset, attribute, fieldname get saved''' names = self.dbhandler.getComponentNames() names = list(set(names)) self.model.assignComponentNames(SetupTag.assignValue, ' '.join(names)) #df is a pandas dataframe of component information df = self.dbhandler.getComponentsTable(self.filter) self.model.assignComponentName( SetupTag.assignValue, [','.join(df['component_name'].tolist())], position=int(self.input) - 1) self.model.assignHeaderName( SetupTag.assignValue, [','.join(df['original_field_name'].tolist())], position=int(self.input) - 1) self.model.assignComponentAttribute(SetupTag.assignValue, [ ','.join([ x if x is not None else 'NA' for x in df['attribute'].tolist() ]) ], position=int(self.input) - 1) self.model.assignComponentAttribute(SetupTag.assignUnits, [ ','.join( [x if x is not None else 'NA' for x in df['units'].tolist()]) ], position=int(self.input) - 1) loC = [ self.model.makeNewComponent(df['component_name'], x['original_field_name'], x['units'], x['attribute'], x['component_type']) for i, x in df.iterrows() ] return loC def close(self): if 'projectFolder' in self.model.__dict__.keys(): #input to model self.saveInput() #input to database setupFields, setupValues = self.getSetupInfo() # update database table if not self.dbhandler.insertRecord('input_files', setupFields, setupValues): self.dbhandler.updateRecord('input_files', ['_id'], [setupFields[0]], setupFields[1:], setupValues[1:]) self.saveTables() # on leave save the xml files self.model.writeNewXML() self.dbhandler.closeDatabase()
class FormSetup(QtWidgets.QWidget): global model model = ModelSetupInformation() def __init__(self, parent): super().__init__(parent) self.lastProjectPath = parent.lastProjectPath self.initUI() #initialize the form def initUI(self): self.dbHandler = ProjectSQLiteHandler() self.setObjectName("setupDialog") self.model = model #the main layout is oriented vertically windowLayout = QtWidgets.QVBoxLayout() # the top block is buttons to load setup xml and data files self.createButtonBlock() windowLayout.addWidget(self.ButtonBlock) self.tabs = Pages(self, '1', FileBlock) self.tabs.setDisabled(True) #each file type gets its own page to specify formats and headers to include # button to create a new set tab newTabButton = QtWidgets.QPushButton() newTabButton.setText(' + Input') newTabButton.setFixedWidth(100) newTabButton.clicked.connect(self.newTab) windowLayout.addWidget(newTabButton) windowLayout.addWidget(self.tabs, 3) #list of dictionaries containing information for wizard #this is the information that is not input file specific. dlist = [{ 'title': 'Dates to model', 'prompt': 'Enter the timespan you would like to include in the model.', 'sqltable': None, 'sqlfield': None, 'reftable': None, 'name': 'runTimesteps', 'folder': False, 'dates': True }, { 'title': 'Timestep', 'prompt': 'Enter desired timestep', 'sqltable': None, 'sqlfield': None, 'reftable': 'ref_time_units', 'name': 'timestep', 'folder': False }, { 'title': 'Project', 'prompt': 'Enter the name of your project', 'sqltable': None, 'sqlfield': None, 'reftable': None, 'name': 'project', 'folder': False }] self.WizardTree = self.buildWizardTree(dlist) self.createBottomButtonBlock() windowLayout.addWidget(self.BottomButtons) #set the main layout as the layout for the window self.setLayout(windowLayout) #title is setup self.setWindowTitle('Setup') self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) #show the form self.showMaximized() #FormSetup -> QWidgets.QHBoxLayout #creates a horizontal button layout to insert in FormSetup def createButtonBlock(self): self.ButtonBlock = QtWidgets.QGroupBox() hlayout = QtWidgets.QHBoxLayout() #layout object name hlayout.setObjectName('buttonLayout') #add the button to load a setup xml hlayout.addWidget( makeButtonBlock(self, self.functionForLoadButton, 'Load Existing Project', None, 'Load a previously created project files.')) #add button to launch the setup wizard for setting up the setup xml file hlayout.addWidget( makeButtonBlock( self, self.functionForCreateButton, 'Create setup XML', None, 'Start the setup wizard to create a new setup file')) #force the buttons to the left side of the layout hlayout.addStretch(1) self.ButtonBlock.setLayout(hlayout) self.ButtonBlock.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) projectTitlewdg = QtWidgets.QLabel() projectTitlewdg.setObjectName('projectTitle') hlayout.addWidget(projectTitlewdg) hlayout.addStretch(1) return hlayout # FormSetup -> QWidgets.QHBoxLayout # creates a horizontal button layout to insert in FormSetup def createBottomButtonBlock(self): self.BottomButtons = QtWidgets.QGroupBox() hlayout = QtWidgets.QHBoxLayout() # layout object name hlayout.setObjectName('buttonLayout') # add the button to load a setup xml button = QtWidgets.QPushButton('Create input files') button.setToolTip('Create input files to run models') button.clicked.connect(lambda: self.onClick(self.createInputFiles)) button.setFixedWidth(200) # windowLayout.addWidget(makeButtonBlock(self,self.createInputFiles,'Create input files',None,'Create input files to run models'),3) hlayout.addWidget(button) dataLoaded = QtWidgets.QLineEdit() dataLoaded.setFrame(False) dataLoaded.setObjectName('dataloaded') dataLoaded.setText('No data loaded') dataLoaded.setFixedWidth(200) self.dataLoaded = dataLoaded hlayout.addWidget(self.dataLoaded) # generate netcd button netCDFButton = self.createSubmitButton() hlayout.addWidget(netCDFButton) button.setFixedWidth(200) self.netCDFsLoaded = QtWidgets.QLineEdit() self.netCDFsLoaded.setFrame(False) self.netCDFsLoaded.setText("none") hlayout.addWidget(self.netCDFsLoaded) #hlayout.addStretch(1) self.BottomButtons.setLayout(hlayout) return hlayout #method -> None #calls the specified function connected to a button onClick event @QtCore.pyqtSlot() def onClick(self, buttonFunction): buttonFunction() #FormSetup -> None #method to modify FormSetup content def functionForCreateButton(self): #if a project is already started save it before starting a new one if (self.model.project != '') & (self.model.project is not None): self.model = switchProject(self) global model model = self.model s = self.WizardTree s.exec_() handler = UIToHandler() handler.makeSetup(model) #display collected data #returns true if setup info has been entered hasSetup = model.feedSetupInfo() self.projectDatabase = False if hasSetup: #self.topBlock.setEnabled(True) #self.environmentBlock.setEnabled(True) #self.componentBlock.setEnabled(True) #enable the model and optimize pages too pages = self.window().findChild(QtWidgets.QTabWidget, 'pages') pages.enableTabs() self.tabs.setEnabled(True) self.findChild(QtWidgets.QLabel, 'projectTitle').setText(self.model.project) #searches for and loads existing project data - database, setupxml,descriptors, DataClass pickle, Component pickle netcdf,previously run model results, previous optimization results def functionForLoadButton(self): '''The load function reads the designated setup xml, looks for descriptor xmls, looks for an existing project database and a pickled data object.''' #if we were already working on a project its state gets saved and new project is loaded if (self.model.project != '') & (self.model.project is not None): self.model = switchProject(self) global model model = self.model #launch file navigator to identify setup file setupFile = QtWidgets.QFileDialog.getOpenFileName( self, "Select your setup file", self.lastProjectPath, "*xml") if (setupFile == ('', '')) | (setupFile is None): return model.assignSetupFolder(setupFile[0]) # assign setup information to data model model.feedSetupInfo() # now that setup data is set display it in the form self.displayModelData() #Look for an existing project database and replace the default one with it if os.path.exists( os.path.join(self.model.projectFolder, 'project_manager')): print('An existing project database was found for %s.' % self.model.project) replaceDefaultDatabase( os.path.join(self.model.projectFolder, 'project_manager')) self.projectDatabase = True else: self.projectDatabase = False print('An existing project database was not found for %s.' % self.model.project) # record the current project self.dbHandler.insertRecord('project', ['project_path'], [setupFile[0]]) # look for an existing data pickle handler = UIToHandler() self.model.data = handler.loadInputData( os.path.join(self.model.setupFolder, self.model.project + 'Setup.xml')) if self.model.data is not None: self.updateModelPage(self.model.data) self.dataLoaded.setText('data loaded') #refresh the plot resultDisplay = self.parent().findChild(ResultsSetup) resultDisplay.defaultPlot() #look for an existing component pickle or create one from information in setup xml self.model.components = handler.loadComponents( os.path.join(self.model.setupFolder, self.model.project + 'Setup.xml')) if self.model.components is None: self.getComponentsFromSetup() #list netcdf files previously generated self.netCDFsLoaded.setText(', '.join(self.listNetCDFs())) #TODO this part of the code always sets setsRun to false, need to implement check for models run #boolean indicator of whether or not model sets have already been run setsRun = False #make the data blocks editable if there are no sets already created #if sets have been created then input data is not editable from the interface if setsRun: msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Analysis in Progress", "Analysis results were detected. You cannot edit input data after analysis has begun." ) msg.setStandardButtons(QtWidgets.QMessageBox.Ok) msg.exec() else: self.tabs.setEnabled(True) print('Loaded %s:' % model.project) #set the project name on the form self.findChild(QtWidgets.QLabel, 'projectTitle').setText(self.model.project) return #looks in the processed folder and lists nc files found #->ListOfStrings def listNetCDFs(self): lof = [ f for f in os.listdir( getFilePath(self.model.setupFolder, 'Processed')) if f[-2] == 'nc' ] return lof def displayModelData(self): """creates a tab for each input directory specified the SetupModelInformation model inputFileDir attribute. Each tab contains a FileBlock object to interact with the data input Each FileBlock is filled with data specific to the input directory""" self.tabs.removeTab(0) #the number of directories listed in inputFileDir indicates how many tabs are required tab_count = len(self.model.inputFileDir.value) #if directories have been entered then replace the first tab and create a tab for each directory #TODO should remove all previous tabs if tab_count > 0: self.tabs.removeTab(0) for i in range(tab_count): self.newTab(i + 1) else: self.newTab(1) return #List -> WizardTree def buildWizardTree(self, dlist): """builds a QWizard based on list of inputs""" wiztree = QtWidgets.QWizard() wiztree.setWizardStyle(QtWidgets.QWizard.ModernStyle) wiztree.setWindowTitle("Setup") wiztree.addPage(WizardPage(dlist[2])) wiztree.addPage(TextWithDropDown(dlist[1])) wiztree.addPage(TwoDatesDialog(dlist[0])) btn = wiztree.button(QtWidgets.QWizard.FinishButton) btn.clicked.connect(self.saveInput) return wiztree #save the input in the wizard tree to the setup data model def saveInput(self): """save the input in the wizard tree to the ModelSetupInformation data model""" model = self.model model.assignProject(self.WizardTree.field('project')) model.assignTimeStep(SetupTag.assignValue, self.WizardTree.field('timestep')) model.assignRunTimesteps( SetupTag.assignValue, self.WizardTree.field('sdate') + ' ' + self.WizardTree.field('edate')) return def sendSetupData(self): """ send input data to the ModelSetupInformation data model reads through all the file tabs to collect input """ #needs to come from each page tabWidget = self.findChild(QtWidgets.QTabWidget) for t in range(tabWidget.count()): page = tabWidget.widget(t) # cycle through the input children in the topblock for child in page.FileBlock.findChildren( (QtWidgets.QLineEdit, QtWidgets.QComboBox)): if type(child) is QtWidgets.QLineEdit: value = child.text() elif type(child) is ClickableLineEdit: value = child.text() elif type(child) is QtWidgets.QComboBox: value = child.itemText(child.currentIndex()) #append to appropriate list attr = child.objectName() model.assign(attr, value, position=int(page.input) - 1) model.setComponents(page.saveTables()) return #TODO this should be done on a seperate thread # Create a dataframe of input data based on importing files within each SetupModelInformation.inputFileDir # None->None def createInputFiles(self): import os self.addProgressBar() self.progress.setRange(0, 0) self.sendSetupData() # check all the required fields are filled dbhandler = ProjectSQLiteHandler() if not dbhandler.dataComplete(): #if required fields are not filled in return to setup page. msg = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Missing Required Fields", "Please fill in all required fields before generating input files." ) msg.setStandardButtons(QtWidgets.QMessageBox.Ok) msg.exec() dbhandler.closeDatabase() return dbhandler.closeDatabase() # write all the xml files # start with the setupxml self.model.writeNewXML() # import datafiles handler = UIToHandler() cleaned_data, components = handler.loadFixData( os.path.join(model.setupFolder, model.project + 'Setup.xml')) self.updateModelPage(cleaned_data) # pickled data to be used later if needed handler.storeData( cleaned_data, os.path.join(model.setupFolder, model.project + 'Setup.xml')) handler.storeComponents( components, os.path.join(model.setupFolder, model.project + 'Setup.xml')) self.dataLoaded.setText('data loaded') self.progress.setRange(0, 1) # generate netcdf files msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, "Time Series loaded", "Do you want to generate netcdf files?.") msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) result = msg.exec() # if yes create netcdf files, Otherwise this can be done after the data is reviewed. if result == QtWidgets.QMessageBox.Ok: d = {} for c in components: d[c.column_name] = c.toDictionary() handler.createNetCDF( cleaned_data.fixed, d, os.path.join(model.setupFolder, model.project + 'Setup.xml')) return # DataClass with data frame called 'fixed' and field 'datetime' # updates default component list, time range and time step values in the setup table and passes these values to the modelDialog # DataClass -> None def updateModelPage(self, data): # start and end dates get set written to database as default date ranges import pandas as pd def getDefaults(listDf, defaultStart=pd.to_datetime("1/1/1900").date(), defaultEnd=pd.datetime.today().date()): if len(listDf) > 0: s = listDf[0].index[0].date() e = listDf[0].index[len(listDf[0]) - 1].date() if (s < defaultStart) & (e > defaultEnd): return getDefaults(listDf[1:], s, e) elif s < defaultStart: return getDefaults(listDf[1:], s, defaultEnd) elif e > defaultStart: return getDefaults(listDf[1:], defaultStart, e) return str(defaultStart), str(defaultEnd) #default start is the first date there is record for defaultStart, defaultEnd = getDefaults(data.fixed) defaultComponents = ','.join(self.model.componentNames.value) #TODO this should be moved to the handler #TODO this should be moved to the handler self.dbHandler.cursor.execute( "UPDATE setup set date_start = ?, date_end = ?, component_names = ? where set_name = 'default'", [defaultStart, defaultEnd, defaultComponents]) self.dbHandler.connection.commit() # Deliver appropriate info to the ModelForm modelForm = self.window().findChild(SetsTableBlock) # start and end are tuples at this point modelForm.makeSetInfo(start=defaultStart, end=defaultEnd, components=defaultComponents) #deliver the data to the ResultsSetup form so it can be plotted resultsForm = self.window().findChild(ResultsSetup) resultsForm.setPlotData(data) # close event is triggered when the form is closed def closeEvent(self, event): #save xmls if 'projectFolder' in self.model.__dict__.keys(): self.sendSetupData() # on close save the xml files self.model.writeNewXML() self.dbHandler.closeDatabase #close the fileblocks for i in range(self.tabs.count()): page = self.tabs.widget(i) page.close() #TODO add progress bar for uploading raw data and generating fixed data pickle def addProgressBar(self): self.progress = QtWidgets.QProgressBar(self) self.progress.setObjectName('uploadBar') self.progress.setGeometry(100, 100, 100, 50) return self.progress # add a new file input tab def newTab(self, i=0): # get the set count tab_count = self.tabs.count() + 1 widg = FileBlock(self, tab_count) #widg.fillSetInfo() self.tabs.addTab(widg, 'Input' + str(tab_count)) #if its not the default empty tab fill data into form slots if i > 0: widg.fillData(self.model, i) # calls the specified function connected to a button onClick event @QtCore.pyqtSlot() def onClick(self, buttonFunction): buttonFunction() # ->QPushButton def createSubmitButton(self): button = QtWidgets.QPushButton() button.setText("Generate netCDF inputs") button.clicked.connect(self.generateNetcdf) return button #uses the current data object to generate input netcdfs def generateNetcdf(self): handler = UIToHandler() #df gets read in from TimeSeries processed data folder #component dictionary comes from setupXML's MainWindow = self.window() setupForm = MainWindow.findChild(QtWidgets.QWidget, 'setupDialog') setupModel = setupForm.model if 'setupFolder' in setupModel.__dict__.keys(): setupFile = os.path.join(setupModel.setupFolder, setupModel.project + 'Setup.xml') componentModel = setupForm.findChild(QtWidgets.QWidget, 'components').model() #From the setup file read the location of the input pickle #by replacing the current pickle with the loaded one the user can manually edit the input and # then return to working with the interface data = handler.loadInputData(setupFile) if data: df = data.fixed componentDict = {} if 'components' not in setupModel.__dict__.keys(): #generate components setupForm.makeComponentList(componentModel) for c in setupModel.components: componentDict[c.column_name] = c.toDictionary() #filesCreated is a list of netcdf files that were generated filesCreated = handler.createNetCDF(df, componentDict, setupModel.setupFolder) self.netCDFsLoaded.setText(', '.join(filesCreated)) else: print("no data found") #generate a list of Component objects based on attributes specified ModelSetupInformation # def getComponentsFromSetup(self): for i, c in enumerate(self.model.componentName.value): self.model.makeNewComponent(c, self.model.headerName.value[i], self.model.componentAttribute.unit[i], self.model.componentAttribute.value[i], None)
def initUI(self): self.dbHandler = ProjectSQLiteHandler() self.setObjectName("setupDialog") self.model = model #the main layout is oriented vertically windowLayout = QtWidgets.QVBoxLayout() # the top block is buttons to load setup xml and data files self.createButtonBlock() windowLayout.addWidget(self.ButtonBlock) self.tabs = Pages(self, '1', FileBlock) self.tabs.setDisabled(True) #each file type gets its own page to specify formats and headers to include # button to create a new set tab newTabButton = QtWidgets.QPushButton() newTabButton.setText(' + Input') newTabButton.setFixedWidth(100) newTabButton.clicked.connect(self.newTab) windowLayout.addWidget(newTabButton) windowLayout.addWidget(self.tabs, 3) #list of dictionaries containing information for wizard #this is the information that is not input file specific. dlist = [{ 'title': 'Dates to model', 'prompt': 'Enter the timespan you would like to include in the model.', 'sqltable': None, 'sqlfield': None, 'reftable': None, 'name': 'runTimesteps', 'folder': False, 'dates': True }, { 'title': 'Timestep', 'prompt': 'Enter desired timestep', 'sqltable': None, 'sqlfield': None, 'reftable': 'ref_time_units', 'name': 'timestep', 'folder': False }, { 'title': 'Project', 'prompt': 'Enter the name of your project', 'sqltable': None, 'sqlfield': None, 'reftable': None, 'name': 'project', 'folder': False }] self.WizardTree = self.buildWizardTree(dlist) self.createBottomButtonBlock() windowLayout.addWidget(self.BottomButtons) #set the main layout as the layout for the window self.setLayout(windowLayout) #title is setup self.setWindowTitle('Setup') self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) #show the form self.showMaximized()
def getItems(self): dbHandler = ProjectSQLiteHandler() items = dbHandler.getCodes(self.d['reftable']) dbHandler.closeDatabase() return items