(kept for reference) """ from CondDBUI import CondDB from PyCool import cool def ts(*args): from datetime import datetime epoch = datetime(1970, 1, 1) return int((datetime(*args) - epoch).total_seconds() * 1000000000) db = CondDB("sqlite_file:RSTEST.db/RSTEST", readOnly=False, create_new_db=True) db.createNode("/Conditions", storageType="NODE") db.createNode("/Conditions/Online", storageType="NODE") db.createNode("/Conditions/Online/LHCb", storageType="NODE") db.createNode("/Conditions/Online/LHCB/RunInfo", versionMode="SINGLE") db.createNode("/Conditions/Online/LHCb/RunStamp.xml", versionMode="SINGLE") rs = """<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE DDDB SYSTEM "conddb:/DTD/structure.dtd"> <DDDB> <condition name="RunStamp"> <param name="RunNumber" type="int">1</param> </condition> </DDDB> """ db.storeXMLString("/Conditions/Online/LHCb/RunStamp.xml", rs, ts(2015, 6, 9), ts(2015, 6, 10))
from CondDBUI import CondDB from PyCool import cool from datetime import datetime, timedelta def toTimeStamp(dt): t = dt - datetime(1970, 1, 1, 0) return (t.days * 60 * 60 * 24 + t.seconds) * 1000000000 db = CondDB("sqlite_file:DQFLAGS.db/DQFLAGS", readOnly=False, create_new_db=True) db.createNode("/Conditions", storageType="NODE") db.createNode("/Conditions/DQ", storageType="NODE") db.createNode("/Conditions/DQ/Flags", versionMode="SINGLE") def cond(d): data = ['<item key="%s" value="%d"/>' % i for i in d.items()] return """<?xml version='1.0' encoding='ISO-8859-1'?> <!DOCTYPE DDDB SYSTEM "conddb:/DTD/structure.dtd"> <DDDB> <condition name="Flags"> <map keytype="string" name="map" valuetype="int"> %s </map> </condition> </DDDB>
#!/usr/bin/env python """ Small script to generate the test database for the heart beat test. (kept for reference) """ from CondDBUI import CondDB from PyCool import cool base_time = 1262304000 # (2010, 1, 1, 0, 0, 0, 4, 1, 0) GMT unit = 60 db = CondDB("sqlite_file:HBTEST.db/HBTEST", readOnly = False, create_new_db = True) db.createNode("/Conditions", storageType = "NODE") db.createNode("/Conditions/Online", storageType = "NODE") db.createNode("/Conditions/Online/HeartBeatTest", storageType = "NODE") db.createNode("/Conditions/Online/HeartBeatTest/Condition1", versionMode = "SINGLE") db.createNode("/Conditions/Online/HeartBeatTest/Condition2", versionMode = "SINGLE") db.createNode("/Conditions/Online/HeartBeatTest/Tick", versionMode = "SINGLE") cond = """<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE DDDB SYSTEM "conddb:/DTD/structure.dtd"> <DDDB> <condition name = "Condition%d"> <param name = "Data" type = "int"> %d </param> </condition> </DDDB> """ db.storeXMLString("/Conditions/Online/HeartBeatTest/Condition1", cond % (1, 0), 0, cool.ValidityKeyMax)
class MainWindow(QMainWindow, Ui_MainWindow): ## Constructor. # Initialises the base class and defines some internal structures. def __init__(self, parent = None, flags = Qt.Widget): # Base class constructor. super(MainWindow, self).__init__(parent, flags) # Application and organization names app = QApplication.instance() self.appName = str(app.applicationName()) self.appOrg = str(app.organizationName()) # Preconfigured databases self.defaultDatabases = {} # action # Icons for the model (property) self._icons = None # Current selected (path, channel) in the database self._path = (None, None) # The database self.db = None # Current connection string. Needed because the copy in # self.db has the variables expanded. self._connectionString = None # Maximum number of entries in the list of recent databases self.maxRecentEntries = 10 # Whether to show the welcome message self._showWelcome = True # set in readSettings # External editor, to be passed to the add condition dialog self._externalEditor = "emacs" # set in readSettings # Prepare the GUI. self.setupUi(self) # --- Part of the initialization that require the GUI objects. --- # Window title self.setWindowTitle(self.appName) # Connect the models self.models = { "tree": CondDBStructureModel(), "nodes": CondDBNodesListModel(), "tags": CondDBTagsListModel(), "iovs": CondDBIoVModel(), "fields": CondDBPayloadFieldModel(), } setModelsIcons(self.icons) self.hierarchyTreeView.setModel(self.models["tree"]) self.pathComboBox.setModel(self.models["nodes"]) self.tagComboBox.setModel(self.models["tags"]) self.iovView.setModel(self.models["iovs"]) self.fieldsView.setModel(self.models["fields"]) for m in [self.models[n] for n in ["tree", "nodes", "iovs", "fields"]]: QObject.connect(self, SIGNAL("openedDB"), m.connectDB) QObject.connect(self, SIGNAL("openedDB"), tagsGlobalCache.setDB) QObject.connect(self.hierarchyTreeView.selectionModel(), SIGNAL("currentChanged(QModelIndex,QModelIndex)"), self.selectedItem) # special settings for the tags model tagsmodel = self.models["tags"] QObject.connect(self, SIGNAL("changedPath"), tagsmodel.setPath) QObject.connect(self.hideAutoCheckBox, SIGNAL("stateChanged(int)"), tagsmodel.setHideAutoTags) QObject.connect(tagsmodel, SIGNAL("setViewEnabled(bool)"), self.tagComboBox, SLOT("setEnabled(bool)")) QObject.connect(tagsmodel, SIGNAL("setViewEnabled(bool)"), self.hideAutoCheckBox, SLOT("setEnabled(bool)")) tagsmodel.setHideAutoTags(self.hideAutoCheckBox.checkState()) # connection for the iovModel iovsmodel = self.models["iovs"] QObject.connect(self, SIGNAL("changedPathChannel"), iovsmodel.setPathChannel) QObject.connect(self.sinceFilterWidget, SIGNAL("validityKeyChange"), iovsmodel.setSince) QObject.connect(self.untilFilterWidget, SIGNAL("validityKeyChange"), iovsmodel.setUntil) QObject.connect(self.tagComboBox, SIGNAL("currentIndexChanged(QString)"), iovsmodel.setTag) QObject.connect(iovsmodel, SIGNAL("setViewEnabled(bool)"), self.iovView, SLOT("setEnabled(bool)")) self.iovView.setEnabled(False) self.iovView.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) QObject.connect(self.iovView.selectionModel(), SIGNAL("currentChanged(QModelIndex,QModelIndex)"), iovsmodel.selectionChanged) QObject.connect(iovsmodel, SIGNAL("setCurrentIndex(QModelIndex,QItemSelectionModel::SelectionFlags)"), self.iovView.selectionModel(), SLOT("setCurrentIndex(QModelIndex,QItemSelectionModel::SelectionFlags)")) QObject.connect(self.iovUTCCheckBox, SIGNAL("stateChanged(int)"), iovsmodel.setShowUTC) iovsmodel.setShowUTC(self.iovUTCCheckBox.checkState()) # Use a consistent DisplayFormat iovsmodel.setDisplayFormat(self.sinceFilterWidget.displayFormat()) # connection for the fields model fieldsmodel = self.models["fields"] QObject.connect(self, SIGNAL("changedPath"), fieldsmodel.setPath) #QObject.connect(fieldsmodel, SIGNAL("setViewEnabled(bool)"), self.fieldsView, SLOT("setEnabled(bool)")) #self.fieldsView.setEnabled(False) QObject.connect(fieldsmodel, SIGNAL("setViewEnabled(bool)"), self.payloadGroupBox, SLOT("setVisible(bool)")) self.payloadGroupBox.setVisible(False) QObject.connect(self.fieldsView.selectionModel(), SIGNAL("currentChanged(QModelIndex,QModelIndex)"), fieldsmodel.selectionChanged) QObject.connect(fieldsmodel, SIGNAL("setCurrentIndex(QModelIndex,QItemSelectionModel::SelectionFlags)"), self.fieldsView.selectionModel(), SLOT("setCurrentIndex(QModelIndex,QItemSelectionModel::SelectionFlags)")) # Filter panel # Default startup values for the IOV filter. self.sinceFilterWidget.setMaxEnabled(False) self.sinceFilterWidget.setDateTime(QDateTime.currentDateTime().addMonths(-1)) #iovsmodel.setSince(self.sinceFilterWidget.toValidityKey()) self.untilFilterWidget.setMaxChecked(True) #iovsmodel.setUntil(self.untilFilterWidget.toValidityKey()) # When created, we have to ensure that everything is grayed out. self.emit(SIGNAL("databaseOpen(bool)"), False) self.menuEdit.setEnabled(False) self.menuAdvanced.setEnabled(False) # Triggers for the update of the central panel QObject.connect(self.iovView.selectionModel(), SIGNAL("currentChanged(QModelIndex,QModelIndex)"), self.showData) QObject.connect(self.fieldsView.selectionModel(), SIGNAL("currentChanged(QModelIndex,QModelIndex)"), self.showData) # Add the list of actions to show/hide the panels self.menuPanels.addAction(self.browsePanel.toggleViewAction()) self.menuPanels.addAction(self.filterPanel.toggleViewAction()) self.readSettings() if self._showWelcome: self._showWelcome = self.showWelcomeInfo(cancel = True) ## Store settings into the configuration file def writeSettings(self): settings = QSettings() settings.beginGroup("MainWindow") settings.setValue("Size", QVariant(self.size())) settings.setValue("Pos", QVariant(self.pos())) settings.endGroup() settings.beginGroup("BrowsePanel") settings.setValue("Visible", QVariant(self.browsePanel.isVisible())) settings.setValue("Floating", QVariant(self.browsePanel.isFloating())) settings.setValue("Size", QVariant(self.browsePanel.size())) settings.setValue("Pos", QVariant(self.browsePanel.pos())) settings.endGroup() settings.beginGroup("FilterPanel") settings.setValue("Visible", QVariant(self.filterPanel.isVisible())) settings.setValue("Floating", QVariant(self.filterPanel.isFloating())) settings.setValue("Size", QVariant(self.filterPanel.size())) settings.setValue("Pos", QVariant(self.filterPanel.pos())) settings.endGroup() settings.beginGroup("DataView") settings.setValue("FixedWidthFont", QVariant(self.dataView.isFixedWidthFont())) settings.endGroup() settings.beginGroup("FindDialog") d = self.dataView.findDialog settings.setValue("Visible", QVariant(d.isVisible())) settings.setValue("Pos", QVariant(d.pos())) settings.setValue("Flags", QVariant(d.getFindFlags())) settings.setValue("WrappedSearch", QVariant(d.getWrappedSearch())) settings.endGroup() settings.setValue("IOVs/UTC", QVariant(self.iovUTCCheckBox.isChecked())) settings.beginWriteArray("Recent") recents = self.menuRecent.actions() i = 0 for action in recents: settings.setArrayIndex(i) settings.setValue("ConnString", QVariant(action.text())) i += 1 settings.endArray() settings.beginGroup("Misc") settings.setValue("ShowWelcome", QVariant(self._showWelcome)) settings.setValue("ExternalEditor", QVariant(self._externalEditor)) settings.endGroup() ## Load settings from the configuration file def readSettings(self): settings = QSettings() settings.beginGroup("MainWindow") self.resize(settings.value("Size", QVariant(QSize(965, 655))).toSize()) self.move(settings.value("Pos", QVariant(QPoint(0, 0))).toPoint()) settings.endGroup() settings.beginGroup("BrowsePanel") self.browsePanel.setVisible(settings.value("Visible", QVariant(True)).toBool()) self.browsePanel.setFloating(settings.value("Floating", QVariant(False)).toBool()) self.browsePanel.resize(settings.value("Size", QVariant(QSize(250, 655))).toSize()) self.browsePanel.move(settings.value("Pos", QVariant(QPoint(0, 0))).toPoint()) settings.endGroup() settings.beginGroup("FilterPanel") self.filterPanel.setVisible(settings.value("Visible", QVariant(True)).toBool()) self.filterPanel.setFloating(settings.value("Floating", QVariant(False)).toBool()) self.filterPanel.resize(settings.value("Size", QVariant(QSize(270, 655))).toSize()) self.filterPanel.move(settings.value("Pos", QVariant(QPoint(0, 0))).toPoint()) settings.endGroup() settings.beginGroup("DataView") self.dataView.setFixedWidthFont(settings.value("FixedWidthFont", QVariant(False)).toBool()) settings.endGroup() settings.beginGroup("FindDialog") d = self.dataView.findDialog d.setVisible(settings.value("Visible", QVariant(False)).toBool()) d.move(settings.value("Pos", QVariant(QPoint(0, 0))).toPoint()) # Note: QVariant.toInt returns a tuple with the result of the conversion # and a boolean for the successful conversion d.setFindFlags(settings.value("Flags", QVariant(0)).toInt()[0]) d.setWrappedSearch(settings.value("WrappedSearch", QVariant(True)).toBool()) settings.endGroup() self.iovUTCCheckBox.setChecked(settings.value("IOVs/UTC", QVariant(True)).toBool()) size = settings.beginReadArray("Recent") for i in range(size): settings.setArrayIndex(i) conn = settings.value("ConnString").toString() action = QAction(self) action.setText(conn) QObject.connect(action, SIGNAL("triggered()"), self.openRecentDatabase) self.menuRecent.addAction(action) settings.endArray() settings.beginGroup("Misc") self._showWelcome = settings.value("ShowWelcome", QVariant(True)).toBool() self._externalEditor = str(settings.value("ExternalEditor", QVariant("emacs")).toString()) settings.endGroup() ## Close Event handler def closeEvent(self, _event): self.writeSettings() ## Fills the menu of standard databases from the connString dictionary. # @see getStandardConnectionStrings() def setDefaultDatabases(self, connStrings): self.menuStandard.clear() self.defaultDatabases = {} names = connStrings.keys() names.sort() for name in names: conn = connStrings[name] action = QAction(self) action.setText(name) action.setData(QVariant(conn)) action.setStatusTip(conn) QObject.connect(action, SIGNAL("triggered()"), self.openStandardDatabase) self.menuStandard.addAction(action) self.defaultDatabases[name] = action self.menuStandard.setEnabled(not self.menuStandard.isEmpty()) ## Slot called by the actions in the menu "Database->Standard". # It can also be called passing the name of one of those databases. def openStandardDatabase(self, name = None, readOnly = True): if name is None: sender = self.sender() else: if name in self.defaultDatabases: sender = self.defaultDatabases[name] else: QMessageBox.critical(self, "Database nickname not known", "The conditions database '%s' is not in the list of known database." % name) return # Open the database using the connection string in the action self.openDatabase(str(sender.data().toString()), readOnly = readOnly) ## Slot called by the actions in the menu "Database->Recent" def openRecentDatabase(self): sender = self.sender() self.openDatabase(str(sender.text())) ## Open the "create new database" dialog box def newDatabaseDialog(self): dd = NewDatabaseDialog(self) if dd.exec_(): connString = str(dd.connectionString()) # Create an empty database CondDB(connString, readOnly = False, create_new_db = True) # and open it in read/write mode self.openDatabase(connString, readOnly = False) ## Open the "open database" dialog box def openDatabaseDialog(self): dd = OpenDatabaseDialog(self) if dd.exec_(): connString = str(dd.connectionString()) self.openDatabase(connString, dd.readOnly()) ## Add a connection string to the list of the recent opened databases def _addToRecents(self, connString): # do nothing if there is no connection string if not connString: return # action to add to the menu action = None # check if the connection string is already in the list for oldAction in self.menuRecent.actions(): if oldAction.text() == connString: # if it is found, remove it from the list and use it as action to add action = oldAction self.menuRecent.removeAction(action) break # if the action was not found in the menu, create a new one if action is None: action = QAction(self) action.setText(connString) QObject.connect(action, SIGNAL("triggered()"), self.openRecentDatabase) # if the menu is not empty if self.menuRecent.actions(): # add the action at the beginning of the list self.menuRecent.insertAction(self.menuRecent.actions()[0], action) # and remove the entries exceeding the maximum while len(self.menuRecent.actions()) > self.maxRecentEntries: self.menuRecent.removeAction(self.menuRecent.actions()[-1]) else: # otherwise just add the action self.menuRecent.addAction(action) ## Open the database identified by a connection string or by a nickname. # If name is an empty string (or None) the result is a disconnection from the # current database. # # @param connString connection string # @param readOnly flag to select if the database has to be opened in read-only mode or in read/write mode def openDatabase(self, connString, readOnly = True): try: # Clean the central view to avoid that old data stays there self.dataView.clear() if connString: try: self.db = CondDB(connString, readOnly = readOnly) except Exception as x: from functools import reduce msg = reduce(lambda x,y: x + y, list(x.args), '') if msg.startswith("Database not found:"): QMessageBox.critical(self, "Cannot open database", msg) return raise title = "%s - %s" % (connString, self.appName) # Notify the widgets that the database is open self.emit(SIGNAL("databaseOpen(bool)"), True) # Notify the widgets if the database is open in read-only mode editable = not readOnly self.emit(SIGNAL("databaseOpenReadOnly(bool)"), bool(readOnly)) self.menuEdit.setEnabled(editable) self.menuAdvanced.setEnabled(editable) if editable: # The "Delete Node" entry should be active only # when a node is selected (when opening a new db there is # no selection) self.actionDelete_node.setEnabled(False) # ... same for the "new tag" and "delete tag" self.actionNew_tag.setEnabled(False) self.actionDelete_tag.setEnabled(False) else: self.db = None title = self.appName # Notify the widgets that there is no database open self.emit(SIGNAL("databaseOpen(bool)"), False) self.menuEdit.setEnabled(False) self.menuAdvanced.setEnabled(False) self.setWindowTitle(title) self._path = None # remember the used connection string self._connectionString = connString self._addToRecents(connString) # update the DB instance of the models self.emit(SIGNAL("openedDB"), self.db) except: self.exceptionDialog() ## Re-open the current database, changing the readOnly flag. # If the database is already in the correct mode, nothing is done. def reopenDatabase(self, readOnly): path = self._path if readOnly != self.db.readOnly: self.openDatabase(self._connectionString, readOnly) if path: self._selectPath(path[0]) ## Disconnect from the database. # @see: openDatabase() def closeDatabase(self): self.openDatabase(None) ## Display the standard Qt information dialog box def aboutQt(self): QMessageBox.aboutQt(self) ## Display the application information dialog box def aboutDialog(self): app = QApplication.instance() message = '''<p><b>%s</b><br/>%s</p> <p>Browser for the LHCb-COOL condition database.</p> <p>This is build on top of the Python API to LHCb-COOL: CondDBUI, and the Python API to COOL: PyCool.</p> <p>The Graphical Library is PyQt %s, based on Qt %s</p> <p><i>Marco Clemencic, Nicolas Gilardi</i></p>''' \ % (app.objectName(), app.applicationVersion(), PYQT_VERSION_STR, qVersion()) QMessageBox.about(self, app.objectName(), message) ## Slots to react to a selected item in the structure view def selectedItem(self, index): item = index.internalPointer() if self._path != (item.path, item.channel): try: i = self.models["nodes"].nodes.index(item.path) self._path = (item.path, item.channel) self.emit(SIGNAL("changedPath"), item.path) if item.leaf and not item.children: self.emit(SIGNAL("changedPathChannel"), item.path, item.channel) else: self.emit(SIGNAL("changedPathChannel"), None, None) # Nodes can be deleted only if the database is in r/w mode and # they are Folders or empty FolderSets self.actionDelete_node.setEnabled(not self.db.readOnly and (item.leaf or not item.children)) # Tags manipulation should be enabled only for FolderSets # and multi-version folders taggable = (not self.db.readOnly and ((not item.leaf) or (not item.singleVersion))) self.actionNew_tag.setEnabled(taggable) self.actionDelete_tag.setEnabled(taggable) except ValueError: i = -1 self.pathComboBox.setCurrentIndex(i) ## Slots to react to a selected entry in the path combo box def selectedPath(self, path): path = str(path) if self._path != (path, None) and path: index = self.models["tree"].findPath(path) self.hierarchyTreeView.setCurrentIndex(index) self._path = (path, None) item = index.internalPointer() self.emit(SIGNAL("changedPath"), item.path) if item.leaf and not item.children: self.emit(SIGNAL("changedPathChannel"), path, None) else: self.emit(SIGNAL("changedPathChannel"), None, None) # Nodes can be deleted only if they are Folders or empty FolderSets self.actionDelete_node.setEnabled(item.leaf or not item.children) ## Return the cool::FolderSet object currently selected in the structure tree. # If the selected item is not a FolderSet, the parent is returned. # If there is not database opened, returns None. def getSelectedFolderSet(self): if not self.db: return None # Get the FolderSet currently selected in the tree view ("/" if none) idx = self.hierarchyTreeView.currentIndex() while idx.isValid() and idx.internalPointer().leaf: idx = idx.internalPointer().parent.index if idx.isValid(): currentFolderSet = idx.internalPointer().node else: currentFolderSet = self.models["tree"].root.node return currentFolderSet def testSlot(self): print "Test slot triggered" @property def icons(self): if self._icons is None: self._icons = {} style = QApplication.instance().style() for iconName, iconId in [ ("folderset", QStyle.SP_DirIcon), ("folder", QStyle.SP_FileIcon), ("up", QStyle.SP_ArrowUp), ("down", QStyle.SP_ArrowDown), ]: self._icons[iconName] = style.standardIcon(iconId) return self._icons ## This function gets called if any selection changes and decides if there # is something to show in the central widget. def showData(self): data = "" payload = self.models["iovs"].getPayload() if payload: field = self.models["fields"].getFieldName() # It may happen (it actually often does) that the function is # called for a change in the field before the IoVs are updated, so # it is better to check if the field exists in the payload. if field in payload: data = payload[field] self.dataView.setPlainText(data) ## Show a critical dialog with the latest exception traceback. # @param source string representing the function that trapped the exception. def exceptionDialog(self, source = None): import traceback if source: msg = "Called from '%s':\n" % source else: msg = "" msg += traceback.format_exc() print msg QMessageBox.critical(self, "Exception in Python code", msg) ## Helper function useful to force a refresh of the models and the selection # of node if specified. def _refreshModels(self, selectPath = None): # trigger a refresh of the caches in the models self.emit(SIGNAL("openedDB"), self.db) self._selectPath(selectPath) ## Helper function to select a path in both the combo box and the hierarchy # view. def _selectPath(self, selectPath = None): if selectPath: # select the specified path i = self.pathComboBox.findText(selectPath) self.pathComboBox.setCurrentIndex(i) self.selectedPath(selectPath) ## Add a new node to the database def newNodeDialog(self): fs = self.getSelectedFolderSet() fsname = str(fs.fullPath()) if fsname != "/": fsname += "/" d = NewNodeDialog(self) folders = set() foldersets = set() for n in self.models["nodes"].nodes: if self.db.db.existsFolder(n): folders.add(n) else: foldersets.add(n) d.setNodes(folders, foldersets) d.setText(fsname) if d.exec_(): if d.getType() == "FolderSet": storageType = "NODE" storageKeys = None # ignored versionMode = None # ignored else: storageType = "XML" # actually it is not used storageKeys = {} for k,v in d.fields(): storageKeys[str(k)] = v versionMode = {"MultiVersion": "MULTI", "SingleVersion": "SIGLE"}[d.getType()] path = str(d.text()) self.db.createNode(path = path, description = str(d.description()), storageType = storageType, versionMode = versionMode, storageKeys = storageKeys) # Now that the node has been created, we have to notify the models. # FIXME: this is the easiest solution to implement, but not optimal self._refreshModels(path) ## Ask for confirmation to delete the selected node and delete it if ok def deleteNode(self): path = str(self.pathComboBox.currentText()) if path: message = """<p>Are you sure you want to delete the node?</p> <p><center><tt>%s</tt><center></p> <p><font color="red">The action cannot be undone.</font></p>""" % path answer = QMessageBox.question(self, "Delete node?", message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if answer == QMessageBox.Yes: try: self.db.deleteNode(path) except: self.exceptionDialog() # Now that the node has been deleted, we have to notify the models. # FIXME: this is the easiest solution to implement, but not optimal self._refreshModels(parentpath(path)) ## Helper to show a dialog about def _unimplemented(self): QMessageBox.information(self, "Unimplemented", "Sorry, the function you selected is not implemented yet.") ## Dump a snapshot of the current database to files def dumpToFiles(self): d = DumpToFilesDialog(self) if d.exec_(): from CondDBUI.Admin import DumpToFiles monitor = ProgressDialog(self) monitor.setWindowModality(Qt.WindowModal) monitor.setWindowTitle("Dumping to files") monitor.setMinimumDuration(0) try: DumpToFiles(database = self.db, time = d.pointInTime.toValidityKey(), tag = str(d.tag.currentText()), srcs = ['/'], destroot = str(d.destDir.text()), force = d.overwrite.isChecked(), addext = False, monitor = monitor) if monitor.wasCanceled(): monitor.reset() QMessageBox.information(self, "Dump to files canceled", "The dump has been canceled.") except: monitor.reset() self.exceptionDialog() ## Create a slice of the current database to a database def createSlice(self): d = CreateSliceDialog(self) partName = str(self.db.db.databaseName()) i = d.partition.findText(partName) if i >= 0: d.partition.setCurrentIndex(i) else: d.partition.lineEdit().setText(partName) if d.exec_(): try: selectionList = [PyCoolCopy.Selection(path, since, until, tags = tags) for path, since, until, tags in d.selectionsModel.selections] connStr = str(d.connectionString()) copyTool = PyCoolCopy.PyCoolCopy(self.db.db) # reduce the verbosity of PyCoolCopy PyCoolCopy.log.setLevel(PyCoolCopy.logging.WARNING) from Utils import BusyCursor _bc = BusyCursor() if d.append.isChecked(): copyTool.append(connStr, selectionList) else: # FIXME : https://sft.its.cern.ch/jira/browse/ROOT-5603 targetDb = CondDB(connStr,create_new_db=True,readOnly=False).db copyTool.copy(targetDb, selectionList) except: self.exceptionDialog() ## Add a new condition to the selected folder+channel def addCondition(self): d = AddConditionDialog(self, externalEditor = self._externalEditor) d.setShowUTC(self.iovUTCCheckBox.checkState()) folder, channel = self._path d.setLocation(folder, channel) payload = self.models["iovs"].getPayload() if payload: d.buffer = payload if d.exec_(): try: from Utils import BusyCursor _bc = BusyCursor() data = [] for cond in d.conditionsStack: data.append({"since": cond.since, "until": cond.until, "channel": int(cond.channel), "payload": cond.data}) folder = d.getFolder() self.db.storeXMLStringList(folder, data) self._refreshModels(folder) except: self.exceptionDialog() ## Create a new tag def newTag(self): path = self._path[0] d = NewTagDialog(self.db, path) if d.exec_(): from Utils import BusyCursor _bc = BusyCursor() tag = str(d.tag.text()) if self.db.db.existsFolder(path): self.db.recursiveTag(path, tag) else: tags = {} if path == "/": p = "" else: p = path for n in d.childTagsModel: tags[p + "/" + n] = d.childTagsModel[n] self.db.moveTagOnNodes(path, tag, tags) self._refreshModels(path) ## Delete a tag def deleteTag(self): path = self._path[0] d = SelectTagDialog(path, self) d.tagLabel.setText("&Tag to delete") tag = d.run() if tag is not None: tag = str(tag) message = """<p>Are you sure you want to delete the tag</p> <p><center><tt>%s</tt><center></p> <p>from the node</p> <p><center><tt>%s</tt><center></p> <p><font color="red">The action cannot be undone.</font></p>""" % (tag, path) answer = QMessageBox.question(self, "Delete tag?", message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if answer == QMessageBox.Yes: try: self.db.deleteTag(path, tag) except: self.exceptionDialog() # Now that the node has been deleted, we have to notify the models. # FIXME: this is the easiest solution to implement, but not optimal self._refreshModels(path) ## Display context menu for the tree view def showHierarchyContextMenu(self, position): index = self.hierarchyTreeView.indexAt(position) if index.isValid(): menu = QMenu(self) item = index.internalPointer() if item.leaf: menu.addAction(self.actionAdd_condition) else: menu.addAction(self.actionNew_node) menu.addAction(self.actionNew_tag) menu.addSeparator() menu.addAction(self.actionCopy_path) menu.addSeparator() menu.addAction(self.actionDelete_node) menu.addAction(self.actionDelete_tag) menu.exec_(self.hierarchyTreeView.mapToGlobal(position)) ## Copy the current selected path to the clipboard. def copyPathToClipboard(self): QApplication.clipboard().setText(self._path[0]) ## Copy the current connection string to the clipboard. def copyConnStrToClipboard(self): QApplication.clipboard().setText(self._connectionString) ## Display the initial info message to notify that this is the new version # of the CondDBBrowser. def showWelcomeInfo(self, cancel = False): mb = QMessageBox(self) mb.setWindowTitle("Welcome to the CondDBBrowser") mb.setText("""<html><body> <p>Welcome to the Qt4-based version of the CondDBBrowser.</p> <p>This is a complete rewrite of the old application, which includes a lot of improvements and clean up.</p> <p>Since it is completely new, some features may not work. In that case you can still use the old version invoking <tt>CondDBBrowserOld.py</tt></p> <p>Among the improvements you can find: <ul> <li>based on Qt4</li> <li>better responsiveness</li> <li>contextual menus</li> <li>improved managing of recent and standard databases</li> <li>extensive usage of tooltips</li> </ul></p> <p>If you find a bug post it to <a href="https://savannah.cern.ch/bugs/?group=lhcbcore">savannah</a>, for comments and suggestions send an email to <a href="mailto:[email protected]">Marco Clemencic</a>.</p> </body></html>""") ok = mb.addButton(QMessageBox.Ok) if cancel: mb.addButton(QMessageBox.Cancel) mb.exec_() return mb.clickedButton() != ok