def __init__(self, szprefix, no_warn=False, parent=None): """Filler 2 class constructor.""" QWidget.__init__(self, parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # check single instance try: self.single_instance = SingleInstance(LOCKFILE) except IOError: logger.error('another instance of Filler 2 is running, exiting...') self.criticalError(_translate("MainWindow", "Cannot acquire a single instance lock. There is " "another Filler 2 program running on the system.")) # parse local SZARP configuration try: self.parser = IPKParser(szprefix) except IOError as err: logger.error("cannot read SZARP configuration") logger.error(str(err)) self.criticalError(_translate("MainWindow", "Cannot read SZARP configuration") + " (%s)." % err.bad_path) sys.exit(1) except ValueError as err: logger.error(str(err)) self.criticalError(_translate("MainWindow", "Non-valid SZARP database prefix.")) sys.exit(1) logger.info("editing database of prefix '%s'", self.parser.ipk_prefix) # verify SZARP prefix versus hostname if no_warn == False: try: process = subprocess.Popen( ["/bin/hostname", "-s"], stdout=subprocess.PIPE) out, err = process.communicate() if err == None: hostname = out[:-1] if hostname != self.parser.ipk_prefix: self.warningBox(_translate("MainWindow", "Warning! Database prefix differs from hostname, " "you are probably modifying non-local database.")) except OSError: pass ### initialize Qt4 widgets ## # name of the local configuration self.ui.titleLabel.setText(self.parser.getTitle()) # list of parameter sets self.ui.listOfSets.addItem( _translate("MainWindow", "--- Choose a set of parameters ---")) self.ui.listOfSets.addItems(self.parser.getSets()) self.ui.listOfSets.model().setData( self.ui.listOfSets.model().index(0,0), QVariant(0), Qt.UserRole-1) self.ui.listOfSets.setEnabled(True) # parameter's type combobox self.dialog_factory = ValueDialogFactory() for name, icon, desc in self.dialog_factory.get_dialogs(): self.ui.valueType.addItem(icon, desc, QVariant((name, desc))) self.ui.valueType.model().setData( self.ui.valueType.model().index(0,0), QVariant(0), Qt.UserRole-1) # date interval variables self.fromDate = None self.toDate = None # table of changes base = 64 self.ui.changesTable.setColumnCount(6) self.ui.changesTable.setColumnWidth(0, 6*base-24) # param's draw_name self.ui.changesTable.setColumnWidth(1, 3*base) # "from" date self.ui.changesTable.setColumnWidth(2, 3*base) # "to" date self.ui.changesTable.setColumnWidth(3, 3*base) # param's value type self.ui.changesTable.setColumnWidth(4, base-20) # remove entry button self.ui.changesTable.setColumnHidden(5, True) # param's full name self.ui.changesTable.horizontalHeader().setVisible(False) self.ui.changesTable.setRowCount(0)
class Filler2(QMainWindow): """SZARP Filler 2 application's main window (pyQt4).""" def __init__(self, szprefix, no_warn=False, parent=None): """Filler 2 class constructor.""" QWidget.__init__(self, parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) # check single instance try: self.single_instance = SingleInstance(LOCKFILE) except IOError: logger.error('another instance of Filler 2 is running, exiting...') self.criticalError(_translate("MainWindow", "Cannot acquire a single instance lock. There is " "another Filler 2 program running on the system.")) # parse local SZARP configuration try: self.parser = IPKParser(szprefix) except IOError as err: logger.error("cannot read SZARP configuration") logger.error(str(err)) self.criticalError(_translate("MainWindow", "Cannot read SZARP configuration") + " (%s)." % err.bad_path) sys.exit(1) except ValueError as err: logger.error(str(err)) self.criticalError(_translate("MainWindow", "Non-valid SZARP database prefix.")) sys.exit(1) logger.info("editing database of prefix '%s'", self.parser.ipk_prefix) # verify SZARP prefix versus hostname if no_warn == False: try: process = subprocess.Popen( ["/bin/hostname", "-s"], stdout=subprocess.PIPE) out, err = process.communicate() if err == None: hostname = out[:-1] if hostname != self.parser.ipk_prefix: self.warningBox(_translate("MainWindow", "Warning! Database prefix differs from hostname, " "you are probably modifying non-local database.")) except OSError: pass ### initialize Qt4 widgets ## # name of the local configuration self.ui.titleLabel.setText(self.parser.getTitle()) # list of parameter sets self.ui.listOfSets.addItem( _translate("MainWindow", "--- Choose a set of parameters ---")) self.ui.listOfSets.addItems(self.parser.getSets()) self.ui.listOfSets.model().setData( self.ui.listOfSets.model().index(0,0), QVariant(0), Qt.UserRole-1) self.ui.listOfSets.setEnabled(True) # parameter's type combobox self.dialog_factory = ValueDialogFactory() for name, icon, desc in self.dialog_factory.get_dialogs(): self.ui.valueType.addItem(icon, desc, QVariant((name, desc))) self.ui.valueType.model().setData( self.ui.valueType.model().index(0,0), QVariant(0), Qt.UserRole-1) # date interval variables self.fromDate = None self.toDate = None # table of changes base = 64 self.ui.changesTable.setColumnCount(6) self.ui.changesTable.setColumnWidth(0, 6*base-24) # param's draw_name self.ui.changesTable.setColumnWidth(1, 3*base) # "from" date self.ui.changesTable.setColumnWidth(2, 3*base) # "to" date self.ui.changesTable.setColumnWidth(3, 3*base) # param's value type self.ui.changesTable.setColumnWidth(4, base-20) # remove entry button self.ui.changesTable.setColumnHidden(5, True) # param's full name self.ui.changesTable.horizontalHeader().setVisible(False) self.ui.changesTable.setRowCount(0) # end of __init__() def criticalError(self, msg, title = None): """Display critical error dialog and terminate. Arguments: msg - text message to be displayed. title - optional dialog title. """ if title is None: title = "SZARP Filler 2 - " + \ _translate("MainWindow", "Critical Error") QMessageBox.critical(self, title, msg) sys.exit(1) # end of criticalError() def warningBox(self, msg, title = None): """Display warning dialog. Arguments: msg - text message to be displayed. title - optional dialog title. """ if title is None: title = "SZARP Filler 2 - " + _translate("MainWindow", "Warning") QMessageBox.warning(self, title, msg) # end of warningBox() def infoBox(self, msg, title = None): """Display information dialog. Arguments: msg - text message to be displayed. title - optional dialog title. """ if title is None: title = "SZARP Filler 2 - " + _translate("MainWindow", "Information") QMessageBox.information(self, title, msg) # end of infoBox() def questionBox(self, msg, title = None): """Display question dialog. Arguments: msg - text message to be displayed. title - optional dialog title. Returns: QMessageBox.Yes | QMessageBox.No - user's answer. """ if title is None: title = "SZARP Filler 2 - " + _translate("MainWindow", "Question") return QMessageBox.question(self, title, msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) # end of questionBox() def onSetChosen(self, text): """Slot for signal activated(QString) from 'listOfSets' (QComboBox). Arguments: text - name of the chosen set. """ self.ui.paramList.clear() self.ui.paramList.addItem( _translate("MainWindow", "--- Choose a parameter ---")) self.ui.paramList.model().setData( self.ui.paramList.model().index(0,0), QVariant(0), Qt.UserRole-1) # insert a list of parameters from given set for p in self.parser.getParams(unicode(text)): self.ui.paramList.addItem(p.draw_name, p) # activate/reset other ui elements self.ui.paramList.setEnabled(True) self.ui.paramList.setFocus() self.ui.valueType.setCurrentIndex(0) self.ui.valueType.setEnabled(False) self.ui.addButton.setEnabled(False) # end of onSetChosen() def onParamChosen(self, index): """Slot for signal activated(QString) from 'paramList' (QComboBox). Arguments: text - name of chosen parameter (not used). """ # activate other ui elements self.ui.fromDate.setEnabled(True) self.ui.toDate.setEnabled(True) self.ui.valueType.setEnabled(True) self.type_dialog = None self.validateInput() # end of onParamChosen() def onFromDate(self): """Slot for signal clicked() from 'fromDate' (QPushButton). Displays dialog for choosing date and time. """ if self.fromDate is None: if self.toDate is None: dlg = DatetimeDialog_impl() else: dlg = DatetimeDialog_impl(start_date= (self.toDate - datetime.timedelta(minutes=10))) else: if self.toDate is None or self.fromDate < self.toDate: dlg = DatetimeDialog_impl(start_date=self.fromDate) else: dlg = DatetimeDialog_impl(start_date= (self.toDate - datetime.timedelta(minutes=10))) # execute DatetimeDialog if dlg.exec_(): self.fromDate = dlg.getValue() self.ui.fromDate.setText(_translate("MainWindow", "From:") + " " + self.fromDate.strftime('%Y-%m-%d %H:%M')) self.validateInput() # end of onFromDate() def onToDate(self): """Slot for signal clicked() from 'toDate' (QPushButton). Displays dialog for choosing date and time. """ if self.toDate is None: if self.fromDate is None: dlg = DatetimeDialog_impl() else: dlg = DatetimeDialog_impl(start_date= (self.fromDate + datetime.timedelta(minutes=10))) else: if self.fromDate is None or self.toDate > self.fromDate: dlg = DatetimeDialog_impl(start_date=self.toDate) else: dlg = DatetimeDialog_impl(start_date= (self.fromDate + datetime.timedelta(minutes=10))) # execute DatetimeDialog if dlg.exec_(): self.toDate = dlg.getValue() self.ui.toDate.setText(_translate("MainWindow", "To:") + " " + self.toDate.strftime('%Y-%m-%d %H:%M')) self.validateInput() # end of onToDate() def onTypeChosen(self, index): """Slot for signal activated() from 'valueType' (QComboBox). Constructs appropriate dialog from factory and collects chosen plot parameters. """ # fetch dialog and parameter data dlg_name = self.ui.valueType.itemData(index).toPyObject()[0] param_info = self.ui.paramList.itemData(self.ui.paramList.currentIndex()).toPyObject() dlg = self.dialog_factory.construct(str(dlg_name), param_info.prec, param_info.lswmsw, parent = self) # reset type items' texts for i in range(self.ui.valueType.count()): desc = self.ui.valueType.itemData(i).toPyObject()[1] self.ui.valueType.setItemText(i, desc) if dlg.exec_(): self.type_dialog = dlg self.ui.valueType.setItemText(index, dlg.get_value_desc()) else: self.ui.valueType.setCurrentIndex(0) self.type_dialog = None self.validateInput() # end of onTypeChosen() def validateInput(self): """Check whether all needed data is filled and valid. If do, "Add" button is activated. """ if self.ui.paramList.currentIndex() != 0 and \ self.fromDate is not None and \ self.toDate is not None and \ self.ui.valueType.currentIndex() != 0: self.ui.addButton.setEnabled(True) else: self.ui.addButton.setEnabled(False) # end of validateInput() def aboutQt(self): """Display about Qt4 dialog.""" QMessageBox.aboutQt(self) def about(self): """Display about Filler 2 dialog.""" AboutDialog_impl().exec_() def addChange(self): """Slot for signal clicked() from addButton (QPushButton). Adds change-entry to changesTable (QTableWidget).""" if self.fromDate >= self.toDate: self.warningBox(_translate("MainWindow", "\"To\" date is earlier (or equals) \"From\" date.\nAdding change aborted.")) return if self.toDate >= datetime.datetime.now(): if QMessageBox.Yes != \ self.questionBox(_translate("MainWindow", "You are trying to modify future data - " "it will block SZARP from writing actual data in this period.\n\n" "Are you sure that's what you really want to do?")): return param_info = self.ui.paramList.itemData( self.ui.paramList.currentIndex()).toPyObject() self.ui.changesTable.setRowCount(self.ui.changesTable.rowCount()+1) self.addRow(self.ui.changesTable.rowCount() - 1, param_info.name, self.ui.paramList.currentText(), self.fromDate, self.toDate, self.type_dialog, param_info.lswmsw) # reset type items' texts for i in range(self.ui.valueType.count()): desc = self.ui.valueType.itemData(i).toPyObject()[1] self.ui.valueType.setItemText(i, desc) self.ui.valueType.setCurrentIndex(0) # end of addChange() def addRow(self, row, fname, pname, from_date, to_date, typedlg, lswmsw): """Add row to changesTable (QTableWidget). Arguments: row - number of row to be set. fname - full parameter name pname - parameter's draw name from_date - beginning of time period to_date - end of time period value - parameter's value typedlg - object of input value dialog lswmsw - whether parametr is a combined lsw/msw """ # visible columns item_pname = QTableWidgetItem(unicode(pname)) item_pname.setFlags(Qt.ItemIsEnabled) item_from_date = QTableWidgetItem(from_date.strftime('%Y-%m-%d %H:%M')) item_from_date.setFlags(Qt.ItemIsEnabled) item_from_date.setTextAlignment(Qt.AlignCenter) item_to_date = QTableWidgetItem(to_date.strftime('%Y-%m-%d %H:%M')) item_to_date.setFlags(Qt.ItemIsEnabled) item_to_date.setTextAlignment(Qt.AlignCenter) item_value = QTableWidgetItem(typedlg.get_value_desc()) item_value.setFlags(Qt.ItemIsEnabled) item_value.setIcon(QIcon(typedlg.qicon_path)) item_value.setTextAlignment(Qt.AlignCenter) # hidden column item_fname = QTableWidgetItem(unicode(fname)) item_fname.setData(Qt.UserRole, QVariant((lswmsw, typedlg))) item_fname.setFlags(Qt.ItemIsEnabled) self.ui.changesTable.setItem(row, 0, item_pname) self.ui.changesTable.setItem(row, 1, item_from_date) self.ui.changesTable.setItem(row, 2, item_to_date) self.ui.changesTable.setItem(row, 3, item_value) self.ui.changesTable.setItem(row, 5, item_fname) # "remove" button widget rm_button = QPushButton(QIcon.fromTheme("window-close"), "") rm_button.setToolTip(_translate("MainWindow", "Remove entry")) rm_button.row_id = row QObject.connect(rm_button, SIGNAL("clicked()"), self.removeChange) self.ui.changesTable.setCellWidget(row, 4, rm_button) # end of addRow() def removeChange(self): """Slot for signal clicked() from rm_button (QPushButton). Removes entry from changesTable (QTableWidget). """ if QMessageBox.Yes != \ self.questionBox(_translate("MainWindow", "Remove change?")): return row = self.sender().row_id self.ui.changesTable.removeRow(row) # update row_id for every row for i in range(row, self.ui.changesTable.rowCount()): self.ui.changesTable.cellWidget(i, 4).row_id -= 1 # end of removeChange() def clearChanges(self): """Slot for action 'actionClear'. Removes all entries from changesTable (QTableWidget). """ txt = _translate("MainWindow", "Are you sure you want to clear all changes?") if QMessageBox.Yes == self.questionBox(txt): self.ui.changesTable.setRowCount(0) # end of clearChanges() def commitChanges(self): """Slot for action 'actionSaveData'. Commits all scheduled changes to local szbase. """ if self.ui.changesTable.rowCount() == 0: self.warningBox(_translate("MainWindow", "No changes to commit.")) return # list and confirm txt = _translate("MainWindow", "Following parameters will be modified:") + "\n\n" for i in range(0, self.ui.changesTable.rowCount()): txt.append(" * %s\n\n" \ % (self.ui.changesTable.item(i,0).text())) txt.append(_translate("MainWindow", "Commit changes?")) if QMessageBox.Yes == self.questionBox(txt): # construct list of changes to be committed logger.info("committing scheduled changes...") changes_list = [] for i in range(0, self.ui.changesTable.rowCount()): start_date = datetime.datetime.strptime( str(self.ui.changesTable.item(i,1).text()), '%Y-%m-%d %H:%M') end_date = datetime.datetime.strptime( str(self.ui.changesTable.item(i,2).text()), '%Y-%m-%d %H:%M') lswmsw, typedlg = \ self.ui.changesTable.item(i,5).data(Qt.UserRole).toPyObject() # generate list of 10-minute probes in given interval dsec = int((end_date-start_date).total_seconds()) dts = [start_date + datetime.timedelta(minutes=x) \ for x in range(0, dsec / 60 + 1, 10)] # generate probes' values dvals = typedlg.generate(dts) rmrk = typedlg.get_remark() changes_list.append(SzChangeInfo( draw_name = unicode(self.ui.changesTable.item(i,0).text()), name = unicode(self.ui.changesTable.item(i,5).text()), dvalues = dvals, lswmsw = lswmsw, remark = rmrk )) # do the job (in a new thread) szbw = SzbWriter(changes_list, self.parser) szbp = SzbProgressWin(szbw, parent = self) # reset all GUI elements self.ui.changesTable.setRowCount(0) self.fromDate = None self.toDate = None self.ui.listOfSets.setCurrentIndex(0) self.ui.paramList.clear() self.ui.paramList.setEnabled(False) self.ui.fromDate.setText(_translate("MainWindow", "From:")) self.ui.fromDate.setEnabled(False) self.ui.toDate.setText(_translate("MainWindow", "To:")) self.ui.toDate.setEnabled(False) self.ui.valueType.setCurrentIndex(0) self.ui.valueType.setEnabled(False) self.ui.addButton.setEnabled(False) # disable main window until job is finished self.setEnabled(False) # end of commitChanges() def contextHelp(self): """Slot for action 'actionContextHelp'. Activates "What is that?" mode.""" QWhatsThis.enterWhatsThisMode() def onViewHistory(self): """Slot for action 'actionViewHistory'. Shows history of committed changes.""" HistoryDialog_impl(self.parser, parent=self).exec_()