def __changeDirectory(self, path): for c in self.filesview: c.setParent(None) c.deleteLater() self.filesview = [] self.checkBox_2.setChecked(False) self.progressBars = {} for f in self.__getFiles(path): try: group = QGroupBox(f, self) group.setGeometry(QRect(20, 20, 100, 150)) group.setCheckable(True) group.setChecked(False) group.setFixedSize(100, 150) group.setFlat(True) group.setToolTip(f) label = QLabel(group) label.setScaledContents(True) label.setGeometry(QRect(5, 25, 90, 90)) label.setToolTip(f) progressBar = QProgressBar(group) progressBar.setGeometry(QRect(0, 70, 111, 10)) progressBar.setProperty("value", 0) progressBar.setTextVisible(False) progressBar.setToolTip('0%') progressBar.setVisible(False) self.progressBars[f] = progressBar self.filesview.append(group) from os.path import isfile if isfile(path + '/' + f): ext = f.split('.')[-1] if isfile('icons/' + ext.lower() + '.png'): label.setPixmap( QPixmap('icons/' + ext.lower() + '.png')) else: label.setPixmap(QPixmap('icons/default.png')) else: label.setPixmap(QPixmap('icons/folder.png')) self.connect(group, SIGNAL("clicked()"), self.__deselectFile) except ValueError: pass i = 0 for x in list(range(len(self.filesview))): if (x % 4) == 0: i = i + 1 self.rowsview[i].addWidget(self.filesview[x])
def initUI(self): self.setWindowTitle(APPNAME) file_menu = self.menuBar().addMenu('File') new_proj_action = create_action(self, 'New Project', self.new_project) open_proj_action = create_action(self, 'Open Project', self.open_project) import_orosMat_action = create_action(self, 'Import Oros Mat', self.import_orosMat) quit_action = create_action(self, 'Quit', self.quit) add_actions(file_menu, (new_proj_action, open_proj_action, None, import_orosMat_action, None, quit_action)) progress_bar = QProgressBar(self) progress_bar.setFixedWidth(200) progress_bar.setTextVisible(False) progress_bar.setVisible(False) self.progress_bar = progress_bar self.statusBar().addPermanentWidget(progress_bar) self.toolBar = self.addToolBar('Curves') main_widget = QWidget(self) main_layout = QGridLayout(main_widget) side_layout = QGridLayout() plot_widget = PlotWidget(main_widget) self.plot_widget = plot_widget list_widget = ListWidget() prop_widget = PropertyWidget() list_widget.registerPropertyWindow(prop_widget) side_layout.addWidget(list_widget, 0, 0) side_layout.addWidget(prop_widget, 1, 0) side_layout.setRowStretch(0, 2) side_layout.setRowStretch(1, 1) self.list_widget = list_widget main_layout.addLayout(side_layout, 0, 0) main_layout.addWidget(plot_widget, 0, 1) main_layout.setColumnStretch(0, 1) main_layout.setColumnStretch(1, 3) self.setCentralWidget(main_widget)
class RenameGui(BaseWindow, QtGui.QMainWindow): renameCompleted = QtCore.pyqtSignal() def __init__(self, session, parent=None): super(RenameGui, self).__init__(parent) self.session = session self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.renameUser.clicked.connect(self.renameUser) self.progressIndicator = QProgressBar(self.ui.statusBar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.ui.statusBar.addWidget(self.progressIndicator) self.renameCompleted.connect(self.renameCompletedSlot) self.uiTranslate() def uiTranslate(self): self.setWindowTitle(strings["renameWindowTitle"]) self.ui.newUsername.setPlaceholderText(strings["newUsernameBoxHint"]) self.ui.renameUser.setText(strings["renameUserButtonText"]) def renameCompletedSlot(self): self.progressIndicator.setVisible(False) self.showMessageBox.emit(strings["renameSuccessText"]) def renameUser(self): self.progressIndicator.setVisible(True) def renameThread(): newUsername = self.ui.newUsername.text() if newUsername == "": self.showMessageBox.emit(strings["missingFieldsErrorText"]) return try: oldUsername = self.session.username self.session.renameUser(newUsername) for message in sharedData.messages: if message.sender == oldUsername: message.sender = newUsername elif message.receiver == oldUsername: message.receiver = newUsername storeMessages(self.session.username, sharedData.messages) storeToken(self.session.username, self.session.token) storeKey(self.session.username, self.session.public, self.session.private) storeMessages(oldUsername, None) storeToken(oldUsername, None) storeKey(oldUsername, None, None) self.renameCompleted.emit() except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return Thread(target=renameThread).start()
class ForgotPasswordUi(BaseWindow, QtGui.QMainWindow): requestSendingCompleted = QtCore.pyqtSignal() def __init__(self, parent=None): super(ForgotPasswordUi, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.forgot.clicked.connect(self.forgot) self.ui.forgot.setDefault(True) self.shortcut = QtGui.QShortcut(QtGui.QKeySequence("Return"), self) self.shortcut.activated.connect(self.forgot) self.shortcut2 = QtGui.QShortcut(QtGui.QKeySequence("Up"), self) self.shortcut2.activated.connect(self.focusUp) self.shortcut2 = QtGui.QShortcut(QtGui.QKeySequence("Down"), self) self.shortcut2.activated.connect(self.focusDown) self.progressIndicator = QProgressBar(self.ui.statusBar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.ui.statusBar.addWidget(self.progressIndicator) self.requestSendingCompleted.connect(self.requestSendingCompleteSlot) self.uiTranslate() def uiTranslate(self): self.setWindowTitle(strings["forgotPasswordWindowTitle"]) self.ui.username.setPlaceholderText(strings["usernameBoxHint"]) self.ui.email.setPlaceholderText(strings["emailBoxHint"]) self.ui.forgot.setText(strings["forgotPasswordButtonText"]) def requestSendingCompleteSlot(self): self.progressIndicator.setVisible(False) self.showMessageBox.emit(strings["forgotSuccessText"]) def focusUp(self): self.ui.username.setFocus() def focusDown(self): self.ui.email.setFocus() def forgot(self): self.progressIndicator.setVisible(True) def requestSenderThread(): if self.ui.username.text() and self.ui.email.text(): try: requestRecovery(self.ui.username.text(), self.ui.email.text()) self.requestSendingCompleted.emit() except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) else: self.showMessageBox.emit(strings["missingFieldsErrorText"]) Thread(target=requestSenderThread).start()
class AddUserGui(BaseWindow): addUserCompleted = QtCore.pyqtSignal() def __init__(self, parent=None): super(AddUserGui, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.adduser.clicked.connect(self.adduser) self.progressIndicator = QProgressBar(self.ui.statusBar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.ui.statusBar.addWidget(self.progressIndicator) self.addUserCompleted.connect(self.addUserCompletedSlot) self.uiTranslate() def uiTranslate(self): self.setWindowTitle(strings["registerUserWindowTitle"]) self.ui.username.setPlaceholderText(strings["usernameBoxHint"]) self.ui.password.setPlaceholderText(strings["passwordBoxHint"]) self.ui.email.setPlaceholderText(strings["emailBoxHint"]) self.ui.confirm.setPlaceholderText(strings["confirmPasswordBoxHint"]) self.ui.adduser.setText(strings["registerUserButtonText"]) def addUserCompletedSlot(self): self.progressIndicator.setVisible(False) self.showMessageBox.emit(strings["registrationSuccessText"]) def adduser(self): self.progressIndicator.setVisible(True) def addUserThread(): name = self.ui.username.text() password = self.ui.password.text() confirm = self.ui.confirm.text() email = self.ui.email.text() if name == "" or password == "" or email == "": self.showMessageBox.emit(strings["missingFieldsErrorText"]) return if password != confirm: self.showMessageBox.emit(strings["passwordsDontMatchErrorText"]) return try: addUser(name, email, password) self.addUserCompleted.emit() except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return Thread(target=addUserThread).start()
class LDSControls(QFrame): STATIC_IMG = ('error_static.png','linz_static.png','busy_static.png','clean_static.png') ANIM_IMG = ('error.gif','linz.gif','layer.gif','clean.gif') IMG_SPEED = 100 IMG_WIDTH = 64 IMG_HEIGHT = 64 MAX_WD = 450 GD_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../bin/gdal/gdal-data')) STATUS = LU.enum('ERROR','IDLE','BUSY','CLEAN') def __init__(self,parent): super(LDSControls, self).__init__() self.parent = parent self.initConf() self.initEPSG() self.initUI() def initConf(self): '''Read files in conf dir ending in conf''' self.cflist = ConfigInitialiser.getConfFiles() #self.imgset = self.STATIC_IMG if ConfigWrapper().readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG #self.imgset = self.STATIC_IMG if self.parent.confconn.tp.src.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG sep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.SRCNAME,self.parent.confconn.uconf) self.imgset = self.STATIC_IMG if sep.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG self.parent.confconn.reg.closeEndPoint(self.parent.confconn.SRCNAME) def initEPSG(self): '''Read GDAL EPSG files, splitting by NZ(RSR) and RestOfTheWorld''' gcsf = gdal.FindFile('gdal','gcs.csv') if not gcsf: gcsf = os.path.join(self.GD_PATH,'gcs.csv') pcsf = gdal.FindFile('gdal','pcs.csv') if not pcsf: pcsf = os.path.join(self.GD_PATH,'pcs.csv') gcs = ConfigInitialiser.readCSV(gcsf) pcs = ConfigInitialiser.readCSV(pcsf) self.nzlsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD' in e[1] or 'RSRGD' in e[1]] \ + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD' in e[1] or 'RSRGD' in e[1]] self.rowsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]] \ + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]] def initUI(self): # 0 1 2 3 4 5 6 7 8 #'destname','lgselect','layer','uconf','group','epsg','fd','td','int' #self.rdest,rlgselect,self.rlayer,ruconf,self.rgroup,repsg,rfd,rtd,rint = readlist QToolTip.setFont(QFont('SansSerif', 10)) #labels destLabel = QLabel('Destination') lgLabel = QLabel('Group/Layer') epsgLabel = QLabel('EPSG') fromDateLabel = QLabel('From Date') toDateLabel = QLabel('To Date') confLabel = QLabel('User Config') self.view = QLabel() self.view.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.view.setAlignment(Qt.AlignCenter) self.confcombo = QComboBox(self) self.confcombo.setToolTip('Enter your user config name (file) here') self.confcombo.addItems(self.cflist) self.confcombo.setEditable(False) #self.confcombo.currentIndexChanged.connect(self.doLGEditUpdate) #combos self.lgcombo = QComboBox(self) self.lgcombo.setMaximumWidth(self.MAX_WD) self.lgcombo.setDuplicatesEnabled(False) #self.lgcombo.setInsertPolicy(QComboBox.InsertAlphabetically)#?doesnt seem to work self.lgcombo.setToolTip('Select either Layer or Group entry') self.lgcombo.setEditable(False) self.sepindex = None #self.updateLGValues() self.epsgcombo = QComboBox(self) self.epsgcombo.setMaximumWidth(self.MAX_WD) self.epsgcombo.setToolTip('Setting an EPSG number here determines the output SR of the layer') self.epsgcombo.addItems([i[1] for i in self.nzlsr]) self.epsgcombo.insertSeparator(len(self.nzlsr)) self.epsgcombo.addItems([i[1] for i in self.rowsr]) self.epsgcombo.setEditable(True) self.epsgcombo.setEnabled(False) self.destlist = self.getConfiguredDestinations() self.destcombo = QComboBox(self) self.destcombo.setToolTip('Choose the desired output type') self.destcombo.setEditable(False) self.destcombo.addItems(self.destlist) #date selection self.fromdateedit = QDateEdit() self.fromdateedit.setCalendarPopup(True) self.fromdateedit.setEnabled(False) self.todateedit = QDateEdit() self.todateedit.setCalendarPopup(True) self.todateedit.setEnabled(False) #check boxes self.epsgenable = QCheckBox() self.epsgenable.setCheckState(False) self.epsgenable.clicked.connect(self.doEPSGEnable) self.fromdateenable = QCheckBox() self.fromdateenable.setCheckState(False) self.fromdateenable.clicked.connect(self.doFromDateEnable) self.todateenable = QCheckBox() self.todateenable.setCheckState(False) self.todateenable.clicked.connect(self.doToDateEnable) self.progressbar = QProgressBar() self.progressbar.setRange(0,100) self.progressbar.setVisible(True) self.progressbar.setMinimumWidth(self.MAX_WD) #buttons self.initbutton = QPushButton("waiting") self.initbutton.setToolTip('Initialise the Layer Configuration') self.initbutton.clicked.connect(self.doInitClickAction) self.cleanbutton = QPushButton("Clean") self.cleanbutton.setToolTip('Clean the selected layer/group from local storage') self.cleanbutton.clicked.connect(self.doCleanClickAction) self.replicatebutton = QPushButton("Replicate") self.replicatebutton.setToolTip('Execute selected replication') self.replicatebutton.clicked.connect(self.doReplicateClickAction) self.cancelbutton = QPushButton("Close") self.cancelbutton.setToolTip('Close the LDS Replicate application') self.cancelbutton.clicked.connect(self.parent.close) #set dialog values using GPR self.updateGUIValues(self.parent.gvs) #set onchange here otherwise we get circular initialisation self.destcombo.currentIndexChanged.connect(self.doDestChanged) self.confcombo.currentIndexChanged.connect(self.doConfChanged) self.lgcombo.currentIndexChanged.connect(self.doLGComboChanged) self.setStatus(self.STATUS.IDLE) #grid grid = QGridLayout() grid.setSpacing(10) #placement section ------------------------------------ #---------+---------+--------+---------+-------- # dest LB | | dest DD # grp LB | | grp DD # conf LB | | conf DD # epsg L | epsg CB | epsg DD # f dt L | f dt CB | f dt DD # t td L | t td CB | t td DD # icon | <- progress -> # layer B | <- . -> |repl B | clean B | close B #---------+---------+--------+---------+-------- grid.addWidget(destLabel, 1, 0) grid.addWidget(self.destcombo, 1, 2) #grid.addWidget(layerLabel, 2, 0) grid.addWidget(lgLabel, 2, 0) grid.addWidget(self.lgcombo, 2, 2) grid.addWidget(confLabel, 3, 0) grid.addWidget(self.confcombo, 3, 2) #grid.addWidget(groupLabel, 4, 0) #grid.addWidget(self.groupEdit, 4, 2) grid.addWidget(epsgLabel, 5, 0) grid.addWidget(self.epsgenable, 5, 1) grid.addWidget(self.epsgcombo, 5, 2) grid.addWidget(fromDateLabel, 6, 0) grid.addWidget(self.fromdateenable, 6, 1) grid.addWidget(self.fromdateedit, 6, 2) grid.addWidget(toDateLabel, 7, 0) grid.addWidget(self.todateenable, 7, 1) grid.addWidget(self.todateedit, 7, 2) hbox3 = QHBoxLayout() hbox3.addWidget(self.view) hbox3.addStretch(1) hbox3.addWidget(self.progressbar) #hbox3.addLayout(vbox2) #hbox3.addLayout(vbox3) hbox4 = QHBoxLayout() hbox4.addWidget(self.initbutton) hbox4.addStretch(1) hbox4.addWidget(self.replicatebutton) hbox4.addWidget(self.cleanbutton) hbox4.addWidget(self.cancelbutton) vbox = QVBoxLayout() #vbox.addStretch(1) vbox.addLayout(grid) vbox.addLayout(hbox3) vbox.addLayout(hbox4) self.setLayout(vbox) #def setProgress(self,pct): # self.progressbar.setValue(pct) def setStatus(self,status,message='',tooltip=None): '''Sets indicator icon and statusbar message''' self.parent.statusbar.showMessage(message) self.parent.statusbar.setToolTip(tooltip if tooltip else '') #progress loc = os.path.abspath(os.path.join(IMG_LOC,self.imgset[status])) #loc = os.path.abspath(os.path.join(os.path.dirname(__file__),self.parent.IMG_LOC,self.imgset[status])) self.progressbar.setVisible(status in (self.STATUS.BUSY, self.STATUS.CLEAN)) #icon anim = QMovie(loc, QByteArray(), self) anim.setScaledSize(QSize(self.IMG_WIDTH,self.IMG_HEIGHT)) anim.setCacheMode(QMovie.CacheAll) anim.setSpeed(self.IMG_SPEED) self.view.clear() self.view.setMovie(anim) anim.start() self.view.repaint() QApplication.processEvents(QEventLoop.AllEvents) def mainWindowEnable(self,enable=True): cons = (self.lgcombo, self.confcombo, self.destcombo, self.initbutton, self.replicatebutton, self.cleanbutton, self.cancelbutton, self.epsgenable,self.fromdateenable,self.todateenable, self.parent.menubar) for c in cons: c.setEnabled(enable) if enable: self.epsgcombo.setEnabled(self.epsgenable.checkState()) self.fromdateedit.setEnabled(self.fromdateenable.checkState()) self.todateedit.setEnabled(self.todateenable.checkState()) else: self.epsgcombo.setEnabled(False) self.fromdateedit.setEnabled(False) self.todateedit.setEnabled(False) QApplication.restoreOverrideCursor() if enable else QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) def refreshLGCombo(self): '''Re index LG combobox since a refreshLG call (new dest?) will usually mean new groups''' self.lgcombo.clear() self.lgcombo.addItems([i[2] for i in self.parent.confconn.lglist]) #NOTE the separator consumes an index, if not clearing the combobox selectively remove the old sepindex (assumes g preceeds l) #if self.sepindex: # self.lgcombo.removeItem(self.sepindex) self.sepindex = [i[0] for i in self.parent.confconn.lglist].count(LORG.GROUP) self.lgcombo.insertSeparator(self.sepindex) def updateLGValues(self,uconf,lgval,dest): '''Sets the values displayed in the Layer/Group combo''' #because we cant seem to sort combobox entries and want groups at the top, clear and re-add #TRACE# #pdb.set_trace() sf = None try: self.parent.confconn.initConnections(uconf,lgval,dest) except Exception as e: sf=1 ldslog.error('Error Updating UC Values. '+str(e)) if sf: self.setStatus(self.STATUS.ERROR,'Error Updating UC Values', str(e)) else: self.setStatus(self.STATUS.IDLE) self.refreshLGCombo() def centre(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def gprParameters(self,rdest): '''Zip default and GPR values''' return [x if LU.assessNone(x) else y for x,y in zip(self.parent.gpr.readsec(rdest),self.parent.DEF_RVALS[1:])] def getLCE(self,ln): '''Read layer parameters''' dep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf) #sep = self.parent.confconn.reg.openEndPoint('WFS',self.parent.confconn.uconf) self.parent.confconn.reg.setupLayerConfig(self.parent.confconn.tp,None,dep,initlc=False) lce = dep.getLayerConf().readLayerParameters(ln) #self.parent.confconn.reg.closeEndPoint('WFS') self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname) sep,dep = None,None return lce def doDestChanged(self): '''Read the destname parameter and fill dialog with matching GPR values''' rdest = str(self.destlist[self.destcombo.currentIndex()]) rvals = self.gprParameters(rdest) self.updateGUIValues([rdest]+rvals) def doConfChanged(self): '''Read the user conf parameter and fill dialog with matching GPR values''' rdest = str(self.destlist[self.destcombo.currentIndex()]) rlg,_,rep,rfd,rtd = self.gprParameters(rdest) ruc = str(self.cflist[self.confcombo.currentIndex()]) self.updateGUIValues((rdest,rlg,ruc,rep,rfd,rtd)) def doLGComboChanged(self): '''Read the layer/group value and change epsg to layer or gpr match''' #get a matching LG entry and test whether its a layer or group #lgi = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data()) lgi = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText())) #lgi can be none if we init a new group, in which case we use the GPR value if lgi: lge = self.parent.confconn.lglist[lgi] lce = self.getLCE(lge[1]) if lge[0]==LORG.LAYER else None else: lce = None #look for filled layer conf epsg OR use prefs stored in gpr if lce and LU.assessNone(lce.epsg): epsgval = lce.epsg else: rdest = str(self.destlist[self.destcombo.currentIndex()]) _,_,epsgval,_,_ = self.gprParameters(rdest) epsgindex = [i[0] for i in self.nzlsr+[(0,0)]+self.rowsr].index(epsgval) if self.epsgcombo.currentIndex() != epsgindex: self.epsgcombo.setCurrentIndex(int(epsgindex)) def updateGUIValues(self,readlist): '''Fill dialog values from provided list''' #TODO. Remove circular references when setCurrentIndex() triggers do###Changed() #Read user input rdest,self.rlgval,ruconf,repsg,rfd,rtd = readlist #-------------------------------------------------------------------- #Destination Menu selecteddest = LU.standardiseDriverNames(rdest) if selecteddest not in self.destlist: self.destlist = self.getConfiguredDestinations() self.destcombo.addItem(selecteddest) destindex = self.destlist.index(selecteddest) if selecteddest else 0 if self.destcombo.currentIndex() != destindex: self.destcombo.setCurrentIndex(destindex) #InitButton self.initbutton.setText('Layer Select') #Config File confindex = 0 if LU.assessNone(ruconf): ruconf = ruconf.split('.')[0] if ruconf not in self.cflist: self.cflist += [ruconf,] self.confcombo.addItem(ruconf) confindex = self.cflist.index(ruconf) if self.confcombo.currentIndex() != confindex: self.confcombo.setCurrentIndex(confindex) #self.confEdit.setText(ruconf if LU.assessNone(ruconf) else '') #Layer/Group Selection self.updateLGValues(ruconf,self.rlgval,rdest) lgindex = None if LU.assessNone(self.rlgval): #index of list value lgindex = self.parent.confconn.getLayerGroupIndex(self.rlgval,col=1) if LU.assessNone(lgindex): #advance by 1 for sep lgindex += 1 if lgindex>self.sepindex else 0 else: #using the separator index sets the combo to blank lgindex = self.sepindex if self.lgcombo.currentIndex() != lgindex: self.lgcombo.setCurrentIndex(lgindex) #self.doLGEditUpdate() #EPSG # user > layerconf #useepsg = LU.precedence(repsg, lce.epsg if lce else None, None) epsgindex = [i[0] for i in self.nzlsr+[(None,None)]+self.rowsr].index(repsg) if self.epsgcombo.currentIndex() != epsgindex: self.epsgcombo.setCurrentIndex(epsgindex) #epsgedit = self.epsgcombo.lineEdit() #epsgedit.setText([e[1] for e in self.nzlsr+self.rowsr if e[0]==repsg][0]) #epsgedit.setText([e for e in self.nzlsr+self.rowsr if re.match('^\s*(\d+).*',e).group(1)==repsg][0]) #To/From Dates if LU.assessNone(rfd): self.fromdateedit.setDate(QDate(int(rfd[0:4]),int(rfd[5:7]),int(rfd[8:10]))) else: early = DataStore.EARLIEST_INIT_DATE self.fromdateedit.setDate(QDate(int(early[0:4]),int(early[5:7]),int(early[8:10]))) if LU.assessNone(rtd): self.todateedit.setDate(QDate(int(rtd[0:4]),int(rtd[5:7]),int(rtd[8:10]))) else: today = DataStore.getCurrent() self.todateedit.setDate(QDate(int(today[0:4]),int(today[5:7]),int(today[8:10]))) #Internal/External CheckBox # if LU.assessNone(rint): # self.internalTrigger.setChecked(rint.lower()==DataStore.CONF_INT) # else: # self.internalTrigger.setChecked(DataStore.DEFAULT_CONF==DataStore.CONF_INT) def getConfiguredDestinations(self): defml = ['',]+DataStore.DRIVER_NAMES.values() return [d for d in self.parent.gpr.getDestinations() if d in defml] def doEPSGEnable(self): self.epsgcombo.setEnabled(self.epsgenable.isChecked()) def doFromDateEnable(self): self.fromdateedit.setEnabled(self.fromdateenable.isChecked()) def doToDateEnable(self): self.todateedit.setEnabled(self.todateenable.isChecked()) def readParameters(self): '''Read values out of dialogs''' destination = LU.assessNone(str(self.destlist[self.destcombo.currentIndex()])) #lgindex = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data()) lgindex = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText())) #NB need to test for None explicitly since zero is a valid index lgval = self.parent.confconn.lglist[lgindex][1] if LU.assessNone(lgindex) else None #uconf = LU.standardiseUserConfigName(str(self.confcombo.lineEdit().text())) #uconf = str(self.confcombo.lineEdit().text()) uconf = str(self.cflist[self.confcombo.currentIndex()]) ee = self.epsgenable.isChecked() epsg = None if ee is False else re.match('^\s*(\d+).*',str(self.epsgcombo.lineEdit().text())).group(1) fe = self.fromdateenable.isChecked() te = self.todateenable.isChecked() fd = None if fe is False else str(self.fromdateedit.date().toString('yyyy-MM-dd')) td = None if te is False else str(self.todateedit.date().toString('yyyy-MM-dd')) return destination,lgval,uconf,epsg,fe,te,fd,td def doInitClickAction(self): '''Initialise the LC on LC-button-click, action''' try: try: self.setStatus(self.STATUS.BUSY,'Opening Layer-Config Editor') self.progressbar.setValue(0) self.parent.runLayerConfigAction() finally: self.setStatus(self.STATUS.IDLE,'Ready') except Exception as e: self.setStatus(self.STATUS.ERROR,'Error in Layer-Config',str(sys.exc_info()))#e)) def doCleanClickAction(self): '''Set clean anim and run clean''' #lgo = self.lgcombo.currentText().toUtf8().data() lgo = LQ.readWidgetText(self.lgcombo.currentText()) try: self.setStatus(self.STATUS.CLEAN,'Running Clean '+lgo) self.progressbar.setValue(0) self.runReplicationScript(True) except Exception as e: self.setStatus(self.STATUS.ERROR,'Failed Clean of '+lgo,str(sys.exc_info()))#e)) def doReplicateClickAction(self): '''Set busy anim and run repl''' lgo = self.lgcombo.currentText()#.toUtf8().data()#only used for error messages try: self.setStatus(self.STATUS.BUSY,'Running Replicate '+lgo) self.progressbar.setValue(0) self.runReplicationScript(False) ldslog.debug('TRPfinish') except Exception as e: self.setStatus(self.STATUS.ERROR,'Failed Replication of '+lgo,str(sys.exc_info()))#e)) def runReplicationScript(self,clean=False): '''Run the layer/group repliction script''' destination,lgval,uconf,epsg,fe,te,fd,td = self.readParameters() uconf_path = LU.standardiseUserConfigName(uconf) destination_path = LU.standardiseLayerConfigName(destination) destination_driver = LU.standardiseDriverNames(destination) if not os.path.exists(uconf_path): self.userConfMessage(uconf_path) return elif not MainFileReader(uconf_path).hasSection(destination_driver): self.userConfMessage(uconf_path,destination_driver) return #----------------------------------------------------- #'destname','layer','uconf','group','epsg','fd','td','int' self.parent.gpr.write((destination_driver,lgval,uconf,epsg,fd,td)) ldslog.info(u'dest={0}, lg={1}, conf={2}, epsg={3}'.format(destination_driver,lgval,uconf,epsg)) ldslog.info('fd={0}, td={1}, fe={2}, te={3}'.format(fd,td,fe,te)) lgindex = self.parent.confconn.getLayerGroupIndex(lgval,col=1) #lorg = self.parent.confconn.lglist[lgindex][0] #----------don't need lorg in TP anymore but it is useful for sorting/counting groups #self.parent.confconn.tp.setLayerOrGroup(lorg) self.parent.confconn.tp.setLayerGroupValue(lgval) if self.fromdateenable.isChecked(): self.parent.confconn.tp.setFromDate(fd) if self.todateenable.isChecked(): self.parent.confconn.tp.setToDate(td) self.parent.confconn.tp.setUserConf(uconf) if self.epsgenable: self.parent.confconn.tp.setEPSG(epsg) #because clean state persists in TP if clean: self.parent.confconn.tp.setCleanConfig() else: self.parent.confconn.tp.clearCleanConfig() #(re)initialise the data source since uconf may have changed #>>#self.parent.confconn.tp.src = self.parent.confconn.initSourceWrapper() #-------------------------- ###ep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf) ###self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname) ###ep = None #Open ProcessRunner and run with TP(proc)/self(gui) instances #HACK temp add of dest_drv to PR call try: #TODO. Test for valid LC first self.tpr = ProcessRunner(self,destination_driver) except Exception as e: ldslog.error('Cannot create ProcessRunner {}. NB Possible missing Layer Config {}'.format(str(e),destination_path)) self.layerConfMessage(destination_path) return #If PR has been successfully created we must vave a valid dst ldslog.debug('TRPstart') self.tpr.start() # def quitProcessRunner(self): # self.tpr.join() # self.tpr.quit() # self.trp = None def userConfMessage(self,uconf,secname=None): ucans = QMessageBox.warning(self, 'User Config Missing/Incomplete', 'Specified User-Config file, '+str(uconf)+' does not exist' if secname is None else 'User-Config file does not contain '+str(secname)+' section', 'Back','Initialise User Config') if not ucans: #Retry ldslog.warn('Retry specifying UC') #self.confcombo.setCurrentIndex(0) return #Init ldslog.warn('Reset User Config Wizard') self.parent.runWizardAction() def layerConfMessage(self,dest): lcans = QMessageBox.warning(self, 'Layer Config Missing', 'Required Layer-Config file, '+str(dest)+' does not exist', 'Back','Run Layer Select') if not lcans: #Retry ldslog.warn('Retry specifying LC') #self.destcombo.setCurrentIndex(0) return #Init ldslog.warn('Reset Layer Config') self.doInitClickAction()
class MainWinGui(QtGui.QMainWindow): progressIndeterminate = QtCore.pyqtSignal() progressSet = QtCore.pyqtSignal(int) progressStop = QtCore.pyqtSignal() def __init__(self, parent=None): super(MainWinGui, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.myName.setText(getStoredName() or "") self.ui.myName.textEdited.connect(self.nameChanged) self.communicator = Communicator(self) self.communicator.peersUpdated.connect(self.refreshPeers) self.communicator.fileReceived.connect(self.fileReceived) self.ui.refreshPeersButton.clicked.connect(self.discoverPeers) self.ui.peerList.itemClicked.connect(self.peerSelected) self.progressIndeterminate.connect(self.progressIndeterminateSlot) self.progressSet.connect(self.progressSetSlot) self.progressStop.connect(self.progressStopSlot) self.progressIndicator = QProgressBar(self.ui.statusbar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.ui.statusbar.addWidget(self.progressIndicator) def nameChanged(self, newName): storeName(newName) self.communicator.updateName(newName) def progressStart(self): self.progressIndicator.setVisible(True) def progressSetSlot(self, value): self.progressStart() self.progressIndicator.setMaximum(100) self.progressIndicator.setValue(value) def progressIndeterminateSlot(self): self.progressStart() self.progressIndicator.setMaximum(0) def progressStopSlot(self): self.progressIndicator.setVisible(False) def show(self): super(MainWinGui, self).show() self.refreshPeers() self.discoverPeers() def discoverPeers(self): self.communicator.discoverPeers() def refreshPeers(self): self.ui.peerList.clear() for peer in self.communicator.peers: peer = self.communicator.peers[peer] peerName = QListWidgetItem(self.ui.peerList) peerName.peer = peer nameFont = QtGui.QFont() nameFont.setPointSize(14) peerDetails = QListWidgetItem(self.ui.peerList) peerDetails.peer = peer detailsFont = QtGui.QFont() detailsFont.setPointSize(10) name = peer.name details = "" if peer.publicKey is None: details += "Unpaired, " else: details += "Paired, " if peer.lastKnownIP is None: details += "unavailable" else: details += "available: " + peer.lastKnownIP peerName.setFont(nameFont) peerName.setText(name) peerDetails.setFont(detailsFont) peerDetails.setText(details) self.ui.peerList.addItem(peerName) self.ui.peerList.addItem(peerDetails) separatorItem = QListWidgetItem(self.ui.peerList) separatorItem.guid = peer.guid separatorItem.peer = None separatorItem.setFlags(QtCore.Qt.NoItemFlags) self.ui.peerList.addItem(separatorItem) def peerSelected(self, selectedItem): selectedItem.setSelected(False) if selectedItem.peer: PeerOptionsUi(self, selectedItem.peer, self.communicator).show() else: pass def sendFile(self, guid): fileName = QtGui.QFileDialog.getOpenFileName() if not fileName: return fileContents = open(fileName, "rb").read() basename = os.path.basename(fileName) self.communicator.sendFile(basename, fileContents, guid) def fileReceived(self, fileName, fileContents): fileName = QtGui.QFileDialog.getSaveFileName(directory=fileName) if not fileName: return with open(fileName, mode="wb") as file: file.write(fileContents)
class ClientDialog(QDialog): """a simple popup dialog for asking the player what he wants to do""" def __init__(self, client, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(m18n('Choose') + ' - Kajongg') self.setObjectName('ClientDialog') self.client = client self.layout = QGridLayout(self) self.progressBar = QProgressBar() self.timer = QTimer() if not client.game.autoPlay: self.timer.timeout.connect(self.timeout) self.deferred = None self.buttons = [] self.setWindowFlags(Qt.SubWindow | Qt.WindowStaysOnTopHint) self.setModal(False) self.btnHeight = 0 self.answered = False def keyPressEvent(self, event): """ESC selects default answer""" if not self.client.game or self.client.game.autoPlay: return if event.key() in [Qt.Key_Escape, Qt.Key_Space]: self.selectButton() event.accept() else: for btn in self.buttons: if str(event.text()).upper() == btn.message.shortcut: self.selectButton(btn) event.accept() return QDialog.keyPressEvent(self, event) def __declareButton(self, message): """define a button""" maySay = self.client.sayable[message] if Preferences.showOnlyPossibleActions and not maySay: return btn = DlgButton(message, self) btn.setAutoDefault(True) btn.clicked.connect(self.selectedAnswer) self.buttons.append(btn) def focusTileChanged(self): """update icon and tooltip for the discard button""" if not self.client.game: return for button in self.buttons: button.decorate(self.client.game.myself.handBoard.focusTile) for tile in self.client.game.myself.handBoard.lowerHalfTiles(): txt = [] for button in self.buttons: _, _, tileTxt = button.message.toolTip(button, tile) if tileTxt: txt.append(tileTxt) txt = '<br><br>'.join(txt) tile.graphics.setToolTip(txt) if self.client.game.activePlayer == self.client.game.myself: Internal.field.handSelectorChanged(self.client.game.myself.handBoard) def checkTiles(self): """does the logical state match the displayed tiles?""" for player in self.client.game.players: logExposed = list() physExposed = list() physConcealed = list() for tile in player.bonusTiles: logExposed.append(tile.element) for tile in player.handBoard.tiles: if tile.yoffset == 0 or tile.element[0] in 'fy': physExposed.append(tile.element) else: physConcealed.append(tile.element) for meld in player.exposedMelds: logExposed.extend(meld.pairs) logConcealed = sorted(player.concealedTileNames) logExposed.sort() physExposed.sort() physConcealed.sort() assert logExposed == physExposed, '%s != %s' % (logExposed, physExposed) assert logConcealed == physConcealed, '%s != %s' % (logConcealed, physConcealed) def messages(self): """a list of all messages returned by the declared buttons""" return list(x.message for x in self.buttons) def proposeAction(self): """either intelligently or first button by default. May also focus a proposed tile depending on the action.""" result = self.buttons[0] game = self.client.game if game.autoPlay or Preferences.propose: answer, parameter = self.client.intelligence.selectAnswer( self.messages()) result = [x for x in self.buttons if x.message == answer][0] result.setFocus() if answer in [Message.Discard, Message.OriginalCall]: for tile in game.myself.handBoard.tiles: if tile.element == parameter: game.myself.handBoard.focusTile = tile return result def askHuman(self, move, answers, deferred): """make buttons specified by answers visible. The first answer is default. The default button only appears with blue border when this dialog has focus but we always want it to be recognizable. Hence setBackgroundRole.""" self.move = move self.deferred = deferred for answer in answers: self.__declareButton(answer) self.focusTileChanged() self.show() self.checkTiles() game = self.client.game myTurn = game.activePlayer == game.myself prefButton = self.proposeAction() if game.autoPlay: self.selectButton(prefButton) return prefButton.setFocus() self.progressBar.setVisible(not myTurn) if not myTurn: msecs = 50 self.progressBar.setMinimum(0) self.progressBar.setMaximum(game.ruleset.claimTimeout * 1000 // msecs) self.progressBar.reset() self.timer.start(msecs) def placeInField(self): """place the dialog at bottom or to the right depending on space.""" field = Internal.field cwi = field.centralWidget() view = field.centralView geometry = self.geometry() if not self.btnHeight: self.btnHeight = self.buttons[0].height() vertical = view.width() > view.height() * 1.2 if vertical: height = (len(self.buttons) + 1) * self.btnHeight * 1.2 width = (cwi.width() - cwi.height() ) // 2 geometry.setX(cwi.width() - width) geometry.setY(min(cwi.height()//3, cwi.height() - height)) else: handBoard = self.client.game.myself.handBoard if not handBoard: # we are in the progress of logging out return hbLeftTop = view.mapFromScene(handBoard.mapToScene(handBoard.rect().topLeft())) hbRightBottom = view.mapFromScene(handBoard.mapToScene(handBoard.rect().bottomRight())) width = hbRightBottom.x() - hbLeftTop.x() height = self.btnHeight geometry.setY(cwi.height() - height) geometry.setX(hbLeftTop.x()) for idx, btn in enumerate(self.buttons + [self.progressBar]): self.layout.addWidget(btn, idx+1 if vertical else 0, idx+1 if not vertical else 0) idx = len(self.buttons) + 2 spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Expanding) self.layout.addItem(spacer, idx if vertical else 0, idx if not vertical else 0) geometry.setWidth(width) geometry.setHeight(height) self.setGeometry(geometry) def showEvent(self, dummyEvent): """try to place the dialog such that it does not cover interesting information""" self.placeInField() def timeout(self): """the progressboard wants an update""" pBar = self.progressBar if isAlive(pBar): pBar.setValue(pBar.value()+1) pBar.setVisible(True) if pBar.value() == pBar.maximum(): # timeout: we always return the original default answer, not the one with focus self.selectButton() pBar.setVisible(False) def selectButton(self, button=None): """select default answer. button may also be of type Message.""" self.timer.stop() self.answered = True if button is None: button = self.focusWidget() if isinstance(button, Message): assert any(x.message == button for x in self.buttons) answer = button else: answer = button.message if not self.client.sayable[answer]: Sorry(m18n('You cannot say %1', answer.i18nName)) return Internal.field.clientDialog = None self.deferred.callback(answer) def selectedAnswer(self, dummyChecked): """the user clicked one of the buttons""" game = self.client.game if game and not game.autoPlay: self.selectButton(self.sender())
class LoginWinGui(BaseWindow, QtGui.QMainWindow): loginComplete = QtCore.pyqtSignal(Session) def __init__(self, parent=None): super(LoginWinGui, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.login.clicked.connect(self.login) self.ui.login.setDefault(True) self.ui.adduser.triggered.connect(self.adduser) self.ui.forgot.triggered.connect(self.forgot) QtGui.QShortcut(QtGui.QKeySequence("Return"), self).activated.connect(self.login) QtGui.QShortcut(QtGui.QKeySequence("Enter"), self).activated.connect(self.login) QtGui.QShortcut(QtGui.QKeySequence("Up"), self).activated.connect(self.focusUp) QtGui.QShortcut(QtGui.QKeySequence("Down"), self).activated.connect(self.focusDown) self.progressIndicator = QProgressBar(self.ui.statusBar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.ui.statusBar.addWidget(self.progressIndicator) self.loginComplete.connect(self.loginCompleteSlot) self.uiTranslate() def uiTranslate(self): self.setWindowTitle(strings["loginWindowTitle"]) self.ui.username.setPlaceholderText(strings["usernameBoxHint"]) self.ui.password.setPlaceholderText(strings["passwordBoxHint"]) self.ui.login.setText(strings["loginButtonText"]) self.ui.adduser.setText(strings["registerUserWindowTitle"]) self.ui.forgot.setText(strings["forgotPasswordWindowTitle"]) self.ui.menuTools.setTitle(strings["toolsMenuText"]) def loginCompleteSlot(self, session): self.progressIndicator.setVisible(False) self.mainWin = ConversationsWinGui(session) self.mainWin.windowClosed.connect(self.close) self.mainWin.loggedOut.connect(self.loggedOut) self.mainWin.show() self.hide() def loggedOut(self): self.show() self.mainWin.windowClosed.disconnect(self.close) self.mainWin.close() def onMainClose(self): pass def focusUp(self): self.ui.username.setFocus() def focusDown(self): self.ui.password.setFocus() def login(self): self.progressIndicator.setVisible(True) def loginThread(): username = self.ui.username.text() password = self.ui.password.text() if username == "" or password == "": self.showMessageBox.emit(strings["missingFieldsErrorText"]) return try: if storage.getToken(username) and storage.getKey(username): token = storage.getToken(username) public, private = storage.getKey(username) newSession = Session(username, password, token, public, private) storage.storeKey(newSession.username, newSession.public, newSession.private) else: token, public, private = Session.requestNewToken(username, password) newSession = Session(username, password, token, public, private) storage.storeToken(newSession.username, token) storage.storeKey(newSession.username, newSession.public, newSession.private) self.loginComplete.emit(newSession) except SecureMessagingException as error: if error.message == HTTPStrings.RESPONSE_UNAUTHORIZED: storage.storeKey(username, None, None) storage.storeToken(username, None) loginThread() return self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) Thread(target=loginThread).start() def adduser(self): addUserGui = AddUserGui(parent=self) addUserGui.show() def forgot(self): forgotUi = ForgotPasswordUi(parent=self) forgotUi.show()
class UI_Psychrometry(QDialog): """Psychrometric charts tool""" def __init__(self, parent=None): super(UI_Psychrometry, self).__init__(parent) self.showMaximized() self.setWindowTitle("Psychrometric chart") layout = QGridLayout(self) self.diagrama2D = PsychroPlot(self, dpi=90) self.diagrama2D.fig.canvas.mpl_connect("motion_notify_event", self.scroll) layout.addWidget(self.diagrama2D, 1, 1, 1, 2) self.progressBar = QProgressBar() self.progressBar.setVisible(False) layout.addWidget(self.progressBar, 2, 1) self.status = QLabel() layout.addWidget(self.status, 2, 1) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close) butonPNG = QPushButton("Save as PNG") self.buttonBox.addButton(butonPNG, QDialogButtonBox.AcceptRole) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.savePNG) layout.addWidget(self.buttonBox, 2, 2) self.plot() def savePNG(self): """Save chart image to png file""" fname = unicode( QFileDialog.getSaveFileName(self, "Save chart to file", "./", "Portable Network Graphics (*.png)") ) self.diagrama2D.fig.savefig(fname, facecolor="#eeeeee") def drawlabel(self, name, Preferences, t, W, label, unit): """ Draw annotation for isolines name: name of isoline Preferences: Configparse instance of pychemqt preferences t: x array of line W: y array of line label: text value to draw unit: text units to draw """ if Preferences.getboolean("Psychr", name + "label"): tmin = Preferences.getfloat("Psychr", "isotdbStart") - 273.15 tmax = Preferences.getfloat("Psychr", "isotdbEnd") - 273.15 x = tmax - tmin wmin = Preferences.getfloat("Psychr", "isowStart") wmax = Preferences.getfloat("Psychr", "isowEnd") y = wmax - wmin i = 0 for ti, wi in zip(t, W): if tmin <= ti <= tmax and wmin <= wi <= wmax: i += 1 label = str(label) if Preferences.getboolean("Psychr", name + "units"): label += unit pos = Preferences.getfloat("Psychr", name + "position") p = int(i * pos / 100 - 1) rot = np.arctan((W[p] - W[p - 1]) / y / (t[p] - t[p - 1]) * x) * 360.0 / 2.0 / np.pi self.diagrama2D.axes2D.annotate(label, (t[p], W[p]), rotation=rot, size="small", ha="center", va="center") def plot(self): """Plot chart""" Preferences = ConfigParser() Preferences.read("psyrc") self.diagrama2D.axes2D.clear() self.diagrama2D.config() filename = "%i.pkl" % P if os.path.isfile(filename): with open(filename, "r") as archivo: data = cPickle.load(archivo) self.status.setText("Loading cached data...") QApplication.processEvents() else: self.progressBar.setVisible(True) self.status.setText("Calculating data, be patient...") QApplication.processEvents() data = PsyCoolprop.calculatePlot(self) cPickle.dump(data, open(filename, "w")) self.progressBar.setVisible(False) self.status.setText("Plotting...") QApplication.processEvents() tmax = Preferences.getfloat("Psychr", "isotdbEnd") - 273.15 t = [ti - 273.15 for ti in data["t"]] Hs = data["Hs"] format = {} format["ls"] = Preferences.get("Psychr", "saturationlineStyle") format["lw"] = Preferences.getfloat("Psychr", "saturationlineWidth") format["color"] = Preferences.get("Psychr", "saturationColor") format["marker"] = Preferences.get("Psychr", "saturationmarker") format["markersize"] = 3 self.diagrama2D.plot(t, Hs, **format) format = {} format["ls"] = Preferences.get("Psychr", "isotdblineStyle") format["lw"] = Preferences.getfloat("Psychr", "isotdblineWidth") format["color"] = Preferences.get("Psychr", "isotdbColor") format["marker"] = Preferences.get("Psychr", "isotdbmarker") format["markersize"] = 3 for i, T in enumerate(t): self.diagrama2D.plot([T, T], [0, Hs[i]], **format) H = data["H"] th = data["th"] format = {} format["ls"] = Preferences.get("Psychr", "isowlineStyle") format["lw"] = Preferences.getfloat("Psychr", "isowlineWidth") format["color"] = Preferences.get("Psychr", "isowColor") format["marker"] = Preferences.get("Psychr", "isowmarker") format["markersize"] = 3 for i, H in enumerate(H): self.diagrama2D.plot([th[i], tmax], [H, H], **format) format = {} format["ls"] = Preferences.get("Psychr", "isohrlineStyle") format["lw"] = Preferences.getfloat("Psychr", "isohrlineWidth") format["color"] = Preferences.get("Psychr", "isohrColor") format["marker"] = Preferences.get("Psychr", "isohrmarker") format["markersize"] = 3 for Hr, H0 in data["Hr"].iteritems(): self.diagrama2D.plot(t, H0, **format) self.drawlabel("isohr", Preferences, t, H0, Hr, "%") format = {} format["ls"] = Preferences.get("Psychr", "isotwblineStyle") format["lw"] = Preferences.getfloat("Psychr", "isotwblineWidth") format["color"] = Preferences.get("Psychr", "isotwbColor") format["marker"] = Preferences.get("Psychr", "isotwbmarker") format["markersize"] = 3 for T, (H, Tw) in data["Twb"].iteritems(): self.diagrama2D.plot(Tw, H, **format) value = T - 273.15 txt = u"ºC" self.drawlabel("isotwb", Preferences, Tw, H, value, txt) format = {} format["ls"] = Preferences.get("Psychr", "isochorlineStyle") format["lw"] = Preferences.getfloat("Psychr", "isochorlineWidth") format["color"] = Preferences.get("Psychr", "isochorColor") format["marker"] = Preferences.get("Psychr", "isochormarker") format["markersize"] = 3 for v, (Td, H) in data["v"].iteritems(): self.diagrama2D.plot(Td, H, **format) value = v txt = u"m³/kg" self.drawlabel("isochor", Preferences, Td, H, value, txt) self.diagrama2D.draw() self.status.setText("P = %i Pa" % P) def scroll(self, event): """Update graph annotate when mouse scroll over chart""" if event.xdata and event.ydata: punto = self.createState(event.xdata, event.ydata) if event.ydata < punto.ws: self.diagrama2D.showPointData(punto) else: self.diagrama2D.clearPointData() def createState(self, x, y): """Create psychrometric state from click or mouse position""" tdb = x + 273.15 punto = PsyCoolprop(P=P, tdb=tdb, w=y) return punto
class SegmentationClassificationWindow(SoundLabWindow, Ui_MainWindow): """ Window that process the segmentation and classification of a signal Contains a QSignalDetectorWidget that wrapper several functionality Allows to select the segmentation and classification settings, and parameter measurement for detected segments. Provides a table for visualization of segment and measures, A two dimensional window to graph two measured params. One for each axis. Options for selection and visualization of segments Provides options for save the parameters to excel. """ # region CONSTANTS # sort type of the parameter table PARAMETER_SORT_NONE, PARAMETER_SORT_BY_LOCATION, PARAMETER_SORT_BY_PARAMETER = range( 3) # different colors for the even and odds rows in the parameter table and segment colors. TABLE_ROW_COLOR_ODD = QColor(0, 0, 255, 150) TABLE_ROW_COLOR_EVEN = QColor(0, 255, 0, 150) # stylesheet to use on excel file saved EXCEL_STYLE_HEADER = xlwt.easyxf( 'font: name Times New Roman, color-index black, bold on, height 300') EXCEL_STYLE_BODY = xlwt.easyxf( 'font: name Times New Roman, color-index black, height 220', num_format_str='#,# # 0.00') EXCEL_STYLE_COPYRIGHT = xlwt.easyxf( 'font: name Arial, color-index pale_blue, height 250, italic on', num_format_str='# ,# # 0.00') # endregion # region Initialize def __init__(self, parent=None, signal=None, workspace=None): """ Create a the window of segmentation and classification. :param parent: the parent widget if any :param signal: the signal to visualize for segmentation and classification :param workspace: the workspace with specgram and oscilogram generation options :return: """ # set the visual variables and methods from ancestor's SoundLabWindow.__init__(self, parent) self.setupUi(self) # check the parameters if signal is None or not isinstance(signal, AudioSignal): raise Exception( "The signal to analyze must be of type AudioSignal") self.configureToolBarActionsGroups() # the segmentation window do not allow to record a signal (for now...) self.actionRecord.setEnabled(False) # the object that handles the measuring of parameters and manage the segments self.segmentManager = SegmentManager() self.segmentManager.measurementsFinished.connect( self.measurement_finished) self.segmentManager.detectionProgressChanged.connect( lambda x: self.windowProgressDetection.setValue(x * 0.9)) self.segmentManager.segmentVisualItemAdded.connect( self.widget.add_parameter_visual_items) self.segmentManager.segmentationFinished.connect( self.segmentation_finished) # the factory of adapters for parameters to supply to the ui window or segmentation dialog self.parameter_manager = MeasurementTemplate(signal=signal) # set the signal to the widget self.widget.signal = signal self.segmentManager.signal = signal # set the configurations for the name of the signal and the visible label self.updateSignalPropertiesLabel(signal) self.signalNameLineEdit.setReadOnly(True) self.actionSignalName.setText(self.widget.signalName) # set visible the two graphs by default self.changeWidgetsVisibility(True, True) # shift of the table parameters rows. If the table visualization isn't expanded # the first row are the name of the parameters and the second one the locations self.parameter_table_row_shift = 2 # connect the signals on the widget for new detected data by its tools # and to select the element in the table. Binding the element click to the table self.widget.toolDataDetected.connect(self.update_status_bar) self.widget.elementClicked.connect(self.select_element) self.tableParameterOscilogram.setSelectionBehavior( QAbstractItemView.SelectRows) self.tableParameterOscilogram.setEditTriggers( QAbstractItemView.NoEditTriggers) self.dockWidgetParameterTableOscilogram.setVisible(False) self.tableParameterOscilogram.setSortingEnabled(False) # connect the table parameter sort type actions self.actionGroupNone.triggered.connect( self.groupTableParametersChanged) self.actionGroupByParameter.triggered.connect( self.groupTableParametersChanged) self.actionGroupByLocation.triggered.connect( self.groupTableParametersChanged) # configuration variable for the parameter table visualization sort self.parameter_table_sort_type = self.PARAMETER_SORT_BY_PARAMETER # add the context menu actions to the widget self.__addContextMenuActions() # create the progress bar that is showed while the detection is made self.windowProgressDetection = QProgressBar(self) self.set_progress_bar_visibility(False) # array of windows with two dimensional graphs self.two_dim_windows = [] self._cross_correlation_windows = [] if workspace is not None: self.load_workspace(workspace) if parent is not None: # show window in the center of the parent parent_x, parent_y = parent.x(), parent.y() parent_width, parent_height = parent.width(), parent.height() a = str('a') window_x = parent_x + (parent_width - self.width()) / 2 window_y = parent_y + (parent_height - self.height()) / 2 self.move(window_x, window_y) self.show() self.try_restore_previous_session() def try_restore_previous_session(self): """ Restore (if any) the previous session with this file. That means detected elements, measured parameters etc that are saved on the signal extra data. :return: """ elements = self.widget.get_signal_segmentation_data() if len(elements) == 0: return buttons_box = QMessageBox.Yes | QMessageBox.No mbox = QMessageBox( QMessageBox.Question, self.tr(u"soundLab"), self. tr(u"The file has segmentation data stored. Do you want to load it?" ), buttons_box, self) result = mbox.exec_() if result == QMessageBox.Yes: for i, e in enumerate(elements): self.segmentManager.add_element(i, e[0], e[1]) self.widget.elements = self.segmentManager.elements self.segmentManager.measure_parameters_and_classify() self.widget.graph() def __addContextMenuActions(self): """ Add the context menu actions into the widget in the creation process of the window :return: """ separator, separator1, separator2, separator3, separator4 = [ QAction(self) for _ in xrange(5) ] for sep in [separator, separator1, separator2, separator3, separator4]: sep.setSeparator(True) # region Context Menu Actions self.widget.createContextCursor([ # parameters manipulation actions self.actionMeditions, self.actionView_Parameters, self.actionAddElement, separator2, # Zoom Actions self.actionZoomIn, self.actionZoom_out, self.actionZoom_out_entire_file, separator1, # widgets visibility actions self.actionCombined, self.actionOscilogram, self.actionSpectogram, separator, # change tools actions self.actionZoom_Cursor, self.actionPointer_Cursor, self.actionRectangular_Cursor, separator3, # elements select/deselect self.actionDeselect_Elements, self.actionDelete_Selected_Elements, self.actionSelectedElement_Correlation, self.actionClassify, separator4 ]) # endregion # add the action to switch visualization mode on the parameter table self.tableParameterOscilogram.setContextMenuPolicy( QtCore.Qt.ActionsContextMenu) parameter_tables_action_group = QActionGroup( self.tableParameterOscilogram) actions = [ self.actionGroupNone, self.actionGroupByLocation, self.actionGroupByParameter ] for act in actions: parameter_tables_action_group.addAction(act) self.tableParameterOscilogram.addAction(act) def configureToolBarActionsGroups(self): """ :return: """ sep = QAction(self) sep.setSeparator(True) # region Segmentation and Transformations actions segm_transf_actions = QActionGroup(self) segm_transf_actions_list = [ self.actionDetection, self.actionTwo_Dimensional_Graphs, self.actionDelete_Selected_Elements, self.actionDeselect_Elements, self.actionAddElement, self.actionParameter_Measurement, sep ] for act in segm_transf_actions_list: act.setActionGroup(segm_transf_actions) # endregion # add to the customizable sound lab toolbar first than the default actions # addActionGroup(actionGroup, name) self.toolBar.addActionGroup(segm_transf_actions, u"Segments and Parameters") # apply the common sound lab window toolbar actions SoundLabWindow.configureToolBarActionsGroups(self) # endregion # region Two Dimensional Graphs @pyqtSlot() def on_actionTwo_Dimensional_Graphs_triggered(self): """ Creates a new two dimensional window for analysis. :return: """ # a two dim window must be created after # segment detection and parameters measurement if self.segmentManager.rowCount == 0 or len( self.segmentManager.parameterColumnNames) == 0: QMessageBox.warning( QMessageBox(), self.tr(u"Error"), self.tr( u"The two dimensional analyses window requires at least " u"one detected element with one parameter measurement.")) return wnd = TwoDimensionalAnalisysWindow(self, self.segmentManager) # connect the signals for update the new two dim window actions wnd.elementSelected.connect(self.select_element) wnd.elementsClassified.connect( lambda indexes_list, classification: self.segmentManager. set_manual_elements_classification(indexes_list, classification)) # load the workspace in the new two dimensional window if self.workSpace: wnd.load_workspace(self.workSpace) # if any previous windows was opened then update in the new one the selected element if len(self.two_dim_windows) > 0: wnd.select_element(self.two_dim_windows[0].selectedElementIndex) # add the new window to the current opened windows self.two_dim_windows.append(wnd) def clear_two_dim_windows(self): """ Close the two dimensional windows and clear the list of two dim windows :return: """ # close the opened windows for window in self.two_dim_windows: window.close() del window # clear the list of two dimensional windows self.two_dim_windows = [] # endregion # region Save Measurements @pyqtSlot() def on_actionMeditions_triggered(self): """ Save to disc the measurement made by the window to the elements detected. :param name: The name of the file to save the data :param table: The table with the parameter to save into excel :return: True if successfully False otherwise """ file_name = unicode( QFileDialog.getSaveFileName( self, self.tr(u"Save parameters as excel file"), os.path.join(self.workSpace.lastOpenedFolder, str(self.widget.signalName) + ".xls"), "*.xls")) # save the data of table if not file_name: return False # if successfully selected the path save the excel book of data try: wb = xlwt.Workbook() ws = wb.add_sheet("Elements Measurements") self.write_data(ws, self.tableParameterOscilogram) wb.save(file_name) self.segmentManager.save_data_on_db() except Exception as ex: print("Error saving the excel file. " + ex.message) return False # open user manual file os.system("start " + file_name) return True @pyqtSlot() def on_actionSound_File_Segmentation_triggered(self): """ Save the signal into file. Store the segmentation values (list of tuples start,end with the indexes of detected element) :return: """ if self.segmentManager.rowCount == 0: return self.widget.save_segments_into_signal() # save the signal to file if self.widget.signalFilePath: self.widget.save() else: file_name = unicode( QFileDialog.getSaveFileName( self, self.tr(u"Save signal"), os.path.join(self.workSpace.lastOpenedFolder, str(self.widget.signalName)), u"*.wav")) if file_name: self.widget.save(file_name) self.widget.signalFilePath = file_name def set_signal_file(self, file_path=''): """ Update the data of the current signal file path origin in the widget (if any) :param file_path: :return: """ self.widget.signalFilePath = file_path def write_data(self, ws, table_parameter): """ Write the data from the table into an excel file stylesheet. :param ws:WorkSheet object from xwlt module for interacts with excell files. :param table_parameter: QTableWidget with the information of the data to save. """ # write headers into the document headers = [] for pos in xrange(table_parameter.columnCount()): header_item = table_parameter.takeHorizontalHeaderItem(pos) if header_item is not None: headers.append(str(header_item.text())) for index, header in enumerate(headers): ws.write(0, index, header, self.EXCEL_STYLE_HEADER) # write data into the document for i in xrange(1, table_parameter.model().rowCount() + 1): for j in xrange(table_parameter.model().columnCount()): item = table_parameter.item(i - 1, j) if item is not None: cell_data = str(item.data(Qt.DisplayRole).toString()) ws.write(i, j, cell_data, self.EXCEL_STYLE_BODY) # ws object must be part of a Work book that would be saved later ws.write(table_parameter.model().rowCount() + 3, 0, unicode(self.tr(u"duetto-Sound Lab")), self.EXCEL_STYLE_COPYRIGHT) # endregion # region Close and Exit @pyqtSlot() def on_actionExit_triggered(self): """ Process the exit action requested by the user. :return: """ self.close() def closeEvent(self, event): """ Event that manages the close of the window. Intercepts the close event for save changes. :param event: :return: """ mbox = QMessageBox( QMessageBox.Question, self.tr(u"Save measurements"), self.tr(u"Do you want to save the parameters of " + unicode(self.widget.signalName)), QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, self) # if the signal was playing must be stopped self.widget.stop() self.segmentManager.stop_processing() # if there is a measurement made and parameters measured that could be saved if self.tableParameterOscilogram.rowCount() > 0: result = mbox.exec_() # check if the user decision about save is discard and continue working if result == QMessageBox.Cancel or ( result == QMessageBox.Yes and not self.on_actionMeditions_triggered()): event.ignore() return self.clear_two_dim_windows() # endregion # region Elements Selection @pyqtSlot() def on_actionDelete_Selected_Elements_triggered(self): """ Delete the element under selection. the selection is the area under the zoom cursor if the zoom cursor is selected or the visible area otherwise. :return: """ # delete the elements on the widget and get the indexes for update deleted_elements = self.widget.selected_elements_interval() self.widget.delete_selected_elements() if deleted_elements is None: return start_removed_index, end_removed_index = deleted_elements if start_removed_index is not None and start_removed_index >= 0 \ and end_removed_index < self.segmentManager.rowCount: # updates the detected elements self.segmentManager.delete_elements(start_removed_index, end_removed_index) for wnd in self.two_dim_windows: wnd.update_data(self.segmentManager) # deselect the elements on the widget self.on_actionDeselect_Elements_triggered() @pyqtSlot() def on_actionDelete_All_triggered(self): """ Removes all the detected elements on the widget :return: """ # clear the segments self.segmentManager.elements = [] # removes the widget elements self.widget.elements = [] self.widget.graph() @pyqtSlot() def on_actionAddElement_triggered(self): """ Try to add the selected region on the widget as a new element Performs the manual segmentation. :return: """ element_added_index = self.widget.mark_region_as_element() if element_added_index is None: return element = self.widget.get_element(element_added_index) self.segmentManager.add_element(element_added_index, element.indexFrom, element.indexTo) self.update_two_dim_windows() self.widget.graph() self.__set_parameter_window_visible() @pyqtSlot() def on_actionDeselect_Elements_triggered(self): """ Deselects the selected element in the widget and in the two dimensional windows opened. :return: """ self.widget.deselect_element() for wnd in self.two_dim_windows: wnd.deselect_element() # the table remains equal as before for efficiency (do not update for deselect) def select_element(self, element_index, column=0): """ Callback that is executed for update the element that is selected. An element is selected by the user ad must be updated in all visible representations like table parameter, two dimensional windows, and graphs. :param element_index: index of the element selected :param column: parameter provided to reuse this method as callback of the event selected cell in the QTableWidget. Useless in this application context. """ # select the element in the table of parameters self.tableParameterOscilogram.selectRow(element_index + self.parameter_table_row_shift) # in the widget... self.widget.select_element(element_index) # in the opened two dimensional windows... for wnd in self.two_dim_windows: wnd.select_element(element_index) # and in the cross-correlation windows for i in xrange(len(self._cross_correlation_windows)): wnd = self._cross_correlation_windows[i] if wnd.isHidden(): del self._cross_correlation_windows[i] else: wnd.select_element(element_index) # show the image if the element is classified try: classification = self.segmentManager.get_segment_classification( element_index) image = classification.get_image() if image: self.show_image_on_element(element_index, image) self.update_status_bar(classification.get_full_description()) except Exception as ex: pass @pyqtSlot() def on_actionSelectedElement_Correlation_triggered(self): signal = self.widget.selected_element_signal() if not signal: QMessageBox.warning(QMessageBox(), self.tr(u"Error"), self.tr(u"There is no selected element.")) return dialog = CrossCorrelationDialog(self, self.widget, signal, self.TABLE_ROW_COLOR_ODD, self.TABLE_ROW_COLOR_EVEN) self._cross_correlation_windows.append(dialog) dialog.elementSelected.connect(self.select_element) dialog.show() @pyqtSlot() def on_actionClassify_triggered(self): """ Open the classification dialog for update the categories and values in which could be classified a segment. """ # create and open the dialog to edit the classification categories selection = self.widget.selected_elements_interval() if selection is None: QMessageBox.warning(QMessageBox(), self.tr(u"Warning"), self.tr(u"There is no selection made.")) return index_from, index_to = selection classification_dialog = ManualClassificationDialog() if classification_dialog.exec_(): classification = classification_dialog.get_classification() self.segmentManager.set_manual_elements_classification( xrange(index_from, index_to + 1), classification) # endregion # region Classification def show_image_on_element(self, element_index, image): """ show a toast with the specie image (if any and if is identified) of the element at element index position :param element_index: :return: """ toast = ToastWidget(self, back_image=image, width=100, heigth=100) element = self.segmentManager.elements[element_index] min_x, max_x = self.widget.get_visible_region() x = element.indexFrom - min_x + (element.indexTo - element.indexFrom) / 2.0 x = x * self.widget.width() * 1.0 / (max_x - min_x) toast.move( self.widget.mapToGlobal( QPoint(x - toast.width() / 2.0, (self.widget.height() - toast.height()) / 2.0))) toast.disappear() @pyqtSlot() def on_actionCross_correlation_triggered(self): """ Opens the cross-correlation dialog (after selecting a file containing a reference segment) to check each segment's match with a reference segment. """ file_name = QFileDialog.getOpenFileName( self, self.tr(u"Select the file of a reference segment"), directory=self.workSpace.lastOpenedFile, filter=self.tr(u"Wave Files") + u"(*.wav);;All Files(*)") if file_name: dialog = CrossCorrelationDialog(self, self.widget, file_name, self.TABLE_ROW_COLOR_ODD, self.TABLE_ROW_COLOR_EVEN) self._cross_correlation_windows.append(dialog) dialog.elementSelected.connect(self.select_element) dialog.show() # endregion # region Detection def set_progress_bar_visibility(self, visibility=True): """ Show the progress bar in the middle of the widget. Used when a high time demanding task is going to be made to show to the user it's progress. :return: """ if not visibility: self.windowProgressDetection.setVisible(False) else: width, height = self.width(), self.height() x, y = self.widget.x(), self.widget.y() progress_bar_height = height / 20.0 self.windowProgressDetection.resize(width / 3.0, progress_bar_height) self.windowProgressDetection.move( x + width / 3.0, y + height / 2.0 + progress_bar_height / 2.0) self.windowProgressDetection.setVisible(True) self.repaint() @pyqtSlot() def on_actionDetection_triggered(self): """ Method that execute the detection """ # there is an on going detection been made if self.windowProgressDetection.isVisible(): QMessageBox.warning( QMessageBox(), self.tr(u"Error"), self.tr(u"There is an on going detection in progress.")) return elementsDetectorDialog = ElemDetectSettingsDialog( self, self.widget.signal, self.segmentManager) elementsDetectorDialog.modifyParametersMeasurement.connect( lambda: self.on_actionParameter_Measurement_triggered()) elementsDetectorDialog.load_workspace(self.workSpace) try: if elementsDetectorDialog.exec_(): # the detection dialog is a factory of segmentation, # parameter parameters and classification concrete implementations # get the segmentation, classification and parameters methods self.segmentManager.detector_adapter = elementsDetectorDialog.detector self.segmentManager.classifier_adapter = elementsDetectorDialog.classifier self.segmentManager.parameters = self.parameter_manager.parameter_list( ) self.windowProgressDetection.setValue(0) self.set_progress_bar_visibility(True) # execute the detection self.segmentManager.detect_elements() except Exception as e: print("detection errors: " + e.message) self.windowProgressDetection.setValue(100) self.set_progress_bar_visibility(False) def segmentation_finished(self): """ Callback to execute when the segmentation segmentation_thread finished. :return: """ self.windowProgressDetection.setValue(90) # put the elements detected into the widget to visualize them self.widget.elements = self.segmentManager.elements # shows the detection elements first and later the measurements as lazy load self.widget.draw_elements() self.windowProgressDetection.setValue(100) self.set_progress_bar_visibility(False) # measure the parameters over elements detected QTimer.singleShot(50, self.segmentManager.measure_parameters_and_classify) def measurement_finished(self): """ Callback to execute when the measurement has finished. :return: """ # must be refreshed the widget because the parameter measurement # may include visual items into the graph self.widget.draw_elements() self.update_two_dim_windows() self.update_parameter_table() self.__set_parameter_window_visible() def __set_parameter_window_visible(self): # set the parameter window to visible state after measurements if not self.tableParameterOscilogram.isVisible(): self.actionView_Parameters.setChecked(True) self.dockWidgetParameterTableOscilogram.setVisible(True) def update_two_dim_windows(self): # update the measured data on the two dimensional opened windows for wnd in self.two_dim_windows: wnd.load_data(self.segmentManager) def groupTableParametersChanged(self): grouped_params = self.actionGroupByParameter.isChecked() grouped_locations = self.actionGroupByLocation.isChecked() grouped_none = self.actionGroupNone.isChecked() self.parameter_table_row_shift = 0 if grouped_none else 2 if grouped_none: self.parameter_table_sort_type = self.PARAMETER_SORT_NONE elif grouped_params: self.parameter_table_sort_type = self.PARAMETER_SORT_BY_PARAMETER elif grouped_locations: self.parameter_table_sort_type = self.PARAMETER_SORT_BY_LOCATION self.update_parameter_table() self.on_actionDeselect_Elements_triggered() # endregion # region Parameter Table def _group_measurements(self, parameters): """ group the measurements for display in table :param parameters: the parameters measurements to group :return: list of groups (list) grouped by location or parameter accord to the instance variable parameter_table_sort_type """ groups = [] def group_item_selector(param): if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_LOCATION: return "-" if param.time_location is None else param.time_location.name return param.name param_positions = [(parameters[i], i) for i in xrange(len(parameters))] comparer = lambda x, y: cmp(group_item_selector(x[0]), group_item_selector(y[0])) param_positions.sort(cmp=comparer) index = 0 while index < len(param_positions): current_group = [] current_item = group_item_selector(param_positions[index][0]) while index < len(parameters) and group_item_selector( param_positions[index][0]) == current_item: current_group.append(param_positions[index][0]) current_item = group_item_selector(param_positions[index][0]) index += 1 groups.append(current_group) return groups, [t[1] for t in param_positions] def _get_table_widget_item_centered(self, text): """ return a TableWidgetItem with the text supplied and centered vertical and horizontally :param text: text of the item :return: """ item = QTableWidgetItem(unicode(text)) item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) return item def __fill_parameter_table_measurements(self, param_indexes): """ Fill the values of the table parameters using the param indexes from the parameter sort defined by user. Is a helper method of the update parameter table :param param_indexes: :return: """ # update every measurement [row, column] position for i in xrange(self.segmentManager.rowCount): for j in xrange(self.segmentManager.columnCount): # the last columns aren't for parameters but for classification param_index = param_indexes[j] if j < len(param_indexes) else j item = self._get_table_widget_item_centered( self.segmentManager[i, param_index]) # color options for the rows of the table item.setBackgroundColor(self.TABLE_ROW_COLOR_ODD if (i + self.parameter_table_row_shift) % 2 == 0 else self.TABLE_ROW_COLOR_EVEN) self.tableParameterOscilogram.setItem( i + self.parameter_table_row_shift, j, item) def __fill_parameter_table_grouped_columns_headers(self, params_groups, indexes): # set the span value to group the param names columns and their names column_index = 0 parameters = [] for g in params_groups: parameters.extend(g) for j in xrange(len(parameters)): if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_LOCATION: text = parameters[j].name else: text = "-" if parameters[j].time_location is None else unicode( parameters[j].time_location.name) item = self._get_table_widget_item_centered(text) self.tableParameterOscilogram.setItem(1, j, item) # set the group names on the row 0 for param_group in params_groups: span_size = len(param_group) if span_size > 1: self.tableParameterOscilogram.setSpan(0, column_index, 1, span_size) if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_PARAMETER: text = param_group[0].name else: text = "-" if param_group[0].time_location is None else unicode( param_group[0].time_location.name) item = self._get_table_widget_item_centered(text) self.tableParameterOscilogram.setItem(0, column_index, item) column_index += span_size # put the classification category headers for index, category_classif in enumerate( self.segmentManager.classificationColumnNames): item = self._get_table_widget_item_centered(category_classif) self.tableParameterOscilogram.setItem(0, column_index + index, item) def update_parameter_table(self): """ Method that updates the parameter table to visualize the data of the detected segments, their measured parameters and classification :return: """ parameters = self.segmentManager.parameters self.tableParameterOscilogram.clear() self.tableParameterOscilogram.setRowCount( self.segmentManager.rowCount + self.parameter_table_row_shift) self.tableParameterOscilogram.setColumnCount( self.segmentManager.columnCount) header_labels = self.segmentManager.columnNames if self.parameter_table_sort_type != self.PARAMETER_SORT_NONE: # get the list of parameters grouped by type params_groups, indexes = self._group_measurements(parameters) header_labels = ["" for _ in self.segmentManager.columnNames] # set the vertical names of rows. Parameters on first row and locations on second vertical_headers = [ "Parameter" if s == 0 else "Location" if s == 1 else str(s - self.parameter_table_row_shift + 1) for s in xrange(self.segmentManager.rowCount + self.parameter_table_row_shift) ] if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_LOCATION: swap = vertical_headers[0] vertical_headers[0] = vertical_headers[1] vertical_headers[1] = swap self.tableParameterOscilogram.setVerticalHeaderLabels( vertical_headers) self.__fill_parameter_table_grouped_columns_headers( params_groups, indexes) self.__fill_parameter_table_measurements(indexes) else: self.__fill_parameter_table_measurements(xrange(len(parameters))) self.tableParameterOscilogram.setHorizontalHeaderLabels(header_labels) # connect the table selection with the selection of an element cell_pressed_element = lambda index, col: self.select_element( index - self.parameter_table_row_shift, col) self.tableParameterOscilogram.cellPressed.connect(cell_pressed_element) self.tableParameterOscilogram.resizeColumnsToContents() self.tableParameterOscilogram.resizeRowsToContents() # endregion # region Visual Elements @pyqtSlot() def on_actionView_Parameters_triggered(self): """ Changes the visibility on the window of the parameter table. The parameter table is where the detected segments and its measured parameters are displayed. """ self.dockWidgetParameterTableOscilogram.setVisible( self.actionView_Parameters.isChecked()) @pyqtSlot() def on_actionTemporal_Elements_triggered(self): """ Temporal Elements are the elements that are visible on the oscilogram graph. This method allows to change its visibility """ visibility = self.actionTemporal_Elements.isChecked() self.widget.visual_items_visibility.oscilogram_items_visible = visibility self.widget.draw_elements() for x in [ self.actionTemporal_Figures, self.actionTemporal_Numbers, self.actionTemporal_Parameters ]: x.setEnabled(visibility) @pyqtSlot() def on_actionTemporal_Figures_triggered(self): self.widget.visual_items_visibility.oscilogram_figures_visible = self.actionTemporal_Figures.isChecked( ) self.widget.draw_elements() @pyqtSlot() def on_actionTemporal_Parameters_triggered(self): self.widget.visual_items_visibility.oscilogram_parameters_visible = self.actionTemporal_Parameters.isChecked( ) self.widget.draw_elements() @pyqtSlot() def on_actionTemporal_Numbers_triggered(self): """ Change visibility of the numbers of the detected segments on the oscilogram graph """ self.widget.visual_items_visibility.oscilogram_text_visible = self.actionTemporal_Numbers.isChecked( ) self.widget.draw_elements() @pyqtSlot() def on_actionSpectral_Elements_triggered(self): """ Spectral Elements are the elements that are visible on the spectrogram graph. This method allows to change its visibility """ visibility = self.actionSpectral_Elements.isChecked() self.widget.visual_items_visibility.spectrogram_items_visible = visibility self.widget.draw_elements() for x in [ self.actionSpectral_Numbers, self.actionSpectral_Figures, self.actionSpectral_Parameters ]: x.setEnabled(visibility) @pyqtSlot() def on_actionSpectral_Figures_triggered(self): self.widget.visual_items_visibility.spectrogram_figures_visible = self.actionSpectral_Figures.isChecked( ) self.widget.draw_elements() @pyqtSlot() def on_actionSpectral_Parameters_triggered(self): self.widget.visual_items_visibility.spectrogram_parameters_visible = self.actionSpectral_Parameters.isChecked( ) self.widget.draw_elements() @pyqtSlot() def on_actionSpectral_Numbers_triggered(self): """ Change visibility of the numbers of the detected segments on the oscilogram graph """ self.widget.visual_items_visibility.spectrogram_text_visible = self.actionSpectral_Numbers.isChecked( ) self.widget.draw_elements() # endregion def update_parameters(self, parameter_manager): """ Updates the parameters to measure from a change in their selection. :param parameter_manager: :return: """ self.widget.release_items(parameter_items=True, elements_items=False) # update the segment manager that measure the new params self.segmentManager.parameters = parameter_manager.parameter_list() self.measurement_finished() @pyqtSlot() def on_actionParameter_Measurement_triggered(self): # check for previously parameters measurements to save # there is measured parameters when the list of their names has at least one element if len(self.segmentManager.parameterColumnNames) > 0: mbox = QMessageBox( QMessageBox.Question, self.tr(u"soundLab"), self.tr( u"You are going to change the parameters to measure." u"All your previously selected measurements will be lost." u"Do you want to save measurements first?"), QMessageBox.Yes | QMessageBox.No, self) if mbox.exec_() == QMessageBox.Yes: self.on_actionMeditions_triggered() param_window = ParametersWindow(self, self.parameter_manager, self.widget.signal, workspace=self.workSpace) param_window.parameterChangeFinished.connect( lambda p: self.update_parameters(p)) param_window.show() def load_workspace(self, workspace): """ Method that loads the workspace to update visual options from main window. :param workspace: """ self.workSpace = workspace self.widget.load_workspace(workspace) # update workspace on every window for wnd in self.two_dim_windows: wnd.load_workspace(self.workSpace)
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0, 4, 0, 0) self.setSpacing(0) self.timeControlFontSize = 12 def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def hideTimeSlider(self, flag): visibleBefore = not self.timeSlider.isHidden() self.timeSlider.setHidden(flag) self.timeEndButton.setHidden(flag) self.timeStartButton.setHidden(flag) self.timePreviousButton.setHidden(flag) self.timeNextButton.setHidden(flag) self._registerTimeframeShortcuts(enabled=not flag, remove=visibleBefore) def setToolTipTimeButtonsCrop(self, croppingFlag=False): if croppingFlag == True: self.timeStartButton.setToolTip( "Jump to lirst frame of current crop") self.timeEndButton.setToolTip("Jump to last frame of current crop") self.timePreviousButton.setToolTip("Previous Frame") self.timeNextButton.setToolTip("Next Frame") else: self.timeStartButton.setToolTip("First Frame") self.timeEndButton.setToolTip("Last Frame") self.timePreviousButton.setToolTip("Previous Frame") self.timeNextButton.setToolTip("Next Frame") def setToolTipTimeSliderCrop(self, croppingFlag=False): if croppingFlag == True: self.timeSlider.setToolTip( "Choose the time coordinate of the current crop.") else: self.timeSlider.setToolTip( "Choose the time coordinate of the current dataset.") def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x')) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y')) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z')) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.crosshairsCheckbox = QCheckBox() self.crosshairsCheckbox.setChecked(False) self.crosshairsCheckbox.setCheckable(True) self.crosshairsCheckbox.setText("Crosshairs") self.addWidget(self.crosshairsCheckbox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximumWidth(200) self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget(self.busyIndicator) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(20) self.timeSpinBox = DelayedSpinBox(750) icons_dir = os.path.dirname(volumina.__file__) + "/icons" self.timeStartButton = QToolButton() self.timeStartButton.setIcon(QIcon(icons_dir + "/skip-start.png")) self.addWidget(self.timeStartButton) self.timeStartButton.clicked.connect(self._onTimeStartButtonClicked) #self.timeStartButton.setFixedWidth(4*self.timeControlFontSize) self.timePreviousButton = QToolButton() self.timePreviousButton.setIcon(QIcon(icons_dir + "/play-reverse.png")) self.addWidget(self.timePreviousButton) self.timePreviousButton.clicked.connect( self._onTimePreviousButtonClicked) #self.timePreviousButton.setFixedWidth(4*self.timeControlFontSize) self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimumWidth(10) self.timeSlider.setMaximumWidth(200) self.setToolTipTimeSliderCrop() self.addWidget(self.timeSlider) self.timeSlider.valueChanged.connect(self._onTimeSliderChanged) self.timeNextButton = QToolButton() self.timeNextButton.setIcon(QIcon(icons_dir + "/play.png")) self.addWidget(self.timeNextButton) self.timeNextButton.clicked.connect(self._onTimeNextButtonClicked) #self.timeNextButton.setFixedWidth(4*self.timeControlFontSize) self.timeEndButton = QToolButton() self.timeEndButton.setIcon(QIcon(icons_dir + "/skip-end.png")) #self.timeEndButton.setFixedWidth(4*self.timeControlFontSize) self.setToolTipTimeButtonsCrop() self.addWidget(self.timeEndButton) self.timeEndButton.clicked.connect(self._onTimeEndButtonClicked) self.timeLabel = QLabel(" Time:") self.addWidget(self.timeLabel) timeControlFont = self.timeSpinBox.font() if self.timeControlFontSize > timeControlFont.pointSize(): timeControlFont.setPixelSize(2 * self.timeControlFontSize) self.timeStartButton.setFont(timeControlFont) self.timeEndButton.setFont(timeControlFont) self.timeLabel.setFont(timeControlFont) self.timeSpinBox.setFont(timeControlFont) self.addWidget(self.timeSpinBox) self.timeSpinBox.delayedValueChanged.connect( self._onTimeSpinBoxChanged) self._registerTimeframeShortcuts() def _registerTimeframeShortcuts(self, enabled=True, remove=True): """ Register or deregister "," and "." as keyboard shortcuts for scrolling in time """ mgr = ShortcutManager() ActionInfo = ShortcutManager.ActionInfo def action(key, actionInfo): if enabled: mgr.register(key, actionInfo) else: if remove: mgr.unregister(actionInfo) action( "<", ActionInfo("Navigation", "Go to next time frame", "Go to next time frame", self._onTimeNextButtonClicked, self.timeNextButton, self.timeNextButton)) action( ">", ActionInfo("Navigation", "Go to previous time frame", "Go to previous time frame", self._onTimePreviousButtonClicked, self.timePreviousButton, self.timePreviousButton)) def _onTimeStartButtonClicked(self): self.timeSpinBox.setValue( self.parent().parent().parent().editor.cropModel.get_roi_t()[0]) def _onTimeEndButtonClicked(self): self.timeSpinBox.setValue( self.parent().parent().parent().editor.cropModel.get_roi_t()[1]) def _onTimePreviousButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value() - 1) def _onTimeNextButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value() + 1) def _onTimeSpinBoxChanged(self): editor = self.parent().parent().parent().editor cropModel = editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): if minValueT > self.timeSpinBox.value( ) or maxValueT < self.timeSpinBox.value(): for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame( True) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame( False) self.timeSlider.setValue(self.timeSpinBox.value()) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame( False) if minValueT > self.timeSpinBox.value(): self.timeSlider.setValue(minValueT) elif maxValueT < self.timeSpinBox.value(): self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSpinBox.value( ) and self.timeSpinBox.value() <= maxValueT: self.timeSlider.setValue(self.timeSpinBox.value()) def _onTimeSliderChanged(self): cropModel = self.parent().parent().parent().editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): self.timeSpinBox.setValue(self.timeSlider.value()) else: if minValueT > self.timeSlider.value(): self.timeSpinBox.setValue(minValueT) self.timeSlider.setValue(minValueT) elif self.timeSlider.value() > maxValueT: self.timeSpinBox.setValue(maxValueT) self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSlider.value( ) and self.timeSlider.value() <= maxValueT: self.timeSpinBox.setValue(self.timeSlider.value()) def _handlePositionBoxValueChanged(self, axis, value): new_position = [ self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value() ] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0] - 1) self.xSpinBox.setMaximum(shape5D[1] - 1) self.ySpinBox.setMaximum(shape5D[2] - 1) self.zSpinBox.setMaximum(shape5D[3] - 1) def updateShape5Dcropped(self, shape5DcropMin, shape5Dmax): self.timeSpinBox.setMaximum(shape5Dmax[0] - 1) self.xSpinBox.setMaximum(shape5Dmax[1] - 1) self.ySpinBox.setMaximum(shape5Dmax[2] - 1) self.zSpinBox.setMaximum(shape5Dmax[3] - 1) self.timeSlider.setMaximum(shape5Dmax[0] - 1) self.timeSpinBox.setValue(shape5DcropMin[0]) self.xSpinBox.setValue(shape5DcropMin[1]) self.ySpinBox.setValue(shape5DcropMin[2]) self.zSpinBox.setValue(shape5DcropMin[3]) self.timeSlider.setValue(shape5DcropMin[0]) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0, 4, 0, 0) self.setSpacing(0) def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def createQuadViewStatusBar( self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor ): self.xLabel, self.xSpinBox = _get_pos_widget("X", xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget("Y", ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget("Z", zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect(partial(self._handlePositionBoxValueChanged, "x")) self.ySpinBox.delayedValueChanged.connect(partial(self._handlePositionBoxValueChanged, "y")) self.zSpinBox.delayedValueChanged.connect(partial(self._handlePositionBoxValueChanged, "z")) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget(self.busyIndicator) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(10) self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(True) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.timeLabel = QLabel("Time:") self.addWidget(self.timeLabel) self.timeSpinBox = DelayedSpinBox(750) self.addWidget(self.timeSpinBox) def _handlePositionBoxValueChanged(self, axis, value): new_position = [self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value()] changed_axis = ord(axis) - ord("x") new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0] - 1) self.xSpinBox.setMaximum(shape5D[1] - 1) self.ySpinBox.setMaximum(shape5D[2] - 1) self.zSpinBox.setMaximum(shape5D[3] - 1) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class ConversationsWinGui(BaseWindow, QtGui.QMainWindow): refreshList = QtCore.pyqtSignal() loggedOut = QtCore.pyqtSignal() windowClosed = QtCore.pyqtSignal() def __init__(self, session, parent=None): super(ConversationsWinGui, self).__init__(parent=parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.activeConversationWindows = {} self.newConversationWindow = None self.session = session sharedData.messages = getMessages(self.session.username) or [] self.ui.refresh.clicked.connect(self.refresh) self.ui.logout.triggered.connect(self.logout) self.refreshShortcut = QtGui.QShortcut(QtGui.QKeySequence("F5"), self) self.refreshShortcut.activated.connect(self.refresh) self.setPeerShortcut = QtGui.QShortcut(QtGui.QKeySequence("Return"), self) self.setPeerShortcut.activated.connect(self.refresh) self.setPeerShortcut.setEnabled(False) self.refreshList.connect(self.refreshListSlot) self.ui.newConversation.clicked.connect(self.startNewConversation) self.ui.conversationsList.itemClicked.connect(self.conversationSelected) self.ui.unauthorizeClient.triggered.connect(self.unauthorize) self.ui.deleteMessages.triggered.connect(self.clearMessages) self.ui.setPassword.triggered.connect(self.setPassword) self.ui.markAllAsRead.triggered.connect(self.markAllAsRead) self.ui.renameUser.triggered.connect(self.renameUser) self.progressIndicator = QProgressBar(self.ui.statusBar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.ui.statusBar.addWidget(self.progressIndicator) self.uiTranslate() self.refresh() def uiTranslate(self): self.ui.refresh.setText(strings["refreshButtonText"]) self.ui.logout.setText(strings["logoutButtonText"]) self.ui.unauthorizeClient.setText(strings["unauthorizeClientButtonText"]) self.ui.deleteMessages.setText(strings["deleteAllMessagesButtonText"]) self.ui.setPassword.setText(strings["setPasswordWindowTitle"]) self.setWindowTitle(strings["appName"] + ": " + self.session.username) self.ui.newConversation.setText(strings["startNewConversationButtonText"]) self.ui.menuTools.setTitle(strings["toolsMenuText"]) self.ui.markAllAsRead.setText(strings["markAllAsReadButtonText"]) self.ui.renameUser.setText(strings["renameUserButtonText"]) def progressStart(self): self.progressIndicator.setVisible(True) def askForConfirmation(self, message): confirmationBox = QtGui.QMessageBox(parent=self, text=message) confirmationBox.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) confirmationBox.setWindowTitle(strings["appName"]) return confirmationBox.exec() == QtGui.QMessageBox.Yes def progressStop(self): self.progressIndicator.setVisible(False) def show(self): super(ConversationsWinGui, self).show() self.refreshList.emit() def refresh(self): self.progressStart() def refreshThread(): try: sharedData.messages += self.session.getMessages() except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) print(error) return sharedData.messages = sorted(sharedData.messages, key=messageSort) storeMessages(self.session.username, sharedData.messages) self.refreshList.emit() Thread(target=refreshThread).start() def refreshListSlot(self, refreshChildren=True): seenPeers = set() self.ui.conversationsList.clear() for message in reversed(sharedData.messages): if not message.messagePeer in seenPeers: seenPeers.add(message.messagePeer) peerName = QListWidgetItem(self.ui.conversationsList) nameFont = QtGui.QFont() contentsFont = QtGui.QFont() nameFont.setPointSize(14) if not message.read: nameFont.setBold(True) contentsFont.setBold(True) peerName.setFont(nameFont) peerName.setText(message.messagePeer) messageContents = QListWidgetItem(self.ui.conversationsList) if message.isFile: contentLines = [message.fileName] elif not message.contents: contentLines = [""] else: contentLines = message.contents.splitlines(keepends=False) contentsFont.setPointSize(10) contents = contentLines[0] if len(contentLines) > 1: contents += "\n" + contentLines[1] if len(contents) > 20: contents = contents[:20] messageContents.setFont(contentsFont) messageContents.setText(contents) self.ui.conversationsList.addItem(peerName) self.ui.conversationsList.addItem(messageContents) separatorItem = QListWidgetItem(self.ui.conversationsList) separatorItem.setFlags(QtCore.Qt.NoItemFlags) self.ui.conversationsList.addItem(separatorItem) if refreshChildren: for peerName in self.activeConversationWindows: self.activeConversationWindows[peerName].refreshDisplay.emit() self.progressStop() def startNewConversation(self): if self.newConversationWindow: self.newConversationWindow.activateWindow() return self.newConversationWindow = MainWinGui(self.session, "") self.newConversationWindow.refreshDisplay.connect(self.refreshTriggeredInChild) self.newConversationWindow.onClose.connect(self.newConversationWindowClosed) self.newConversationWindow.sendCompleted.connect(self.conversationStarted) self.newConversationWindow.show() def conversationStarted(self, message): if message.messagePeer in self.activeConversationWindows: self.activeConversationWindows[message.messagePeer].close() self.newConversationWindow.onClose.disconnect(self.newConversationWindowClosed) self.newConversationWindow.sendCompleted.disconnect(self.conversationStarted) self.newConversationWindow.onClose.connect(lambda peerName: self.activeConversationWindows.pop(peerName)) self.activeConversationWindows[message.messagePeer] = self.newConversationWindow self.newConversationWindow = None def newConversationWindowClosed(self, *args, **kwargs): self.newConversationWindow = None def conversationSelected(self, listItem): row = self.ui.conversationsList.indexFromItem(listItem).row() selectedPeer = "" if row % 3 == 0: selectedPeer += listItem.text() elif row % 3 == 1: selectedPeer += self.ui.conversationsList.item(row - 1).text() else: listItem.setSelected(False) if selectedPeer != "": if selectedPeer in self.activeConversationWindows: self.activeConversationWindows[selectedPeer].activateWindow() else: newWindow = MainWinGui(self.session, selectedPeer) self.activeConversationWindows[selectedPeer] = newWindow newWindow.onClose.connect(lambda peerName: self.activeConversationWindows.pop(peerName)) newWindow.refreshDisplay.connect(self.refreshTriggeredInChild) newWindow.show() def refreshTriggeredInChild(self): self.refreshListSlot(False) def clearMessages(self): if self.askForConfirmation(strings["confirmDeleteAllMessagesText"]): sharedData.messages = [] storeMessages(self.session.username, None) self.refreshList.emit() def setPassword(self): setPasswordUi = SetPasswordGui(self.session, parent=self) setPasswordUi.show() def renameUser(self): renameUi = RenameGui(self.session, parent=self) renameUi.show() def logout(self): self.session.logout() self.loggedOut.emit() def unauthorize(self): if self.askForConfirmation(strings["confirmUnauthorizeClientText"]): try: self.session.unauthorize() except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return storeMessages(self.session.username, None) storeToken(self.session.username, None) storeKey(self.session.username, None, None) self.logout() def closeEvent(self, *args, **kwargs): windowsToClose = [] for window in self.activeConversationWindows: windowsToClose += [self.activeConversationWindows[window]] for window in windowsToClose: window.close() if self.newConversationWindow: self.newConversationWindow.close() self.windowClosed.emit() def markAllAsRead(self): sharedData.markAsRead() storeMessages(self.session.username, sharedData.messages) self.refreshList.emit()
class MainWinGui(BaseWindow, QtGui.QMainWindow): refreshDisplay = QtCore.pyqtSignal() sendCompleted = QtCore.pyqtSignal(Message) fileDownloadCompleted = QtCore.pyqtSignal(bytes, str) onClose = QtCore.pyqtSignal(str) def __init__(self, session, peerName, parent=None): super(MainWinGui, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.session = session self.peerName = peerName if peerName == "": self.ui.peerName.setVisible(False) self.ui.peer.setVisible(True) self.ui.peer.setText("") self.ui.peer.setFocus() else: self.ui.peerName.setVisible(True) self.ui.peer.setVisible(False) self.ui.peer.setText(peerName) self.ui.peerName.setText(peerName) self.ui.message.setFocus() self.ui.refresh.clicked.connect(self.refresh) self.ui.send.clicked.connect(self.send) self.ui.scrollBar = self.ui.display.verticalScrollBar() self.ui.display.setOpenLinks(False) self.ui.display.anchorClicked.connect(self.linkClicked) self.ui.display.anchorClicked.emit(QtCore.QUrl("UP")) self.refreshShortcut = QtGui.QShortcut(QtGui.QKeySequence("F5"), self) self.refreshShortcut.activated.connect(self.refresh) self.enterShortcut = QtGui.QShortcut(QtGui.QKeySequence("Enter"), self) self.returnShortcut = QtGui.QShortcut(QtGui.QKeySequence("Return"), self) self.enterShortcut.activated.connect(self.enterPressed) self.returnShortcut.activated.connect(self.enterPressed) self.ui.message.keyPressEvent = types.MethodType(keyPressEventReplacement, self.ui.message) self.ui.uploadFile.triggered.connect(self.uploadFile) self.ui.deleteMessages.triggered.connect(self.clearMessages) self.ui.stopSigning.triggered.connect(self.clearSigningSecret) self.ui.signMessages.triggered.connect(self.askForSigningSecret) self.ui.message.parentWindow = self self.refreshDisplay.connect(self.refreshDisplaySlot) self.sendCompleted.connect(self.sendCompletedSlot) self.fileDownloadCompleted.connect(self.fileDownloadCompletedSlot) self.progressIndicator = QProgressBar(self.ui.statusBar) self.progressIndicator.setMinimumHeight(5) self.progressIndicator.setVisible(False) self.progressIndicator.setMaximum(0) self.progressIndicator.setMinimum(0) self.signatureIndicator = QLabel(self.ui.statusBar) self.ui.statusBar.addWidget(self.progressIndicator) self.ui.statusBar.addPermanentWidget(self.signatureIndicator) self.signingSecret = None self.uiTranslate() self.refresh() def uiTranslate(self): self.ui.peer.setPlaceholderText(strings["peerBoxHint"]) self.ui.refresh.setText(strings["refreshButtonText"]) self.ui.send.setText(strings["sendMessageButtonText"]) self.ui.deleteMessages.setText(strings["deleteConversationButtonText"]) self.setWindowTitle(strings["conversationWindowTitle"] + ": " + self.peerName) self.ui.menuTools.setTitle(strings["toolsMenuText"]) self.ui.uploadFile.setText(strings["uploadFileButtonText"]) self.ui.stopSigning.setText(strings["stopSigningButtonText"]) self.ui.signMessages.setText(strings["signMessagesButtonText"]) self.signatureIndicator.setText(strings["signatureIndicatorOffText"]) def enterPressed(self): if self.ui.peer.hasFocus(): self.ui.message.setFocus() def progressStart(self): self.progressIndicator.setVisible(True) def askForConfirmation(self, message): confirmationBox = QtGui.QMessageBox(parent=self, text=message) confirmationBox.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) confirmationBox.setWindowTitle(strings["appName"]) return confirmationBox.exec() == QtGui.QMessageBox.Yes def askForSigningSecret(self): response = QInputDialog.getText( self, strings["signatureSecretInputTitle"], strings["signatureSecretInputLabel"] ) if response[1]: self.signingSecret = response[0].encode("utf-8") self.signatureIndicator.setText(strings["signatureIndicatorOnText"]) def clearSigningSecret(self): self.signingSecret = None self.signatureIndicator.setText(strings["signatureIndicatorOffText"]) def progressStop(self): self.progressIndicator.setVisible(False) def fileDownloadCompletedSlot(self, contents, fileName): self.progressStop() try: with open(fileName, mode="wb") as file: file.write(contents) except (SecureMessagingException, IOError) as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return def sendCompletedSlot(self, message): sharedData.messages += [message] self.ui.message.clear() self.ui.peerName.setText(self.ui.peer.text()) self.ui.peer.setVisible(False) self.ui.peerName.setVisible(True) self.peerName = self.ui.peer.text() self.setWindowTitle(strings["conversationWindowTitle"] + ": " + self.peerName) self.refresh() def show(self): super(MainWinGui, self).show() self.refreshDisplay.emit() def linkClicked(self, url): url = url.toString() if url == "UP": return if url.startswith("FILE"): messageId = url[len("FILE") :] messageId = int(messageId) message = sharedData.messages[messageId] fileName = QtGui.QFileDialog.getSaveFileName(directory=message.fileName) if fileName: self.progressStart() def fileDownloaderThread(): try: fileContents = self.session.downloadFile(message) self.fileDownloadCompleted.emit(fileContents, fileName) except (SecureMessagingException, IOError) as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return Thread(target=fileDownloaderThread).start() elif url.startswith("DEL_"): messageId = int(url[len("DEL_") :]) if self.askForConfirmation(strings["confirmDeleteMessageText"]): sharedData.messages.pop(messageId) storeMessages(self.session.username, sharedData.messages) self.refreshDisplay.emit() elif url.startswith("SGN_"): messageId = int(url[len("SGN_") :]) message = sharedData.messages[messageId] response = QInputDialog.getText( self, strings["signatureSecretInputTitle"], strings["signatureSecretInputLabel"] ) if response[1]: if message.verifySignature(response[0].encode("utf-8")): self.showMessageBox.emit(strings["signatureOkMessageText"]) else: self.showMessageBox.emit(strings["signatureBadMessageText"]) def refreshDisplaySlot(self): outputText = '<table width="100%" cellpadding="5" border="" style="padding:15px;width:100%;">' peer = self.ui.peer.text() msgCount = -1 for message in sharedData.messages: msgCount += 1 if message.messagePeer != peer: continue contents = message.contents contents = escape(contents) if message.isFile: contents = ( '<a href ="FILE' + str(sharedData.messages.index(message)) + '">File: ' + message.fileName + "</a>" ) if message.sender == self.session.username: outputText += '<tr style="background-color:#cadced;color:#000000"><td>' else: outputText += '<tr style="background-color:#FFFFFF;color:#000000"><td>' outputText += "<b>" + message.sender + "</b>➩<b>" + message.receiver + "</b>" outputText += "<pre style='word-break:break-all;white-space:pre-wrap;font-family:Arial'>" outputText += contents outputText += "</pre>" outputText += ( "<a style='text-decoration:none;color:#000000' href='" + "DEL_" + str(msgCount) + "'><font size='1'>⌫</font></a>" ) if message.isSigned(): outputText += ( " <a style='text-decoration:none;color:#000000' href='" + "SGN_" + str(msgCount) + "'><font size='1'>✔</font></a>" ) outputText += "<div style='font-size:small;' align='right'>" + message.getTimeStamp() + "</div>" outputText += "</td></tr>" outputText += "</table>" self.ui.display.setHtml(outputText) self.ui.display.verticalScrollBar().setValue(self.ui.display.verticalScrollBar().maximum()) self.progressStop() def refresh(self): self.progressStart() def refreshThread(): try: sharedData.messages += self.session.getMessages() except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return sharedData.messages = sorted(sharedData.messages, key=messageSort) sharedData.markAsRead(self.ui.peer.text()) storeMessages(self.session.username, sharedData.messages) self.refreshDisplay.emit() Thread(target=refreshThread).start() def send(self): self.progressStart() def senderThread(): peer = self.ui.peer.text() contents = self.ui.message.toPlainText() if peer == "" or contents == "": self.showMessageBox.emit(strings["missingPeerOrMessageErrorText"]) return if peer == self.session.username: self.showMessageBox.emit(strings["cantSendMessageToSelfText"]) return sharedData.messages = sorted(sharedData.messages, key=messageSort) try: self.session.sendMessage(contents, peer, signatureSecret=self.signingSecret) except SecureMessagingException as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return self.sendCompleted.emit(Message(contents, self.session.username, peer, peer, self.session.timeDiff)) Thread(target=senderThread).start() def uploadFile(self): peer = self.ui.peer.text() if peer == "": self.showMessageBox.emit(strings["missingPeerErrorText"]) return if peer == self.session.username: self.showMessageBox.emit(strings["cantSendMessageToSelfText"]) return fileName = QtGui.QFileDialog.getOpenFileName() if fileName: self.progressStart() def uploaderThread(): try: fileContents = open(fileName, "rb").read() basename = os.path.basename(fileName) fileType = mimetypes.guess_type("file://" + fileName)[0] or "application/octet-stream" fileType = fileType.encode("ascii") self.session.sendFile(basename, fileContents, peer, fileType) self.sendCompleted.emit(Message(basename, self.session.username, peer, peer, self.session.timeDiff)) except (SecureMessagingException, IOError) as error: self.showMessageBox.emit(strings["errorText"] + responseTranslations[str(error)]) return Thread(target=uploaderThread).start() def clearMessages(self): if self.peerName and self.askForConfirmation(strings["confirmDeleteConversationText"]): numberOfMessages = len(sharedData.messages) deletedMessages = 0 for i in range(numberOfMessages): if sharedData.messages[i - deletedMessages].messagePeer == self.peerName: sharedData.messages.pop(i - deletedMessages) deletedMessages += 1 storeMessages(self.session.username, sharedData.messages) self.refreshDisplay.emit() def closeEvent(self, event): self.onClose.emit(self.ui.peer.text()) super(MainWinGui, self).closeEvent(event)
class UI_Psychrometry(QDialog): """Psychrometric charts tool""" def __init__(self, parent=None): super(UI_Psychrometry, self).__init__(parent) self.showMaximized() self.setWindowTitle("Psychrometric chart") layout = QGridLayout(self) self.diagrama2D = PsychroPlot(self, dpi=90) self.diagrama2D.fig.canvas.mpl_connect('motion_notify_event', self.scroll) layout.addWidget(self.diagrama2D, 1, 1, 1, 2) self.progressBar = QProgressBar() self.progressBar.setVisible(False) layout.addWidget(self.progressBar, 2, 1) self.status = QLabel() layout.addWidget(self.status, 2, 1) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close) butonPNG = QPushButton("Save as PNG") self.buttonBox.addButton(butonPNG, QDialogButtonBox.AcceptRole) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.savePNG) layout.addWidget(self.buttonBox, 2, 2) self.plot() def savePNG(self): """Save chart image to png file""" fname = unicode(QFileDialog.getSaveFileName( self, "Save chart to file", "./", "Portable Network Graphics (*.png)")) self.diagrama2D.fig.savefig(fname, facecolor='#eeeeee') def drawlabel(self, name, Preferences, t, W, label, unit): """ Draw annotation for isolines name: name of isoline Preferences: Configparse instance of pychemqt preferences t: x array of line W: y array of line label: text value to draw unit: text units to draw """ if Preferences.getboolean("Psychr", name+"label"): tmin = Preferences.getfloat("Psychr", "isotdbStart")-273.15 tmax = Preferences.getfloat("Psychr", "isotdbEnd")-273.15 x = tmax-tmin wmin = Preferences.getfloat("Psychr", "isowStart") wmax = Preferences.getfloat("Psychr", "isowEnd") y = wmax-wmin i = 0 for ti, wi in zip(t, W): if tmin <= ti <= tmax and wmin <= wi <= wmax: i += 1 label = str(label) if Preferences.getboolean("Psychr", name+"units"): label += unit pos = Preferences.getfloat("Psychr", name+"position") p = int(i*pos/100-1) rot = np.arctan((W[p]-W[p-1])/y/(t[p]-t[p-1])*x)*360.0/2.0/np.pi self.diagrama2D.axes2D.annotate(label, (t[p], W[p]), rotation=rot, size="small", ha="center", va="center") def plot(self): """Plot chart""" Preferences = ConfigParser() Preferences.read("psyrc") self.diagrama2D.axes2D.clear() self.diagrama2D.config() filename = "%i.pkl" % P if os.path.isfile(filename): with open(filename, "r") as archivo: data = cPickle.load(archivo) self.status.setText("Loading cached data...") QApplication.processEvents() else: self.progressBar.setVisible(True) self.status.setText("Calculating data, be patient...") QApplication.processEvents() data = PsyCoolprop.calculatePlot(self) cPickle.dump(data, open(filename, "w")) self.progressBar.setVisible(False) self.status.setText("Plotting...") QApplication.processEvents() tmax = Preferences.getfloat("Psychr", "isotdbEnd")-273.15 t = [ti-273.15 for ti in data["t"]] Hs = data["Hs"] format = {} format["ls"] = Preferences.get("Psychr", "saturationlineStyle") format["lw"] = Preferences.getfloat("Psychr", "saturationlineWidth") format["color"] = Preferences.get("Psychr", "saturationColor") format["marker"] = Preferences.get("Psychr", "saturationmarker") format["markersize"] = 3 self.diagrama2D.plot(t, Hs, **format) format = {} format["ls"] = Preferences.get("Psychr", "isotdblineStyle") format["lw"] = Preferences.getfloat("Psychr", "isotdblineWidth") format["color"] = Preferences.get("Psychr", "isotdbColor") format["marker"] = Preferences.get("Psychr", "isotdbmarker") format["markersize"] = 3 for i, T in enumerate(t): self.diagrama2D.plot([T, T], [0, Hs[i]], **format) H = data["H"] th = data["th"] format = {} format["ls"] = Preferences.get("Psychr", "isowlineStyle") format["lw"] = Preferences.getfloat("Psychr", "isowlineWidth") format["color"] = Preferences.get("Psychr", "isowColor") format["marker"] = Preferences.get("Psychr", "isowmarker") format["markersize"] = 3 for i, H in enumerate(H): self.diagrama2D.plot([th[i], tmax], [H, H], **format) format = {} format["ls"] = Preferences.get("Psychr", "isohrlineStyle") format["lw"] = Preferences.getfloat("Psychr", "isohrlineWidth") format["color"] = Preferences.get("Psychr", "isohrColor") format["marker"] = Preferences.get("Psychr", "isohrmarker") format["markersize"] = 3 for Hr, H0 in data["Hr"].iteritems(): self.diagrama2D.plot(t, H0, **format) self.drawlabel("isohr", Preferences, t, H0, Hr, "%") format = {} format["ls"] = Preferences.get("Psychr", "isotwblineStyle") format["lw"] = Preferences.getfloat("Psychr", "isotwblineWidth") format["color"] = Preferences.get("Psychr", "isotwbColor") format["marker"] = Preferences.get("Psychr", "isotwbmarker") format["markersize"] = 3 for T, (H, Tw) in data["Twb"].iteritems(): self.diagrama2D.plot(Tw, H, **format) value = T-273.15 txt = u"ºC" self.drawlabel("isotwb", Preferences, Tw, H, value, txt) format = {} format["ls"] = Preferences.get("Psychr", "isochorlineStyle") format["lw"] = Preferences.getfloat("Psychr", "isochorlineWidth") format["color"] = Preferences.get("Psychr", "isochorColor") format["marker"] = Preferences.get("Psychr", "isochormarker") format["markersize"] = 3 for v, (Td, H) in data["v"].iteritems(): self.diagrama2D.plot(Td, H, **format) value = v txt = u"m³/kg" self.drawlabel("isochor", Preferences, Td, H, value, txt) self.diagrama2D.draw() self.status.setText("P = %i Pa" % P) def scroll(self, event): """Update graph annotate when mouse scroll over chart""" if event.xdata and event.ydata: punto = self.createState(event.xdata, event.ydata) if event.ydata < punto.ws: self.diagrama2D.showPointData(punto) else: self.diagrama2D.clearPointData() def createState(self, x, y): """Create psychrometric state from click or mouse position""" tdb = x+273.15 punto = PsyCoolprop(P=P, tdb=tdb, w=y) return punto
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0, 4, 0, 0) self.setSpacing(0) def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x')) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y')) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z')) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget(self.busyIndicator) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(10) self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(True) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.timeLabel = QLabel("Time:") self.addWidget(self.timeLabel) self.timeSpinBox = DelayedSpinBox(750) self.addWidget(self.timeSpinBox) def _handlePositionBoxValueChanged(self, axis, value): new_position = [ self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value() ] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0] - 1) self.xSpinBox.setMaximum(shape5D[1] - 1) self.ySpinBox.setMaximum(shape5D[2] - 1) self.zSpinBox.setMaximum(shape5D[3] - 1) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class StatusBox(QGroupBox): STATUS_BOX_TEXT = 'Status' BASE_PATH_REQUIRED_MESSAGE_TEXT = 'Fill in the base path to start scanning for files to be renamed.' SCAN_FILES_PROGRESS_TEXT = 'Scanning files' SCAN_FILES_ERROR_CAPTION_TEXT = 'Error scanning files' RULES_ERROR_CAPTION_TEXT = 'Error in rules' NO_FILES_TO_RENAME_MESSAGE_TEXT = 'No files to rename.' RENAME_FILES_PROGRESS_TEXT = 'Renaming files' RENAME_FILES_ERROR_CAPTION_TEXT = 'Error renaming files' NO_FILES_RENAMED_MESSAGE_TEXT = 'No files changed.' READY_TO_RENAME_MESSAGE_TEXT = 'Ready to rename.' RENAME_PROGRESS_TEXT = 'Executing rename' PLANNING_RENAME_ERROR_CAPTION_TEXT = 'Error while planning rename' RENAME_ERROR_CAPTION_TEXT = 'Error while executing rename' RENAME_COMPLETE_MESSAGE_TEXT = 'Rename completed.' READY_MESSAGE_TEXT = 'Ready.' ERROR_COLOR = '#ff0000' _status_label = None _status_progressbar = None def __init__(self, parent): super().__init__(self.STATUS_BOX_TEXT, parent) self._status_label = QLabel(self) self._status_progressbar = QProgressBar(self) self._status_progressbar.setVisible(False) self._status_progressbar.setMinimum(0) layout = QHBoxLayout(self) layout.addWidget(self._status_label) layout.addWidget(self._status_progressbar) @pyqtSlot(BAONStatus) def show_status(self, status): if status.scan_status == BAONStatus.IN_PROGRESS: self._show_progress(status.scan_status_extra, self.SCAN_FILES_PROGRESS_TEXT) elif status.rename_status == BAONStatus.IN_PROGRESS: self._show_progress(status.rename_status_extra, self.RENAME_FILES_PROGRESS_TEXT) elif status.execute_status == BAONStatus.IN_PROGRESS: self._show_progress(status.execute_status_extra, self.RENAME_PROGRESS_TEXT) elif status.scan_status == BAONStatus.ERROR: self._show_error(status.scan_status_extra, self.SCAN_FILES_ERROR_CAPTION_TEXT) elif status.rules_status == BAONStatus.ERROR: self._show_error(status.rules_status_extra, self.RULES_ERROR_CAPTION_TEXT) elif status.rename_status == BAONStatus.ERROR: self._show_error(status.rename_status_extra, self.RENAME_FILES_ERROR_CAPTION_TEXT) elif status.execute_status == BAONStatus.ERROR: if isinstance(status.execute_status_extra, MakeRenamePlanError): self._show_error(status.execute_status_extra, self.PLANNING_RENAME_ERROR_CAPTION_TEXT) else: self._show_error(status.execute_status_extra, self.RENAME_ERROR_CAPTION_TEXT) elif status.scan_status == BAONStatus.NOT_AVAILABLE: self._show_message(self.BASE_PATH_REQUIRED_MESSAGE_TEXT) elif status.rename_status == BAONStatus.NOT_AVAILABLE: self._show_message(self.NO_FILES_TO_RENAME_MESSAGE_TEXT) elif status.execute_status == BAONStatus.NOT_AVAILABLE: self._show_message(self.NO_FILES_RENAMED_MESSAGE_TEXT) elif status.execute_status == BAONStatus.WAITING_FOR_USER: self._show_message(self.READY_TO_RENAME_MESSAGE_TEXT) elif status.execute_status == BAONStatus.AVAILABLE: self._show_message(self.RENAME_COMPLETE_MESSAGE_TEXT) else: self._show_message(self.READY_MESSAGE_TEXT) def _show_message(self, format_string, *args, **kwargs): self._show_raw_message(format_string.format(*args, **kwargs)) def _show_progress(self, progress, text): self._show_raw_message(text, progress=progress) def _show_error(self, error, caption=None): text = str(error) if caption is not None: text = caption + ': ' + text self._show_raw_message(text, is_error=True) def _show_raw_message(self, text, is_error=False, progress=None): self._status_label.setText(text) self._status_label.setStyleSheet("QLabel {{ color: {0} }}".format(self.ERROR_COLOR) if is_error else "") self._status_progressbar.setVisible(progress is not None) if progress is not None: if progress.is_indeterminate(): self._status_progressbar.setMaximum(0) else: self._status_progressbar.setMaximum(progress.total) self._status_progressbar.setValue(progress.done)
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None ): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0,4,0,0) self.setSpacing(0) self.timeControlFontSize = 12 def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def hideTimeSlider(self,flag): self.timeSlider.setHidden(flag) self.timeEndButton.setHidden(flag) self.timeStartButton.setHidden(flag) def setToolTipTimeButtonsCrop(self,croppingFlag=False): if croppingFlag==True: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current crop.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current crop.") self.timePreviousButton.setToolTip("Set the time coordinate to the previous time frame.") self.timeNextButton.setToolTip("Set the time coordinate to the next time frame.") else: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current dataset.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current dataset.") self.timePreviousButton.setToolTip("Set the time coordinate to the previous time frame.") self.timeNextButton.setToolTip("Set the time coordinate to the next time frame.") def setToolTipTimeSliderCrop(self,croppingFlag=False): if croppingFlag==True: self.timeSlider.setToolTip("Choose the time coordinate of the current crop.") else: self.timeSlider.setToolTip("Choose the time coordinate of the current dataset.") def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x') ) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y') ) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z') ) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximumWidth(200) self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget( self.busyIndicator ) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(10) self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(False) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.timeSpinBox = DelayedSpinBox(750) self.timeStartButton = QPushButton("|<") self.addWidget(self.timeStartButton) self.timeStartButton.clicked.connect(self._onTimeStartButtonClicked) self.timePreviousButton = QPushButton("<") self.addWidget(self.timePreviousButton) self.timePreviousButton.clicked.connect(self._onTimePreviousButtonClicked) self.timePreviousButton.setFixedWidth(4*self.timeControlFontSize) self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimumWidth(10) self.timeSlider.setMaximumWidth(200) self.setToolTipTimeSliderCrop() self.addWidget(self.timeSlider) self.timeSlider.valueChanged.connect(self._onTimeSliderChanged) self.timeNextButton = QPushButton(">") self.addWidget(self.timeNextButton) self.timeNextButton.clicked.connect(self._onTimeNextButtonClicked) self.timeNextButton.setFixedWidth(4*self.timeControlFontSize) self.timeEndButton = QPushButton(">|") self.timeEndButton.setFixedWidth(4*self.timeControlFontSize) self.timeStartButton.setFixedWidth(4*self.timeControlFontSize) self.setToolTipTimeButtonsCrop() self.addWidget(self.timeEndButton) self.timeEndButton.clicked.connect(self._onTimeEndButtonClicked) self.timeLabel = QLabel(" Time:") self.addWidget(self.timeLabel) timeControlFont = self.timeSpinBox.font() if self.timeControlFontSize > timeControlFont.pointSize(): timeControlFont.setPixelSize(2*self.timeControlFontSize) self.timeStartButton.setFont(timeControlFont) self.timeEndButton.setFont(timeControlFont) self.timeLabel.setFont(timeControlFont) self.timeSpinBox.setFont(timeControlFont) self.addWidget(self.timeSpinBox) self.timeSpinBox.delayedValueChanged.connect(self._onTimeSpinBoxChanged) def _onTimeStartButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[0]) def _onTimeEndButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[1]) def _onTimePreviousButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value()-1) def _onTimeNextButtonClicked(self): self.timeSpinBox.setValue(self.timeSpinBox.value()+1) def _onTimeSpinBoxChanged(self): editor = self.parent().parent().parent().editor cropModel = editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): if minValueT > self.timeSpinBox.value() or maxValueT < self.timeSpinBox.value(): for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(True) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) self.timeSlider.setValue(self.timeSpinBox.value()) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) if minValueT > self.timeSpinBox.value(): self.timeSlider.setValue(minValueT) elif maxValueT < self.timeSpinBox.value(): self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSpinBox.value() and self.timeSpinBox.value() <= maxValueT: self.timeSlider.setValue(self.timeSpinBox.value()) def _onTimeSliderChanged(self): cropModel = self.parent().parent().parent().editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): self.timeSpinBox.setValue(self.timeSlider.value()) else: if minValueT > self.timeSlider.value(): self.timeSpinBox.setValue(minValueT) self.timeSlider.setValue(minValueT) elif self.timeSlider.value() > maxValueT: self.timeSpinBox.setValue(maxValueT) self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSlider.value() and self.timeSlider.value() <= maxValueT: self.timeSpinBox.setValue(self.timeSlider.value()) def _handlePositionBoxValueChanged(self, axis, value): new_position = [self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value()] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0]-1) self.xSpinBox.setMaximum(shape5D[1]-1) self.ySpinBox.setMaximum(shape5D[2]-1) self.zSpinBox.setMaximum(shape5D[3]-1) def updateShape5Dcropped(self, shape5DcropMin, shape5Dmax): self.timeSpinBox.setMaximum(shape5Dmax[0]-1) self.xSpinBox.setMaximum(shape5Dmax[1]-1) self.ySpinBox.setMaximum(shape5Dmax[2]-1) self.zSpinBox.setMaximum(shape5Dmax[3]-1) self.timeSlider.setMaximum(shape5Dmax[0]-1) self.timeSpinBox.setValue(shape5DcropMin[0]) self.xSpinBox.setValue(shape5DcropMin[1]) self.ySpinBox.setValue(shape5DcropMin[2]) self.zSpinBox.setValue(shape5DcropMin[3]) self.timeSlider.setValue(shape5DcropMin[0]) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class SearchWidget(QFrame): """Widget, appeared, when Ctrl+F pressed. Has different forms for different search modes """ Normal = 'normal' Good = 'good' Bad = 'bad' Incorrect = 'incorrect' visibilityChanged = pyqtSignal(bool) """ visibilityChanged(visible) **Signal** emitted, when widget has been shown or hidden """ # pylint: disable=W0105 searchInDirectoryStartPressed = pyqtSignal(type(re.compile('')), list, unicode) """ searchInDirectoryStartPressed(regEx, mask, path) **Signal** emitted, when 'search in directory' button had been pressed """ # pylint: disable=W0105 searchInDirectoryStopPressed = pyqtSignal() """ searchInDirectoryStopPressed() **Signal** emitted, when 'stop search in directory' button had been pressed """ # pylint: disable=W0105 replaceCheckedStartPressed = pyqtSignal(unicode) """ replaceCheckedStartPressed(replText) **Signal** emitted, when 'replace checked' button had been pressed """ # pylint: disable=W0105 replaceCheckedStopPressed = pyqtSignal() """ replaceCheckedStartPressed() **Signal** emitted, when 'stop replacing checked' button had been pressed """ # pylint: disable=W0105 searchRegExpChanged = pyqtSignal(type(re.compile(''))) """ searchRegExpValidStateChanged(regEx) **Signal** emitted, when search regexp has been changed. If reg exp is invalid - regEx object contains empty pattern """ # pylint: disable=W0105 searchNext = pyqtSignal() """ searchNext() **Signal** emitted, when 'Search Next' had been pressed """ # pylint: disable=W0105 searchPrevious = pyqtSignal() """ searchPrevious() **Signal** emitted, when 'Search Previous' had been pressed """ # pylint: disable=W0105 replaceFileOne = pyqtSignal(unicode) """ replaceFileOne(replText) **Signal** emitted, when 'Replace' had been pressed """ # pylint: disable=W0105 replaceFileAll = pyqtSignal(unicode) """ replaceFileAll(replText) **Signal** emitted, when 'Replace All' had been pressed """ # pylint: disable=W0105 def __init__(self, plugin): QFrame.__init__(self, core.workspace()) self._mode = None self.plugin = plugin from PyQt4 import uic # lazy import for better startup performance uic.loadUi(os.path.join(os.path.dirname(__file__), 'SearchWidget.ui'), self) self.cbSearch.setCompleter(None) self.cbReplace.setCompleter(None) self.cbMask.setCompleter(None) self.fsModel = QDirModel(self.cbPath.lineEdit()) self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.cbPath.lineEdit().setCompleter( QCompleter(self.fsModel, self.cbPath.lineEdit())) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemodel - please update when possible.""" self.cbSearch.setCompleter(None) self.pbSearchStop.setVisible(False) self.pbReplaceCheckedStop.setVisible(False) self._progress = QProgressBar(self) self._progress.setAlignment(Qt.AlignCenter) self._progress.setToolTip(self.tr("Search in progress...")) self._progress.setMaximumSize(QSize(80, 16)) core.mainWindow().statusBar().insertPermanentWidget(1, self._progress) self._progress.setVisible(False) # cd up action self.tbCdUp = QToolButton(self.cbPath.lineEdit()) self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png")) self.tbCdUp.setCursor(Qt.ArrowCursor) self.tbCdUp.installEventFilter(self) # for drawing button self.cbSearch.installEventFilter( self) # for catching Tab and Shift+Tab self.cbReplace.installEventFilter( self) # for catching Tab and Shift+Tab self.cbPath.installEventFilter(self) # for catching Tab and Shift+Tab self.cbMask.installEventFilter(self) # for catching Tab and Shift+Tab self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self.hide) # connections self.cbSearch.lineEdit().textChanged.connect( self._onSearchRegExpChanged) self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbRegularExpression.stateChanged.connect( self._onSearchRegExpChanged) self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged) self.cbWholeWord.stateChanged.connect(self._onSearchRegExpChanged) self.tbCdUp.clicked.connect(self._onCdUpPressed) self.pbNext.pressed.connect(self.searchNext) self.pbPrevious.pressed.connect(self.searchPrevious) self.pbSearchStop.pressed.connect(self.searchInDirectoryStopPressed) self.pbReplaceCheckedStop.pressed.connect( self.replaceCheckedStopPressed) core.mainWindow().hideAllWindows.connect(self.hide) core.workspace().escPressed.connect(self.hide) core.workspace().currentDocumentChanged.connect( \ lambda old, new: self.setVisible(self.isVisible() and new is not None)) def show(self): """Reimplemented function. Sends signal """ super(SearchWidget, self).show() self.visibilityChanged.emit(self.isVisible()) def hide(self): """Reimplemented function. Sends signal, returns focus to workspace """ super(SearchWidget, self).hide() core.workspace().focusCurrentDocument() self.visibilityChanged.emit(self.isVisible()) def setVisible(self, visible): """Reimplemented function. Sends signal """ super(SearchWidget, self).setVisible(visible) self.visibilityChanged.emit(self.isVisible()) def _regExEscape(self, text): """Improved version of re.escape() Doesn't escape space, comma, underscore. Escapes \n and \t """ text = re.escape(text) # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable for symbol in (' ,_=\'"/:@#%&'): text = text.replace('\\' + symbol, symbol) text = text.replace('\\\n', '\\n') text = text.replace('\\\t', '\\t') return text def _makeEscapeSeqsVisible(self, text): """Replace invisible \n and \t with escape sequences """ text = text.replace('\\', '\\\\') text = text.replace('\t', '\\t') text = text.replace('\n', '\\n') return text def setMode(self, mode): """Change search mode. i.e. from "Search file" to "Replace directory" """ if self._mode == mode and self.isVisible(): if core.workspace().currentDocument() is not None and \ not core.workspace().currentDocument().hasFocus(): self.cbSearch.lineEdit().selectAll() self.cbSearch.setFocus() self._mode = mode # Set Search and Replace text document = core.workspace().currentDocument() if document is not None and \ document.hasFocus() and \ document.qutepart.selectedText: searchText = document.qutepart.selectedText self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText)) if self.cbRegularExpression.checkState() == Qt.Checked: searchText = self._regExEscape(searchText) self.cbSearch.setEditText(searchText) if not self.cbReplace.lineEdit().text() and \ self.cbSearch.lineEdit().text() and \ not self.cbRegularExpression.checkState() == Qt.Checked: replaceText = self.cbSearch.lineEdit().text().replace('\\', '\\\\') self.cbReplace.setEditText(replaceText) # Move focus to Search edit self.cbSearch.setFocus() self.cbSearch.lineEdit().selectAll() # Set search path if mode & MODE_FLAG_DIRECTORY and \ not (self.isVisible() and self.cbPath.isVisible()): try: searchPath = os.path.abspath(unicode(os.path.curdir)) self.cbPath.setEditText(searchPath) except OSError: # current directory might have been deleted pass # Set widgets visibility flag according to state widgets = (self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath, \ self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask) # wSear pbPrev pbNext pbSear wRepl wPath pbRep pbRAll pbRCHK wOpti wMask visible = \ {MODE_SEARCH : (1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,), MODE_REPLACE: (1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,), MODE_SEARCH_DIRECTORY: (1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,), MODE_REPLACE_DIRECTORY: (1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,), MODE_SEARCH_OPENED_FILES: (1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,), MODE_REPLACE_OPENED_FILES: (1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,)} for i, widget in enumerate(widgets): widget.setVisible(visible[mode][i]) # Search next button text if mode == MODE_REPLACE: self.pbNext.setText('Next') else: self.pbNext.setText(u'Next↵') # Finaly show all with valid size self.show() # show before updating widgets and labels self._updateLabels() self._updateWidgets() def eventFilter(self, object_, event): """ Event filter for mode switch tool button Draws icons in the search and path lineEdits """ if event.type( ) == QEvent.Paint and object_ is self.tbCdUp: # draw CdUp button in search path QLineEdit toolButton = object_ lineEdit = self.cbPath.lineEdit() lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0) height = lineEdit.height() availableRect = QRect(0, 0, height, height) if toolButton.rect() != availableRect: toolButton.setGeometry(availableRect) painter = QPainter(toolButton) toolButton.icon().paint(painter, availableRect) return True elif event.type( ) == QEvent.KeyPress: # Tab and Shift+Tab in QLineEdits if event.key() == Qt.Key_Tab: self._moveFocus(1) return True elif event.key() == Qt.Key_Backtab: self._moveFocus(-1) return True return QFrame.eventFilter(self, object_, event) def _onReturnPressed(self): """Return or Enter pressed on widget. Search next or Replace next """ if self.pbReplace.isVisible(): self.pbReplace.click() elif self.pbNext.isVisible(): self.pbNext.click() elif self.pbSearch.isVisible(): self.pbSearch.click() elif self.pbSearchStop.isVisible(): self.pbSearchStop.click() def _moveFocus(self, step): """Move focus forward or backward according to step. Standard Qt Keyboard focus algorithm doesn't allow circular navigation """ allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask) visibleWidgets = [widget for widget in allFocusableWidgets \ if widget.isVisible()] try: focusedIndex = visibleWidgets.index(QApplication.focusWidget()) except ValueError: print >> sys.stderr, 'Invalid focused widget in Search Widget' return nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets) visibleWidgets[nextFocusedIndex].setFocus() visibleWidgets[nextFocusedIndex].lineEdit().selectAll() def _updateLabels(self): """Update 'Search' 'Replace' 'Path' labels geometry """ width = 0 if self.lSearch.isVisible(): width = max(width, self.lSearch.minimumSizeHint().width()) if self.lReplace.isVisible(): width = max(width, self.lReplace.minimumSizeHint().width()) if self.lPath.isVisible(): width = max(width, self.lPath.minimumSizeHint().width()) self.lSearch.setMinimumWidth(width) self.lReplace.setMinimumWidth(width) self.lPath.setMinimumWidth(width) def _updateWidgets(self): """Update geometry of widgets with buttons """ width = 0 if self.wSearchRight.isVisible(): width = max(width, self.wSearchRight.minimumSizeHint().width()) if self.wReplaceRight.isVisible(): width = max(width, self.wReplaceRight.minimumSizeHint().width()) if self.wPathRight.isVisible(): width = max(width, self.wPathRight.minimumSizeHint().width()) self.wSearchRight.setMinimumWidth(width) self.wReplaceRight.setMinimumWidth(width) self.wPathRight.setMinimumWidth(width) def updateComboBoxes(self): """Update comboboxes with last used texts """ searchText = self.cbSearch.currentText() replaceText = self.cbReplace.currentText() maskText = self.cbMask.currentText() # search if searchText: index = self.cbSearch.findText(searchText) if index == -1: self.cbSearch.addItem(searchText) # replace if replaceText: index = self.cbReplace.findText(replaceText) if index == -1: self.cbReplace.addItem(replaceText) # mask if maskText: index = self.cbMask.findText(maskText) if index == -1: self.cbMask.addItem(maskText) def _searchPatternTextAndFlags(self): """Get search pattern and flags """ pattern = self.cbSearch.currentText() pattern = pattern.replace( u'\u2029', '\n') # replace unicode paragraph separator with habitual \n if not self.cbRegularExpression.checkState() == Qt.Checked: pattern = re.escape(pattern) if self.cbWholeWord.checkState() == Qt.Checked: pattern = r'\b' + pattern + r'\b' flags = 0 if not self.cbCaseSensitive.checkState() == Qt.Checked: flags = re.IGNORECASE return pattern, flags def getRegExp(self): """Read search parameters from controls and present it as a regular expression """ pattern, flags = self._searchPatternTextAndFlags() return re.compile(pattern, flags) def isSearchRegExpValid(self): """Try to compile search pattern to check if it is valid Returns bool result and text error """ pattern, flags = self._searchPatternTextAndFlags() try: re.compile(pattern, flags) except re.error, ex: return False, unicode(ex) return True, None
class Base(QMainWindow): """ Esta clase sirve de base para todos aquellos formularios que siguen el estandar de dos pestañas, una para navegación y otra para edición """ orientation = QPrinter.Portrait pageSize = QPrinter.Letter web = "" def __init__(self, parent, own_toolbar=False): """ @param parent: El widget padre de esta ventana @param own_toolbar: Si este widget dibujara su toolbar en el padre o en el mismo @type parent: QWidget @type own_toolbar: bool """ super(Base, self).__init__(parent) self.user = user.LoggedUser self._status = True self.parentWindow = parent self.own_toolbar = own_toolbar self.database = QSqlDatabase.database() """ @type: QSqlDatabase @ivar: La base de datos a la cual se conecta el sistema """ self.mapper = QDataWidgetMapper(self) u""" @type: QDataWidgetMapper @ivar: El mapper que se encarga de asignar los datos del modelo de navegación a los distintos widgets """ self.printProgressBar = QProgressBar(self) self.webview = QWebView() """ @ivar: EL objeto webview usado para cargar los reportes @type: QWebView """ self.loaded = False """ @ivar: Si se pudo o no cargar un reporte @type: bool """ self.startUi() self.editmodel = None self.printer = QPrinter() self._status = False def startUi(self): """ Iniciar todos los elementos graficos """ self.setupUi(self) if not self.own_toolbar: self.parent().addToolBar(self.toolBar) self.removeToolBar(self.toolBar) settings = QSettings() self.restoreState(settings.value(self.windowTitle() + "/State").toByteArray()) """ @ivar: El MainWindow al que pertenece este widget """ self.createActions() self.printProgressBar.setVisible(False) _tab1shortcut = QShortcut(QKeySequence("Ctrl+1"), self, functools.partial(self.tabWidget.setCurrentIndex, 0)) _tab2shortcut = QShortcut(QKeySequence("Ctrl+2"), self, functools.partial(self.tabWidget.setCurrentIndex, 1)) self.mapper.currentIndexChanged[int].connect(self.updateDetailFilter) self.actionCut.setVisible(False) self.actionPaste.setVisible(False) self.actionCopy.setVisible(False) def closeEvent(self, event): u""" Guardar el tamaño, la posición en la pantalla y la posición de la barra de tareas Preguntar si realmente se desea cerrar la pestaña cuando se esta en modo edición """ if not self.status: if ( not QMessageBox.question( self, qApp.organizationName(), u"¿Está seguro que desea salir?", QMessageBox.Yes | QMessageBox.No ) == QMessageBox.Yes ): event.ignore() # Guardar el tamaño y la posición settings = QSettings() settings.setValue(self.windowTitle() + "/Geometry", self.saveGeometry()) if not self.own_toolbar: self.parent().mdiArea().parent().parent().removeToolBar(self.toolBar) def editCell(self): """ Editar la celda actualmente seleccionada de self.tableedit """ self.tabledetails.edit(self.tabledetails.selectionModel().currentIndex()) @pyqtSlot(QDateTime) @if_edit_model def on_dtPicker_dateTimeChanged(self, datetime): """ Cambiar el tipo de cambio del modelo de edición si cambia la fecha @param datetime: La fecha contenida en self.dtPicker @type datetime: QDateTime """ query = QSqlQuery() try: if not self.database.isOpen(): if not self.database.open(): raise Exception( "No se pudo conectar a la base de " + "datos para recuperar los tipos " + "de cambio" ) q = """ SELECT idtc, tasa FROM tiposcambio WHERE fecha = %s LIMIT 1 """ % datetime.toString( "yyyyMMdd" ) if not query.exec_(q): raise UserWarning("No se pudieron recuperar los tipos de " + "cambio") if not query.size() == 1: logging.critical(u"La consulta para obtener tipos de " + "cambio no devolvio exactamente un valor") raise UserWarning(u"Hubo un error al obtener los tipos " + "de cambio") query.first() self.editmodel.exchangeRateId = query.value(0).toInt()[0] self.editmodel.exchangeRate = Decimal(query.value(1).toString()) # self.editmodel.setData( self.editmodel.index( 0, 0 ), self.editmodel.index( 0, 0 ).data() ) self.editmodel.datetime = datetime except UserWarning as inst: QMessageBox.critical(self, qApp.organizationName(), unicode(inst)) self.dtPicker.setDateTime(self.editmodel.datetime) logging.error(inst) logging.error(query.lastError().text()) except Exception as inst: QMessageBox.critical(self, qApp.organizationName(), u"Hubo un error al obtener los tipos de" + " cambio") logging.critical(query.lastError().text()) logging.critical(inst) self.dtPicker.setDateTime(self.editmodel.datetime) def navigate(self, to): """ Esta funcion se encarga de navegar entro los distintos documentos @param to: es una string que puede tomar los valores 'next' 'previous' 'first' 'last' """ if self.mapper.currentIndex != -1: row = self.mapper.currentIndex() if to == "next": row += 1 if row >= self.navproxymodel.rowCount(): row = self.navproxymodel.rowCount() - 1 self.mapper.setCurrentIndex(row) elif to == "previous": if row <= 1: row = 0 else: row = row - 1 self.mapper.setCurrentIndex(row) elif to == "first": self.mapper.toFirst() elif to == "last": self.mapper.toLast() else: self.mapper.toLast()() if self.tabledetails != None: self.tabledetails.resizeColumnsToContents() self.tabledetails.horizontalHeader().setStretchLastSection(True) self.tablenavigation.selectRow(self.mapper.currentIndex()) def updateDetailFilter(self, _index): """ Esta función se debe implementar en los formularios para que al navegar se actualize el filtro de la tabla detalles @param index: Este es el indice del mapper en el que actualmente se encuentra navegando @type index: int """ QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha " + "sido implementada") raise NotImplementedError() def loadModels(self): """ Esta función se ejecuta en el constructor del formulario mediante un QTimer, carga los formularios por primera vez """ self.updateModels() self.navigate("last") self.status = True def _setStatus(self, stat): """ @param stat: False = editando, True = navegando @type stat: bool """ self._status = stat self.setControls(self._status) def _getStatus(self): """ esta propiedad cambia entre navegar y editar """ return self._status status = property(_getStatus, _setStatus) @pyqtSlot(unicode) def on_txtSearch_textChanged(self, searchstring): """ Cambiar el filtro para el navigation model @param searchstring: Es el contenido por el cual se va a filtrar el modelo de navegación @type searchstring: string """ self.navproxymodel.setFilterFixedString(searchstring) @pyqtSlot(QModelIndex) def on_tablenavigation_doubleClicked(self, index): """ Al hacer doble click en la tabla de navegación el se cambia a la pestaña detalles mostrando el documento seleccionado @param index: El indice de la tabla en la que se dio doble click @type index: QModelIndex """ self.mapper.setCurrentIndex(index.row()) self.tabWidget.setCurrentIndex(0) @pyqtSlot(QModelIndex) def on_tablenavigation_clicked(self, index): self.mapper.setCurrentIndex(index.row()) def save(self, ask=True): """ Guardar el documento actual @param ask: Si se deberia o no preguntar al usuario si esta seguro antes de proceder @type ask: bool """ if ( ask == False or QMessageBox.question( self, qApp.organizationName(), u"¿Esta seguro que desea guardar?", QMessageBox.Yes | QMessageBox.No ) == QMessageBox.Yes ): if self.editmodel.valid: if self.editmodel.save(): QMessageBox.information(self, qApp.organizationName(), u"El documento se ha guardado con éxito") self.editmodel = None self.updateModels() self.navigate("last") self.status = True else: QMessageBox.critical(self, qApp.organizationName(), "Ha ocurrido un error al guardar el documento") else: try: QMessageBox.warning(self, qApp.organizationName(), self.editmodel.validError) except AttributeError: QMessageBox.warning( self, qApp.organizationName(), u"El documento no puede guardarse" + " ya que la información no esta" + " completa", ) def setControls(self, unused_status): """ Habilitar o deshabilitar los controles según status @param status: @type status: bool """ QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha sido implementada") raise NotImplementedError() def addLine(self): """ añadir una linea a table edit, solo se llama directamente en una ocasion, al comenzar la edicion de un documento """ row = self.editmodel.rowCount() self.editmodel.insertRows(row) def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered"): """ Crear un objeto acción @param text: El texto de la acción @type text: string @param slot: El slot que se ejecutara cuando se dispare esta acción @type slot: callable @param shortcut: El acceso directo que tiene asignada esta acción @type shortcut: QKeySequence @param icon: El icono de esta acción @type icon: string @param tip: El tooltip que tendra esta acción @type tip: string @param checkable: Si esta acción es checkable o no @type checkable: bool @param signal: La señal en la que esta acción ejecutara el slot @type signal: string @rtype: QAction """ action = QAction(text, self) if icon is not None: if type(icon) == QIcon: action.setIcon(icon) else: action.setIcon(QIcon(icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: getattr(action, signal).connect(slot) if checkable: action.setCheckable(True) return action def newDocument(self): """ Empezar la edición de un nuevo documento """ QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha sido implementada") raise NotImplementedError() def cancel(self): """ Cancelar la edición del nuevo documento """ QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha sido implementada") raise NotImplementedError() @property def printIdentifier(self): """ La identificación de este documento para reporte, normalmente sera el iddocumento o el ndocimpreso @rtype:string """ raise NotImplementedError(u"printIdentifier debe implementarse para " + "poder imprimir") def preview(self): """ Muestra el dialogo de vista previa de impresión """ try: printer = QPrinter() printer.setOrientation(self.orientation) printer.setPageSize(self.pageSize) web = self.web + self.printIdentifier report = reports.frmReportes(web, printer, self) report.exec_() except NotImplementedError as inst: QMessageBox.information( self, qApp.organizationName(), u"No se ha implementado la función de impresión para este modulo" ) logging.error(unicode(inst)) except UserWarning as inst: QMessageBox.critical(self, qApp.organizationName(), unicode(inst)) logging.error(unicode(inst)) except Exception as inst: QMessageBox.critical(self, qApp.organizationName(), "Hubo un error al intentar mostrar su reporte") logging.critical(unicode(inst)) def printDocument(self): """ Imprime el documento actual """ try: base = reports.Reports.url if base == "": raise UserWarning(u"No existe una configuración para el " + "servidor de reportes") self.printer.setOrientation(self.orientation) self.printer.setPageSize(self.pageSize) web = base + self.web + self.printIdentifier + "&uname=" + self.user.user + "&hash=" + self.user.hash self.loaded = False self.webview.load(QUrl(web)) self.webview.loadFinished[bool].connect(self.on_webview_loadFinished) self.webview.loadProgress[int].connect(self.on_webview_loadProgress) except NotImplementedError as inst: logging.error(unicode(inst)) QMessageBox.information( self, qApp.organizationName(), u"La función de impresión no se ha " + "implementado para este modulo" ) except UserWarning as inst: logging.error(unicode(inst)) QMessageBox.critical(self, qApp.organizationName(), unicode(inst)) except Exception as inst: logging.critical(unicode(inst)) QMessageBox.critical(self, qApp.organizationName(), "Hubo un problema al intentar imprimir" + " su reporte") def on_webview_loadProgress(self, progress): """ Muestra el progreso de la carga del reporte en un progressBar """ self.printProgressBar.setValue(progress) def on_webview_loadFinished(self, status): if self.printProgressBar.isVisible(): self.printProgressBar.hide() if not status: QMessageBox.critical(self, qApp.organizationName(), "El reporte no se pudo cargar") logging.error("No se pudo cargar el reporte") self.loaded = True printdialog = QPrintDialog(self.printer, self) if printdialog.exec_() == QDialog.Accepted: self.webview.print_(self.printer) del self.webview def deleteRow(self): """ Funcion usada para borrar lineas de la tabla """ index = self.tabledetails.currentIndex() if not index.isValid(): return row = index.row() self.editmodel.removeRows(row, 1) self.updateLabels() def updateLabels(self): """ Este metodo se llama para actualizar las etiquetas de totales en el formulario """ raise NotImplementedError() def createActions(self): """ Crea las acciones predefinidas del sistema """ self.actionNew = self.createAction( text="Nuevo", tip="Crear un nuevo documento", icon=QIcon.fromTheme("document-new", QIcon(":/icons/res/document-new.png")), shortcut="Ctrl+n", slot=self.newDocument, ) self.actionPreview = self.createAction( text="Previsualizar", tip=u"Vista de impresión del documento", icon=QIcon.fromTheme("document-preview", QIcon(":/icons/res/document-preview.png")), shortcut="Ctrl+p", slot=self.preview, ) self.actionPrint = self.createAction( text="Imprimir", tip="Imprimir el documento", icon=QIcon.fromTheme("document-print", QIcon(":/icons/res/document-print.png")), slot=self.printDocument, ) self.actionSave = self.createAction( text="Guardar", tip="Guardar el documento", icon=QIcon.fromTheme("document-save", QIcon(":/icons/res/document-save.png")), shortcut="Ctrl+g", slot=self.save, ) self.actionCancel = self.createAction( text="Cancelar", tip=u"Cancelar la creación del nuevo documento", icon=QIcon.fromTheme("dialog-cancel", QIcon(":/icons/res/dialog-cancel.png")), shortcut="Esc", slot=self.cancel, ) # edicion, TODO: QUE FUNCIONEN ESTAS ACCIONES self.actionCopy = self.createAction( text="Copiar", icon=QIcon.fromTheme("edit-copy", QIcon(":/icons/res/edit-copy.png")), shortcut="Ctrl+c" ) self.actionCut = self.createAction( text="Cortar", icon=QIcon.fromTheme("edit-cut", QIcon(":/icons/res/edit-cut.png")), shortcut="Ctrl+x" ) self.actionPaste = self.createAction(text="Pegar", icon=":/icons/res/edit-paste.png", shortcut="Ctrl+v") # navegación self.actionGoFirst = self.createAction( text="Primer documento", tip="Ir al primer documento", icon=QIcon.fromTheme("go-first", QIcon(":/icons/res/go-first.png")), slot=functools.partial(self.navigate, "first"), ) self.actionGoPrevious = self.createAction( text="Documento anterior", tip="Ir al documento anterior", icon=QIcon.fromTheme("go-previous", QIcon(":/icons/res/go-previous.png")), slot=functools.partial(self.navigate, "previous"), ) self.actionGoLast = self.createAction( text="Ultimo documento", tip="Ir al ultimo documento", icon=QIcon.fromTheme("go-last", QIcon(":/icons/res/go-last.png")), slot=functools.partial(self.navigate, "last"), ) self.actionGoNext = self.createAction( text="Documento siguiente", tip="Ir al siguiente documento", icon=QIcon.fromTheme("go-next", QIcon(":/icons/res/go-next.png")), slot=functools.partial(self.navigate, "next"), ) self.actionDeleteRow = self.createAction( text="Borrar la fila", icon=QIcon.fromTheme("edit-delete", QIcon(":/icons/res/edit-delete.png")), slot=self.deleteRow, ) self.addActionsToToolBar() def addActionsToToolBar(self): """ Añade las acciones predefinidas a la barra de tareas """ self.toolBar.addActions( [self.actionNew, self.actionPreview, self.actionPrint, self.actionSave, self.actionCancel] ) self.toolBar.addSeparator() self.toolBar.addActions( [self.actionGoFirst, self.actionGoPrevious, self.actionGoLast, self.actionGoNext, self.actionGoLast] ) @pyqtSlot() @if_edit_model def on_txtObservations_textChanged(self): """ Asignar las observaciones al editmodel """ self.editmodel.observations = self.txtObservations.toPlainText().strip()
class LDSControls(QFrame): STATIC_IMG = ('error_static.png','linz_static.png','busy_static.png','clean_static.png') ANIM_IMG = ('error.gif','linz.gif','layer.gif','clean.gif') IMG_SPEED = 100 IMG_WIDTH = 64 IMG_HEIGHT = 64 MAX_WD = 450 GD_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../bin/gdal/gdal-data')) STATUS = LU.enum('ERROR','IDLE','BUSY','CLEAN') def __init__(self,parent): super(LDSControls, self).__init__() self.parent = parent self.initConf() self.initEPSG() self.initUI() def initConf(self): '''Read files in conf dir ending in conf''' self.cflist = ConfigInitialiser.getConfFiles() #self.imgset = self.STATIC_IMG if ConfigWrapper().readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG #self.imgset = self.STATIC_IMG if self.parent.confconn.tp.src.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG sep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.SRCNAME,self.parent.confconn.uconf) self.imgset = self.STATIC_IMG if sep.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG self.parent.confconn.reg.closeEndPoint(self.parent.confconn.SRCNAME) def initEPSG(self): '''Read GDAL EPSG files, splitting by NZ(RSR) and RestOfTheWorld''' gcsf = gdal.FindFile('gdal','gcs.csv') if not gcsf: gcsf = os.path.join(self.GD_PATH,'gcs.csv') pcsf = gdal.FindFile('gdal','pcs.csv') if not pcsf: pcsf = os.path.join(self.GD_PATH,'pcs.csv') gcs = ConfigInitialiser.readCSV(gcsf) pcs = ConfigInitialiser.readCSV(pcsf) self.nzlsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD' in e[1] or 'RSRGD' in e[1]] \ + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD' in e[1] or 'RSRGD' in e[1]] self.rowsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]] \ + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]] def initUI(self): # 0 1 2 3 4 5 6 7 8 #'destname','lgselect','layer','uconf','group','epsg','fd','td','int' #self.rdest,rlgselect,self.rlayer,ruconf,self.rgroup,repsg,rfd,rtd,rint = readlist QToolTip.setFont(QFont('SansSerif', 10)) #labels destLabel = QLabel('Destination') lgLabel = QLabel('Group/Layer') epsgLabel = QLabel('EPSG') fromDateLabel = QLabel('From Date') toDateLabel = QLabel('To Date') confLabel = QLabel('User Config') self.view = QLabel() self.view.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.view.setAlignment(Qt.AlignCenter) self.confcombo = QComboBox(self) self.confcombo.setToolTip('Enter your user config name (file) here') self.confcombo.addItems(self.cflist) self.confcombo.setEditable(False) #self.confcombo.currentIndexChanged.connect(self.doLGEditUpdate) #combos self.lgcombo = QComboBox(self) self.lgcombo.setMaximumWidth(self.MAX_WD) self.lgcombo.setDuplicatesEnabled(False) #self.lgcombo.setInsertPolicy(QComboBox.InsertAlphabetically)#?doesnt seem to work self.lgcombo.setToolTip('Select either Layer or Group entry') self.lgcombo.setEditable(False) self.sepindex = None #self.updateLGValues() self.epsgcombo = QComboBox(self) self.epsgcombo.setMaximumWidth(self.MAX_WD) self.epsgcombo.setToolTip('Setting an EPSG number here determines the output SR of the layer') self.epsgcombo.addItems([i[1] for i in self.nzlsr]) self.epsgcombo.insertSeparator(len(self.nzlsr)) self.epsgcombo.addItems([i[1] for i in self.rowsr]) self.epsgcombo.setEditable(True) self.epsgcombo.setEnabled(False) self.destlist = self.getConfiguredDestinations() self.destcombo = QComboBox(self) self.destcombo.setToolTip('Choose the desired output type') self.destcombo.setEditable(False) self.destcombo.addItems(self.destlist) #date selection self.fromdateedit = QDateEdit() self.fromdateedit.setCalendarPopup(True) self.fromdateedit.setEnabled(False) self.todateedit = QDateEdit() self.todateedit.setCalendarPopup(True) self.todateedit.setEnabled(False) #check boxes self.epsgenable = QCheckBox() self.epsgenable.setCheckState(False) self.epsgenable.clicked.connect(self.doEPSGEnable) self.fromdateenable = QCheckBox() self.fromdateenable.setCheckState(False) self.fromdateenable.clicked.connect(self.doFromDateEnable) self.todateenable = QCheckBox() self.todateenable.setCheckState(False) self.todateenable.clicked.connect(self.doToDateEnable) self.progressbar = QProgressBar() self.progressbar.setRange(0,100) self.progressbar.setVisible(True) self.progressbar.setMinimumWidth(self.MAX_WD) #buttons self.initbutton = QPushButton("waiting") self.initbutton.setToolTip('Initialise the Layer Configuration') self.initbutton.clicked.connect(self.doInitClickAction) self.cleanbutton = QPushButton("Clean") self.cleanbutton.setToolTip('Clean the selected layer/group from local storage') self.cleanbutton.clicked.connect(self.doCleanClickAction) self.replicatebutton = QPushButton("Replicate") self.replicatebutton.setToolTip('Execute selected replication') self.replicatebutton.clicked.connect(self.doReplicateClickAction) self.cancelbutton = QPushButton("Close") self.cancelbutton.setToolTip('Close the LDS Replicate application') self.cancelbutton.clicked.connect(self.parent.close) #set dialog values using GPR self.updateGUIValues(self.parent.gvs) #set onchange here otherwise we get circular initialisation self.destcombo.currentIndexChanged.connect(self.doDestChanged) self.confcombo.currentIndexChanged.connect(self.doConfChanged) self.lgcombo.currentIndexChanged.connect(self.doLGComboChanged) self.setStatus(self.STATUS.IDLE) #grid grid = QGridLayout() grid.setSpacing(10) #placement section ------------------------------------ #---------+---------+--------+---------+-------- # dest LB | | dest DD # grp LB | | grp DD # conf LB | | conf DD # epsg L | epsg CB | epsg DD # f dt L | f dt CB | f dt DD # t td L | t td CB | t td DD # icon | <- progress -> # layer B | <- . -> |repl B | clean B | close B #---------+---------+--------+---------+-------- grid.addWidget(destLabel, 1, 0) grid.addWidget(self.destcombo, 1, 2) #grid.addWidget(layerLabel, 2, 0) grid.addWidget(lgLabel, 2, 0) grid.addWidget(self.lgcombo, 2, 2) grid.addWidget(confLabel, 3, 0) grid.addWidget(self.confcombo, 3, 2) #grid.addWidget(groupLabel, 4, 0) #grid.addWidget(self.groupEdit, 4, 2) grid.addWidget(epsgLabel, 5, 0) grid.addWidget(self.epsgenable, 5, 1) grid.addWidget(self.epsgcombo, 5, 2) grid.addWidget(fromDateLabel, 6, 0) grid.addWidget(self.fromdateenable, 6, 1) grid.addWidget(self.fromdateedit, 6, 2) grid.addWidget(toDateLabel, 7, 0) grid.addWidget(self.todateenable, 7, 1) grid.addWidget(self.todateedit, 7, 2) hbox3 = QHBoxLayout() hbox3.addWidget(self.view) hbox3.addStretch(1) hbox3.addWidget(self.progressbar) #hbox3.addLayout(vbox2) #hbox3.addLayout(vbox3) hbox4 = QHBoxLayout() hbox4.addWidget(self.initbutton) hbox4.addStretch(1) hbox4.addWidget(self.replicatebutton) hbox4.addWidget(self.cleanbutton) hbox4.addWidget(self.cancelbutton) vbox = QVBoxLayout() #vbox.addStretch(1) vbox.addLayout(grid) vbox.addLayout(hbox3) vbox.addLayout(hbox4) self.setLayout(vbox) #def setProgress(self,pct): # self.progressbar.setValue(pct) def setStatus(self,status,message='',tooltip=None): '''Sets indicator icon and statusbar message''' self.parent.statusbar.showMessage(message) self.parent.statusbar.setToolTip(tooltip if tooltip else '') #progress loc = os.path.abspath(os.path.join(IMG_LOC,self.imgset[status])) #loc = os.path.abspath(os.path.join(os.path.dirname(__file__),self.parent.IMG_LOC,self.imgset[status])) self.progressbar.setVisible(status in (self.STATUS.BUSY, self.STATUS.CLEAN)) #icon anim = QMovie(loc, QByteArray(), self) anim.setScaledSize(QSize(self.IMG_WIDTH,self.IMG_HEIGHT)) anim.setCacheMode(QMovie.CacheAll) anim.setSpeed(self.IMG_SPEED) self.view.clear() self.view.setMovie(anim) anim.start() self.view.repaint() QApplication.processEvents(QEventLoop.AllEvents) def mainWindowEnable(self,enable=True): cons = (self.lgcombo, self.confcombo, self.destcombo, self.initbutton, self.replicatebutton, self.cleanbutton, self.cancelbutton, self.epsgenable,self.fromdateenable,self.todateenable, self.parent.menubar) for c in cons: c.setEnabled(enable) if enable: self.epsgcombo.setEnabled(self.epsgenable.checkState()) self.fromdateedit.setEnabled(self.fromdateenable.checkState()) self.todateedit.setEnabled(self.todateenable.checkState()) else: self.epsgcombo.setEnabled(False) self.fromdateedit.setEnabled(False) self.todateedit.setEnabled(False) QApplication.restoreOverrideCursor() if enable else QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) def refreshLGCombo(self): '''Re index LG combobox since a refreshLG call (new dest?) will usually mean new groups''' self.lgcombo.clear() self.lgcombo.addItems([i[2] for i in self.parent.confconn.lglist]) #NOTE the separator consumes an index, if not clearing the combobox selectively remove the old sepindex (assumes g preceeds l) #if self.sepindex: # self.lgcombo.removeItem(self.sepindex) self.sepindex = [i[0] for i in self.parent.confconn.lglist].count(LORG.GROUP) self.lgcombo.insertSeparator(self.sepindex) def updateLGValues(self,uconf,lgval,dest): '''Sets the values displayed in the Layer/Group combo''' #because we cant seem to sort combobox entries and want groups at the top, clear and re-add #TRACE# #pdb.set_trace() sf = None try: self.parent.confconn.initConnections(uconf,lgval,dest) except Exception as e: sf=1 ldslog.error('Error Updating UC Values. '+str(e)) if sf: self.setStatus(self.STATUS.ERROR,'Error Updating UC Values', str(e)) else: self.setStatus(self.STATUS.IDLE) self.refreshLGCombo() def centre(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def gprParameters(self,rdest): '''Zip default and GPR values''' return [x if LU.assessNone(x) else y for x,y in zip(self.parent.gpr.readsec(rdest),self.parent.DEF_RVALS[1:])] def getLCE(self,ln): '''Read layer parameters''' dep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf) #sep = self.parent.confconn.reg.openEndPoint('WFS',self.parent.confconn.uconf) self.parent.confconn.reg.setupLayerConfig(self.parent.confconn.tp,None,dep,initlc=False) lce = dep.getLayerConf().readLayerParameters(ln) #self.parent.confconn.reg.closeEndPoint('WFS') self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname) sep,dep = None,None return lce def doDestChanged(self): '''Read the destname parameter and fill dialog with matching GPR values''' rdest = str(self.destlist[self.destcombo.currentIndex()]) rvals = self.gprParameters(rdest) self.updateGUIValues([rdest]+rvals) def doConfChanged(self): '''Read the user conf parameter and fill dialog with matching GPR values''' rdest = str(self.destlist[self.destcombo.currentIndex()]) rlg,_,rep,rfd,rtd = self.gprParameters(rdest) ruc = str(self.cflist[self.confcombo.currentIndex()]) self.updateGUIValues((rdest,rlg,ruc,rep,rfd,rtd)) def doLGComboChanged(self): '''Read the layer/group value and change epsg to layer or gpr match''' #get a matching LG entry and test whether its a layer or group #lgi = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data()) lgi = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText())) #lgi can be none if we init a new group, in which case we use the GPR value if lgi: lge = self.parent.confconn.lglist[lgi] lce = self.getLCE(lge[1]) if lge[0]==LORG.LAYER else None else: lce = None #look for filled layer conf epsg OR use prefs stored in gpr if lce and LU.assessNone(lce.epsg): epsgval = lce.epsg else: rdest = str(self.destlist[self.destcombo.currentIndex()]) _,_,epsgval,_,_ = self.gprParameters(rdest) epsgindex = [i[0] for i in self.nzlsr+[(0,0)]+self.rowsr].index(epsgval) if self.epsgcombo.currentIndex() != epsgindex: self.epsgcombo.setCurrentIndex(int(epsgindex)) def updateGUIValues(self,readlist): '''Fill dialog values from provided list''' #TODO. Remove circular references when setCurrentIndex() triggers do###Changed() #Read user input rdest,self.rlgval,ruconf,repsg,rfd,rtd = readlist #-------------------------------------------------------------------- #Destination Menu selecteddest = LU.standardiseDriverNames(rdest) if selecteddest not in self.destlist: self.destlist = self.getConfiguredDestinations() self.destcombo.addItem(selecteddest) destindex = self.destlist.index(selecteddest) if selecteddest else 0 if self.destcombo.currentIndex() != destindex: self.destcombo.setCurrentIndex(destindex) #InitButton self.initbutton.setText('Layer Select') #Config File confindex = 0 if LU.assessNone(ruconf): ruconf = ruconf.split('.')[0] if ruconf not in self.cflist: self.cflist += [ruconf,] self.confcombo.addItem(ruconf) confindex = self.cflist.index(ruconf) if self.confcombo.currentIndex() != confindex: self.confcombo.setCurrentIndex(confindex) #self.confEdit.setText(ruconf if LU.assessNone(ruconf) else '') #Layer/Group Selection self.updateLGValues(ruconf,self.rlgval,rdest) lgindex = None if LU.assessNone(self.rlgval): #index of list value lgindex = self.parent.confconn.getLayerGroupIndex(self.rlgval,col=1) if LU.assessNone(lgindex): #advance by 1 for sep lgindex += 1 if lgindex>self.sepindex else 0 else: #using the separator index sets the combo to blank lgindex = self.sepindex if self.lgcombo.currentIndex() != lgindex: self.lgcombo.setCurrentIndex(lgindex) #self.doLGEditUpdate() #EPSG # user > layerconf #useepsg = LU.precedence(repsg, lce.epsg if lce else None, None) epsgindex = [i[0] for i in self.nzlsr+[(None,None)]+self.rowsr].index(repsg) if self.epsgcombo.currentIndex() != epsgindex: self.epsgcombo.setCurrentIndex(epsgindex) #epsgedit = self.epsgcombo.lineEdit() #epsgedit.setText([e[1] for e in self.nzlsr+self.rowsr if e[0]==repsg][0]) #epsgedit.setText([e for e in self.nzlsr+self.rowsr if re.match('^\s*(\d+).*',e).group(1)==repsg][0]) #To/From Dates if LU.assessNone(rfd): self.fromdateedit.setDate(QDate(int(rfd[0:4]),int(rfd[5:7]),int(rfd[8:10]))) else: early = DataStore.EARLIEST_INIT_DATE self.fromdateedit.setDate(QDate(int(early[0:4]),int(early[5:7]),int(early[8:10]))) if LU.assessNone(rtd): self.todateedit.setDate(QDate(int(rtd[0:4]),int(rtd[5:7]),int(rtd[8:10]))) else: today = DataStore.getCurrent() self.todateedit.setDate(QDate(int(today[0:4]),int(today[5:7]),int(today[8:10]))) #Internal/External CheckBox # if LU.assessNone(rint): # self.internalTrigger.setChecked(rint.lower()==DataStore.CONF_INT) # else: # self.internalTrigger.setChecked(DataStore.DEFAULT_CONF==DataStore.CONF_INT) def getConfiguredDestinations(self): defml = ['',]+DataStore.DRIVER_NAMES.values() return [d for d in self.parent.gpr.getDestinations() if d in defml] def doEPSGEnable(self): self.epsgcombo.setEnabled(self.epsgenable.isChecked()) def doFromDateEnable(self): self.fromdateedit.setEnabled(self.fromdateenable.isChecked()) def doToDateEnable(self): self.todateedit.setEnabled(self.todateenable.isChecked()) def readParameters(self): '''Read values out of dialogs''' destination = LU.assessNone(str(self.destlist[self.destcombo.currentIndex()])) #lgindex = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data()) lgindex = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText())) #NB need to test for None explicitly since zero is a valid index lgval = self.parent.confconn.lglist[lgindex][1] if LU.assessNone(lgindex) else None #uconf = LU.standardiseUserConfigName(str(self.confcombo.lineEdit().text())) #uconf = str(self.confcombo.lineEdit().text()) uconf = str(self.cflist[self.confcombo.currentIndex()]) ee = self.epsgenable.isChecked() epsg = None if ee is False else re.match('^\s*(\d+).*',str(self.epsgcombo.lineEdit().text())).group(1) fe = self.fromdateenable.isChecked() te = self.todateenable.isChecked() fd = None if fe is False else str(self.fromdateedit.date().toString('yyyy-MM-dd')) td = None if te is False else str(self.todateedit.date().toString('yyyy-MM-dd')) return destination,lgval,uconf,epsg,fe,te,fd,td def doInitClickAction(self): '''Initialise the LC on LC-button-click, action''' try: try: self.setStatus(self.STATUS.BUSY,'Opening Layer-Config Editor') self.progressbar.setValue(0) self.parent.runLayerConfigAction() finally: self.setStatus(self.STATUS.IDLE,'Ready') except Exception as e: self.setStatus(self.STATUS.ERROR,'Error in Layer-Config',str(sys.exc_info()))#e)) def doCleanClickAction(self): '''Set clean anim and run clean''' #lgo = self.lgcombo.currentText().toUtf8().data() lgo = LQ.readWidgetText(self.lgcombo.currentText()) try: self.setStatus(self.STATUS.CLEAN,'Running Clean '+lgo) self.progressbar.setValue(0) self.runReplicationScript(True) except Exception as e: self.setStatus(self.STATUS.ERROR,'Failed Clean of '+lgo,str(sys.exc_info()))#e)) def doReplicateClickAction(self): '''Set busy anim and run repl''' lgo = self.lgcombo.currentText()#.toUtf8().data()#only used for error messages try: self.setStatus(self.STATUS.BUSY,'Running Replicate '+lgo) self.progressbar.setValue(0) self.runReplicationScript(False) except Exception as e: self.setStatus(self.STATUS.ERROR,'Failed Replication of '+lgo,str(sys.exc_info()))#e)) def runReplicationScript(self,clean=False): '''Run the layer/group repliction script''' destination,lgval,uconf,epsg,fe,te,fd,td = self.readParameters() uconf_path = LU.standardiseUserConfigName(uconf) destination_path = LU.standardiseLayerConfigName(destination) destination_driver = LU.standardiseDriverNames(destination) if not os.path.exists(uconf_path): self.userConfMessage(uconf_path) return elif not MainFileReader(uconf_path).hasSection(destination_driver): self.userConfMessage(uconf_path,destination_driver) return #----------------------------------------------------- #'destname','layer','uconf','group','epsg','fd','td','int' self.parent.gpr.write((destination_driver,lgval,uconf,epsg,fd,td)) ldslog.info(u'dest={0}, lg={1}, conf={2}, epsg={3}'.format(destination_driver,lgval,uconf,epsg)) ldslog.info('fd={0}, td={1}, fe={2}, te={3}'.format(fd,td,fe,te)) lgindex = self.parent.confconn.getLayerGroupIndex(lgval,col=1) #lorg = self.parent.confconn.lglist[lgindex][0] #----------don't need lorg in TP anymore but it is useful for sorting/counting groups #self.parent.confconn.tp.setLayerOrGroup(lorg) self.parent.confconn.tp.setLayerGroupValue(lgval) if self.fromdateenable.isChecked(): self.parent.confconn.tp.setFromDate(fd) if self.todateenable.isChecked(): self.parent.confconn.tp.setToDate(td) self.parent.confconn.tp.setUserConf(uconf) if self.epsgenable: self.parent.confconn.tp.setEPSG(epsg) #because clean state persists in TP if clean: self.parent.confconn.tp.setCleanConfig() else: self.parent.confconn.tp.clearCleanConfig() #(re)initialise the data source since uconf may have changed #>>#self.parent.confconn.tp.src = self.parent.confconn.initSourceWrapper() #-------------------------- ###ep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf) ###self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname) ###ep = None #Open ProcessRunner and run with TP(proc)/self(gui) instances #HACK temp add of dest_drv to PR call try: #TODO. Test for valid LC first self.tpr = ProcessRunner(self,destination_driver) except Exception as e: ldslog.error('Cannot create ProcessRunner {}. NB Possible missing Layer Config {}'.format(str(e),destination_path)) self.layerConfMessage(destination_path) return #If PR has been successfully created we must vave a valid dst self.tpr.start() def quitProcessRunner(self): self.tpr.join() self.tpr.quit() self.trp = None def userConfMessage(self,uconf,secname=None): ucans = QMessageBox.warning(self, 'User Config Missing/Incomplete', 'Specified User-Config file, '+str(uconf)+' does not exist' if secname is None else 'User-Config file does not contain '+str(secname)+' section', 'Back','Initialise User Config') if not ucans: #Retry ldslog.warn('Retry specifying UC') #self.confcombo.setCurrentIndex(0) return #Init ldslog.warn('Reset User Config Wizard') self.parent.runWizardAction() def layerConfMessage(self,dest): lcans = QMessageBox.warning(self, 'Layer Config Missing', 'Required Layer-Config file, '+str(dest)+' does not exist', 'Back','Run Layer Select') if not lcans: #Retry ldslog.warn('Retry specifying LC') #self.destcombo.setCurrentIndex(0) return #Init ldslog.warn('Reset Layer Config') self.doInitClickAction()
class QuadStatusBar(QHBoxLayout): positionChanged = pyqtSignal(int, int, int) # x,y,z def __init__(self, parent=None ): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0,4,0,0) self.setSpacing(0) self.timeControlFontSize = 12 def showXYCoordinates(self): self.zLabel.setHidden(True) self.zSpinBox.setHidden(True) def showXYZCoordinates(self): self.zLabel.setHidden(False) self.zSpinBox.setHidden(False) def hideTimeSlider(self,flag): self.timeSlider.setHidden(flag) self.timeEndButton.setHidden(flag) self.timeStartButton.setHidden(flag) def setToolTipTimeButtonsCrop(self,croppingFlag=False): if croppingFlag==True: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current crop.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current crop.") else: self.timeStartButton.setToolTip("Set the time coordinate to the beginning of the current dataset.") self.timeEndButton.setToolTip("Set the time coordinate to the end of the current dataset.") def setToolTipTimeSliderCrop(self,croppingFlag=False): if croppingFlag==True: self.timeSlider.setToolTip("Choose the time coordinate of the current crop.") else: self.timeSlider.setToolTip("Choose the time coordinate of the current dataset.") def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor): self.xLabel, self.xSpinBox = _get_pos_widget('X', xbackgroundColor, xforegroundColor) self.yLabel, self.ySpinBox = _get_pos_widget('Y', ybackgroundColor, yforegroundColor) self.zLabel, self.zSpinBox = _get_pos_widget('Z', zbackgroundColor, zforegroundColor) self.xSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'x') ) self.ySpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'y') ) self.zSpinBox.delayedValueChanged.connect( partial(self._handlePositionBoxValueChanged, 'z') ) self.addWidget(self.xLabel) self.addWidget(self.xSpinBox) self.addWidget(self.yLabel) self.addWidget(self.ySpinBox) self.addWidget(self.zLabel) self.addWidget(self.zSpinBox) self.addSpacing(10) self.busyIndicator = QProgressBar() self.busyIndicator.setMaximumWidth(200) self.busyIndicator.setMaximum(0) self.busyIndicator.setMinimum(0) self.busyIndicator.setVisible(False) self.busyIndicator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.addWidget( self.busyIndicator ) self.setStretchFactor(self.busyIndicator, 1) self.addStretch() self.addSpacing(10) self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(True) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.timeSpinBox = DelayedSpinBox(750) self.timeStartButton = QPushButton("Start") self.addWidget(self.timeStartButton) self.timeStartButton.clicked.connect(self._onTimeStartButtonClicked) self.timeSlider = QSlider(Qt.Horizontal) self.timeSlider.setMinimumWidth(10) self.timeSlider.setMaximumWidth(200) self.setToolTipTimeSliderCrop() self.addWidget(self.timeSlider) self.timeSlider.valueChanged.connect(self._onTimeSliderChanged) self.timeEndButton = QPushButton("End") self.timeEndButton.setFixedWidth(4*self.timeControlFontSize) self.timeStartButton.setFixedWidth(4*self.timeControlFontSize) self.setToolTipTimeButtonsCrop() self.addWidget(self.timeEndButton) self.timeEndButton.clicked.connect(self._onTimeEndButtonClicked) self.timeLabel = QLabel(" Time:") self.addWidget(self.timeLabel) timeControlFont = self.timeSpinBox.font() if self.timeControlFontSize > timeControlFont.pointSize(): timeControlFont.setPixelSize(self.timeControlFontSize) self.timeStartButton.setFont(timeControlFont) self.timeEndButton.setFont(timeControlFont) self.timeLabel.setFont(timeControlFont) self.timeSpinBox.setFont(timeControlFont) self.addWidget(self.timeSpinBox) self.timeSpinBox.delayedValueChanged.connect(self._onTimeSpinBoxChanged) def _onTimeStartButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[0]) def _onTimeEndButtonClicked(self): self.timeSpinBox.setValue(self.parent().parent().parent().editor.cropModel.get_roi_t()[1]) def _onTimeSpinBoxChanged(self): editor = self.parent().parent().parent().editor cropModel = editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): if minValueT > self.timeSpinBox.value() or maxValueT < self.timeSpinBox.value(): for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(True) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) self.timeSlider.setValue(self.timeSpinBox.value()) else: for imgView in editor.imageViews: imgView._croppingMarkers._shading_item.set_paint_full_frame(False) if minValueT > self.timeSpinBox.value(): self.timeSlider.setValue(minValueT) elif maxValueT < self.timeSpinBox.value(): self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSpinBox.value() and self.timeSpinBox.value() <= maxValueT: self.timeSlider.setValue(self.timeSpinBox.value()) def _onTimeSliderChanged(self): cropModel = self.parent().parent().parent().editor.cropModel minValueT = cropModel.get_roi_t()[0] maxValueT = cropModel.get_roi_t()[1] if cropModel.get_scroll_time_outside_crop(): self.timeSpinBox.setValue(self.timeSlider.value()) else: if minValueT > self.timeSlider.value(): self.timeSpinBox.setValue(minValueT) self.timeSlider.setValue(minValueT) elif self.timeSlider.value() > maxValueT: self.timeSpinBox.setValue(maxValueT) self.timeSlider.setValue(maxValueT) elif minValueT <= self.timeSlider.value() and self.timeSlider.value() <= maxValueT: self.timeSpinBox.setValue(self.timeSlider.value()) def _handlePositionBoxValueChanged(self, axis, value): new_position = [self.xSpinBox.value(), self.ySpinBox.value(), self.zSpinBox.value()] changed_axis = ord(axis) - ord('x') new_position[changed_axis] = value self.positionChanged.emit(*new_position) def updateShape5D(self, shape5D): self.timeSpinBox.setMaximum(shape5D[0]-1) self.xSpinBox.setMaximum(shape5D[1]-1) self.ySpinBox.setMaximum(shape5D[2]-1) self.zSpinBox.setMaximum(shape5D[3]-1) def updateShape5Dcropped(self, shape5DcropMin, shape5Dmax): self.timeSpinBox.setMaximum(shape5Dmax[0]-1) self.xSpinBox.setMaximum(shape5Dmax[1]-1) self.ySpinBox.setMaximum(shape5Dmax[2]-1) self.zSpinBox.setMaximum(shape5Dmax[3]-1) self.timeSlider.setMaximum(shape5Dmax[0]-1) self.timeSpinBox.setValue(shape5DcropMin[0]) self.xSpinBox.setValue(shape5DcropMin[1]) self.ySpinBox.setValue(shape5DcropMin[2]) self.zSpinBox.setValue(shape5DcropMin[3]) self.timeSlider.setValue(shape5DcropMin[0]) def setMouseCoords(self, x, y, z): self.xSpinBox.setValueWithoutSignal(x) self.ySpinBox.setValueWithoutSignal(y) self.zSpinBox.setValueWithoutSignal(z)
class ExcelWorkbookView(QWidget): """A read-only table view for browsing MS Excel data.""" def __init__(self, *args, **kwargs): super(QWidget, self).__init__(*args, **kwargs) self._init_ui() # Optional args self._dt_format = kwargs.pop('date_format', '%Y-%m-%d') # Map for sheet widgets self._ws_info = {} # Reset the view self.reset_view() # Check availability of XLRD library if not XLRD_AVAIL: QMessageBox.critical( self, self.tr('Missing Dependency'), self. tr('\'xlrd\' library is missing.\nExcel data cannot be loaded ' 'without this library. Please install it and try again.')) self.setEnabled(False) return @property def date_format(self): """ :return: Returns the format used to render the date/time in the view. The default formatting will return the date in ISO 8601 format i.e. 'YYYY-MM-DD' where the format is '%Y-%m-%d'. :rtype: str """ return self._dt_format @date_format.setter def date_format(self, format): """ Sets the format used to render the date/time in the view. The format needs to be in a format that is understood by Python's 'strftime()' function. :param format: Format for rendering date/time :type format: str """ self._dt_format = format def worksheet_info(self, idx): """ :param idx: :return: Returns a WorksheetInfo object containing references to the ExcelWorksheetView, xlrd.sheet.Sheet and name of the sheet, will return None if the index does not exist. :rtype: WorksheetInfo """ return self._ws_info.get(idx, None) def current_worksheet_info(self): """ :return: Returns the WorksheetInfo object for the current displayed tab. None if the view is empty. :rtype:WorksheetInfo """ curr_idx = self._tbw.currentIndex() return self.worksheet_info(curr_idx) @property def progress_bar(self): """ :return: Returns the progress bar for showing progress when Excel data is being added to the table. :rtype: QProgressBar """ return self._pg_par def sizeHint(self): return QSize(480, 360) def clear_view(self): # Removes and deletes all sheet widgets and resets the widget registry index. self._tbw.clear() self._ws_info.clear() def reset_view(self): # Clears the view and add an empty default sheet. self.clear_view() self._add_default_sheet() def _add_default_sheet(self): # Add a default/empty sheet to the view. def_sheet = ExcelSheetView() self._tbw.addTab(def_sheet, self.tr('Sheet 1')) def _init_ui(self): # Set up layout and widgets self._vl = QVBoxLayout() self._tbw = QTabWidget() self._tbw.setTabShape(QTabWidget.Triangular) self._tbw.setTabPosition(QTabWidget.South) self._tbw.setStyleSheet('QTabBar::tab:selected { color: green; }') self._vl.addWidget(self._tbw) self._pg_par = QProgressBar() self._pg_par.setVisible(False) self._vl.addWidget(self._pg_par) self.setLayout(self._vl) def add_xlrd_sheet(self, xsheet): """ Adds data contained in a sheet object to the view. :param xsheet: Object containing worksheet data. :type xsheet: xlrd.sheet.Sheet """ worksheet = ExcelSheetView() worksheet.date_format = self._dt_format name = xsheet.name idx = self._tbw.addTab(worksheet, name) self._tbw.setTabToolTip(idx, name) worksheet.load_worksheet(xsheet) # Add worksheet info to collection wsi = WorksheetInfo() wsi.name = name wsi.idx = idx wsi.ws_widget = worksheet wsi.ws = xsheet self._ws_info[wsi.idx] = wsi def load_workbook(self, path): """ Loads the workbook contained in the specified file to the view. :param path: Path to fil containing workbook data. :type path: str """ xfile = QFile(path) if not xfile.exists(): QMessageBox.critical( self, self.tr('Invalid path'), u'\'{0}\' {1}'.format(path, self.tr('does not exist.'))) return # Check permissions xfileinfo = QFileInfo(xfile) if not xfileinfo.isReadable(): QMessageBox.critical( self, self.tr('Unreadable file'), u'{0} {1}'.format(path, self.tr('is not readable.'))) return # Clear view self.clear_view() # Show progress bar self._pg_par.setVisible(True) pg_val = 0 # Add sheets wb = open_workbook(path) self._pg_par.setRange(0, wb.nsheets) for s in wb.sheets(): self.add_xlrd_sheet(s) # Update progress bar pg_val += 1 self._pg_par.setValue(pg_val) self._pg_par.setVisible(False)
class OnlineUpdateGUI(QWidget): CHECK_INTERVAL = 12 * 60 * 60 * 1000 # check twice a day checkForAppUpdate = pyqtSignal() checkForRepoUpdates = pyqtSignal() installUpdates = pyqtSignal() def __init__(self, installedVersion, parent): super(OnlineUpdateGUI, self).__init__(parent) self._canCheckForAppUpdate = False self._canCheckForRepoUpdate = False self._appUpdatesAvailable = False self._repoUpdatesAvailable = False layout = QVBoxLayout(self) layout.addWidget(self._createAppUpdateWidget(installedVersion)) layout.addWidget(self._createRepoUpdateWidget()) self._installUpdatesButton = QPushButton("Install Update(s) and Restart", self) self._installUpdatesButton.clicked.connect(self.installUpdates) self._installUpdatesButton.setEnabled(False) layout.addWidget(self._installUpdatesButton) self._spacing = QWidget(self) layout.addWidget(self._spacing, 1) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) def _createAppUpdateWidget(self, installedVersion): widget = QGroupBox("Application", self) layout = QVBoxLayout(widget) versionLabel = QLabel("Installed Version: " + installedVersion) layout.addWidget(versionLabel, 0) self._appStatusLabel = QLabel(widget) self._appStatusLabel.setWordWrap(True) layout.addWidget(self._appStatusLabel, 0) self._progressBar = QProgressBar(widget) self._progressBar.setVisible(False) layout.addWidget(self._progressBar, 0) self._appCheckButton = QPushButton("Check for Update", widget) self._appCheckButton.clicked.connect(self.checkForAppUpdate) layout.addWidget(self._appCheckButton, 0) self._appChangeLog = QTextEdit(self) self._appChangeLog.setReadOnly(True) self._appChangeLog.setVisible(False) layout.addWidget(self._appChangeLog, 1) return widget def _createRepoUpdateWidget(self): widget = QGroupBox("Plugin Repositories", self) layout = QVBoxLayout(widget) self._repoStatusLabel = QLabel(widget) layout.addWidget(self._repoStatusLabel) self._repoCheckButton = QPushButton("Check for Updates", widget) self._repoCheckButton.clicked.connect(self.checkForRepoUpdates) layout.addWidget(self._repoCheckButton) return widget def setChangelogVisible(self, v): self._appChangeLog.setVisible(v) self._spacing.setVisible(not v) def setProgress(self, prog): self._progressBar.setValue(prog) def setProgressIndeterminate(self, indeterminate): self._progressBar.setMaximum(0 if indeterminate else 100) def setCheckAppUpdateButtonText(self, text=None): if not text: text = "Check for Update" self._appCheckButton.setText(text) def setInteractive(self, interactive): if interactive: self._appCheckButton.setEnabled(self._canCheckForAppUpdate) self._repoCheckButton.setEnabled(self._canCheckForRepoUpdate) self._installUpdatesButton.setEnabled(self._appUpdatesAvailable or self._repoUpdatesAvailable) else: self._appCheckButton.setEnabled(False) self._repoCheckButton.setEnabled(False) self._installUpdatesButton.setEnabled(False) def setCanCheckForAppUpdate(self, can): self._canCheckForAppUpdate = can self._appCheckButton.setEnabled(can) def setCanCheckForRepoUpdate(self, can): self._canCheckForRepoUpdate = can self._repoCheckButton.setEnabled(can) def appInstallReady(self): self._appUpdatesAvailable = True self._installUpdatesButton.setEnabled(True) def setRepoUpdatesAvailable(self, avail): self._repoUpdatesAvailable = True self._installUpdatesButton.setEnabled(avail or self._appUpdatesAvailable) def setRepoStatus(self, status): self._repoStatusLabel.setText(status) def setAppStatus(self, status, progress=False): self._progressBar.setVisible(progress) self._appStatusLabel.setText(status) def setAppStatusToolTip(self, text): self._appStatusLabel.setToolTip(text) def setAppChangeLog(self, log): self._appChangeLog.clear() document = self._appChangeLog.document() document.setIndentWidth(20) cursor = QTextCursor(document) cursor.insertText("Changes:\n") listFormat = QTextListFormat() listFormat.setStyle(QTextListFormat.ListDisc) listFormat2 = QTextListFormat() listFormat2.setStyle(QTextListFormat.ListDisc) listFormat2.setIndent(2) for line in log: if line.startswith("*"): cursor.insertList(listFormat2) line = line[1:] else: cursor.insertList(listFormat) cursor.insertText(line) self.setChangelogVisible(True)
class HoldersTableView(QWidget): """A read-only table view for browsing MS Excel or CSV data.""" def __init__(self, *args, **kwargs): super(QWidget, self).__init__(*args, **kwargs) self._init_ui() # Optional args self._dt_format = kwargs.pop('date_format', '%d-%m-%Y') # Map for sheet widgets self._ws_info = {} # Reset the view self.reset_view() @property def date_format(self): """ :return: Returns the format used to render the date/time in the view. The default formatting will return the date in ISO 8601 format i.e. 'YYYY-MM-DD' where the format is '%Y-%m-%d'. :rtype: str """ return self._dt_format @date_format.setter def date_format(self, format): """ Sets the format used to render the date/time in the view. The format needs to be in a format that is understood by Python's 'strftime()' function. :param format: Format for rendering date/time :type format: str """ self._dt_format = format def worksheet_info(self, idx): """ :param idx: :return: Returns a WorksheetInfo object containing references to the ExcelWorksheetView, xlrd.sheet.Sheet and name of the sheet, will return None if the index does not exist. :rtype: WorksheetInfo """ return self._ws_info.get(idx, None) @property def progress_bar(self): """ :return: Returns the progress bar for showing progress when Excel data is being added to the table. :rtype: QProgressBar """ return self._pg_par def sizeHint(self): return QSize(480, 360) def clear_view(self): # Removes and deletes all sheet widgets and resets the widget registry index. self._tbw.clear() self._ws_info.clear() def reset_view(self): # Clears the view and add an empty default sheet. self.clear_view() self._add_default_sheet() def _add_default_sheet(self): # Add a default/empty sheet to the view. def_sheet = HoldersSheetView() self._tbw.addTab(def_sheet, self.tr('Sheet 1')) def _init_ui(self): # Set up layout and widgets self._vl = QVBoxLayout() self._tbw = QTabWidget() self._tbw.setTabShape(QTabWidget.Triangular) self._tbw.setTabPosition(QTabWidget.South) self._tbw.setStyleSheet('QTabBar::tab:selected { color: green; }') self._vl.addWidget(self._tbw) self._pg_par = QProgressBar() self._pg_par.setVisible(False) self._vl.addWidget(self._pg_par) self.setLayout(self._vl) def add_vector_layer(self, vl): """ Adds data contained in Qgis vector layer object to the view. :param vl: Object containing holders data. :type vl: QgsVectorLayer """ holders_sheet = HoldersSheetView() holders_sheet.date_format = self._dt_format name = vl.name() idx = self._tbw.addTab(holders_sheet, name) self._tbw.setTabToolTip(idx, name) holders_sheet.load_qgs_vector_layer(vl) # Add worksheet info to collection wsi = WorksheetInfo() wsi.name = name wsi.idx = idx wsi.ws_widget = holders_sheet wsi.ws = vl self._ws_info[wsi.idx] = wsi def current_sheet_view(self): """ Gets the sheet view in the current tab view. :return: Sheet view widget. :rtype: HoldersSheetView """ return self._tbw.currentWidget() def sheet_view(self, idx): """ Gets the sheet view widget with the given index. :param idx: Index number. :type idx: int :return: Sheet view widget. :rtype: HoldersSheetView """ return self.worksheet_info(idx).ws_widget def load_holders_file(self, path): """ Loads the holders data contained in the specified file to the view. :param path: Path to file containing holders data. :type path: str """ holders_file = QFile(path) if not holders_file.exists(): QMessageBox.critical( self, self.tr('Invalid path'), u'\'{0}\' {1}'.format(path, self.tr('does not exist.'))) return # Check permissions holders_fileinfo = QFileInfo(holders_file) if not holders_fileinfo.isReadable(): QMessageBox.critical( self, self.tr('Unreadable file'), u'{0} {1}'.format(path, self.tr('is not readable.'))) return # Get file extension ext = holders_fileinfo.suffix() # Get reader based on suffix if ext not in holder_readers: msg = 'No reader defined for \'{0}\' file extension'.format(ext) QMessageBox.critical(self, self.tr('Invalid Extension'), msg) return reader = holder_readers[ext] vl = None try: # Get vector layer vl = reader(path) except Exception as ex: QMessageBox.critical(self, self.tr('Error Loading Data Source.'), str(ex)) return if not vl: QMessageBox.critical( self.parentWidget(), self.tr('Null Data Source'), self.tr('Data source object is None, cannot be loaded.')) return if not vl.isValid(): err = vl.error() if not err.isEmpty(): err_msg = err.summary() else: err_msg = 'The holders data source is invalid.' QMessageBox.critical(self.parentWidget(), self.tr('Invalid Data Source'), err_msg) return # Clear view self.clear_view() # Show progress bar self._pg_par.setVisible(True) pg_val = 0 # Add vector layer to the view self._pg_par.setRange(0, 1) self.add_vector_layer(vl) self._pg_par.setValue(1) self._pg_par.setVisible(False)
class GUI(QWidget): def __init__(self, parent=None): global f f = open(filename, "a") f.write("Widget init.\n") f.close() QWidget.__init__(self, parent, Qt.WindowStaysOnTopHint) self.__setup_gui__(self) self._flag = False self._change = False f = open(filename, "a") f.write("End of widget init.\n") f.close() def closeEvent(self, event): reply = QMessageBox.question(self, "Confirm", "Are you sure You want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() def __setup_gui__(self, Dialog): global f f = open(filename, "a") f.write("Setup of gui.\n") f.close() Dialog.setObjectName("Dialog") Dialog.resize(270, 145) self.setWindowTitle("Map Layer") screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) self.Render = QPushButton("Render", Dialog) self.Render.setGeometry(QRect(85, 90, 100, 25)) self.Render.setObjectName("Render") self.comboBox = QComboBox(Dialog) self.comboBox.setGeometry(QRect(100, 34, 115, 18)) self.comboBox.setEditable(False) self.comboBox.setMaxVisibleItems(11) self.comboBox.setInsertPolicy(QComboBox.InsertAtBottom) self.comboBox.setObjectName("comboBox") self.comboBox.addItems([ "Google Roadmap", "Google Terrain", "Google Satellite", "Google Hybrid", "Yahoo Roadmap", "Yahoo Satellite", "Yahoo Hybrid", "Bing Roadmap", "Bing Satellite", "Bing Hybrid", "Open Street Maps" ]) self.comboBox.setCurrentIndex(10) self.label1 = QLabel("Source:", Dialog) self.label1.setGeometry(QRect(55, 35, 35, 16)) self.label1.setObjectName("label1") self.slider = QSlider(Dialog) self.slider.setOrientation(Qt.Horizontal) self.slider.setMinimum(1) self.slider.setMaximum(12) self.slider.setValue(4) self.slider.setGeometry(QRect(110, 61, 114, 16)) self.label2 = QLabel("Quality: " + str(self.slider.value()), Dialog) self.label2.setGeometry(QRect(47, 61, 54, 16)) self.label2.setObjectName("label2") self.doubleSpinBox = QDoubleSpinBox(Dialog) self.doubleSpinBox.setGeometry(QRect(160, 5, 40, 20)) self.doubleSpinBox.setDecimals(0) self.doubleSpinBox.setObjectName("doubleSpinBox") self.doubleSpinBox.setMinimum(10.0) self.doubleSpinBox.setValue(20.0) self.doubleSpinBox.setEnabled(False) self.checkBox = QCheckBox("Auto refresh", Dialog) self.checkBox.setGeometry(QRect(50, 6, 100, 20)) self.checkBox.setLayoutDirection(Qt.RightToLeft) self.checkBox.setObjectName("checkBox") self.progressBar = QProgressBar(Dialog) self.progressBar.setGeometry(QRect(5, 130, 260, 10)) self.progressBar.setProperty("value", 0) self.progressBar.setTextVisible(False) self.progressBar.setObjectName("progressBar") self.progressBar.setVisible(False) QObject.connect(self.Render, SIGNAL("clicked()"), Dialog.__repaint__) QMetaObject.connectSlotsByName(Dialog) QObject.connect(self.slider, SIGNAL("valueChanged(int)"), self.__update_slider_label__) QObject.connect(self.comboBox, SIGNAL("activated(int)"), self.__combobox_changed__) self.timerRepaint = QTimer() QObject.connect(self.checkBox, SIGNAL("clicked()"), self.__activate_timer__) QObject.connect(self.timerRepaint, SIGNAL("timeout()"), self.__on_timer__) f = open(filename, "a") f.write("End of setup of gui.\n") f.close() def __combobox_changed__(self): self._change = True def __activate_timer__(self): self.doubleSpinBox.setEnabled(self.checkBox.isChecked()) if self.checkBox.isChecked(): self.timerRepaint.start(self.doubleSpinBox.value() * 1000) self.Render.setEnabled(False) if _progress == 0: self.__repaint__() else: self.timerRepaint.stop() self.Render.setEnabled(True) def __get_net_size__(self): global f f = open(filename, "a") f.write("Geting net size...\n") f.close() if not os.path.exists(Paths["Screenshot"]): Visum.Graphic.Screenshot(Paths["Screenshot"]) size = Image.open(Paths["Screenshot"]).size f = open(filename, "a") f.write("Read net size:" + str(size) + ".\n") f.close() return size def __on_timer__(self): global _paramGlobal self._flag = False Visum.Graphic.MaximizeNetWindow() param = _paramGlobal _paramGlobal = Visum.Graphic.GetWindow() shift = abs((param[0] - _paramGlobal[0]) / (param[2] - param[0])) zoom = abs((param[2] - param[0]) / (_paramGlobal[2] - _paramGlobal[0]) - 1) print _windowSizeGlobal if _windowSizeGlobal[2:4] != Visum.Graphic.GetMainWindowPos()[2:4]: self.__get_net_size__() self._flag = True elif shift > 0.4 or zoom > 0.2: self._flag = True if self._flag or self._change and _progress == 0: self.__repaint__() self._change = False def __update_slider_label__(self, value): self.label2.setText("Quality: " + str(value)) self._change = True def __update_progress_bar__(self): if _progress != 0: self.progressBar.setVisible(True) self.progressBar.setValue(_progress) else: self.progressBar.setVisible(False) def __rebuild_paths__(self): global Paths Paths["Images"] = [] list = os.listdir(Paths["ScriptFolder"]) imageList = [] for i in range(len(list)): if list[i][-3:] == "png": imageList.append(list[i]) for i in range(len(imageList)): try: Visum.Graphic.Backgrounds.ItemByKey(imageList[i]) Paths["Images"].append(Paths["ScriptFolder"] + "\\" + imageList[i]) except: pass def __repaint__(self): global _progress, f if len(Visum.Graphic.Backgrounds.GetAll) != len(Paths["Images"]): self.__rebuild_paths__() if _progress == 0: f = open(filename, "a") f.write("Doing repaint...\n") f.close() QWebSettings.clearMemoryCaches() timer = QTimer() timer.start(100) QObject.connect(timer, SIGNAL("timeout()"), self.__update_progress_bar__) Main(self.comboBox.currentIndex(), Visum.Graphic.GetWindow(), self.slider.value() / 4.0, self.__get_net_size__()) Visum.Graphic.Draw() self.__update_progress_bar__() _progress = 0 QTimer().singleShot(1500, self.__update_progress_bar__) f = open(filename, "a") f.write("End of doing repaint.\n") f.close()
class SearchWidget(QFrame): """Widget, appeared, when Ctrl+F pressed. Has different forms for different search modes """ Normal = "normal" Good = "good" Bad = "bad" Incorrect = "incorrect" visibilityChanged = pyqtSignal(bool) """ visibilityChanged(visible) **Signal** emitted, when widget has been shown or hidden """ # pylint: disable=W0105 searchInDirectoryStartPressed = pyqtSignal(type(re.compile("")), list, unicode) """ searchInDirectoryStartPressed(regEx, mask, path) **Signal** emitted, when 'search in directory' button had been pressed """ # pylint: disable=W0105 searchInDirectoryStopPressed = pyqtSignal() """ searchInDirectoryStopPressed() **Signal** emitted, when 'stop search in directory' button had been pressed """ # pylint: disable=W0105 replaceCheckedStartPressed = pyqtSignal(unicode) """ replaceCheckedStartPressed(replText) **Signal** emitted, when 'replace checked' button had been pressed """ # pylint: disable=W0105 replaceCheckedStopPressed = pyqtSignal() """ replaceCheckedStartPressed() **Signal** emitted, when 'stop replacing checked' button had been pressed """ # pylint: disable=W0105 searchRegExpChanged = pyqtSignal(type(re.compile(""))) """ searchRegExpValidStateChanged(regEx) **Signal** emitted, when search regexp has been changed. If reg exp is invalid - regEx object contains empty pattern """ # pylint: disable=W0105 searchNext = pyqtSignal() """ searchNext() **Signal** emitted, when 'Search Next' had been pressed """ # pylint: disable=W0105 searchPrevious = pyqtSignal() """ searchPrevious() **Signal** emitted, when 'Search Previous' had been pressed """ # pylint: disable=W0105 replaceFileOne = pyqtSignal(unicode) """ replaceFileOne(replText) **Signal** emitted, when 'Replace' had been pressed """ # pylint: disable=W0105 replaceFileAll = pyqtSignal(unicode) """ replaceFileAll(replText) **Signal** emitted, when 'Replace All' had been pressed """ # pylint: disable=W0105 def __init__(self, plugin): QFrame.__init__(self, core.workspace()) self._mode = None self.plugin = plugin from PyQt4 import uic # lazy import for better startup performance uic.loadUi(os.path.join(os.path.dirname(__file__), "SearchWidget.ui"), self) self.cbSearch.setCompleter(None) self.cbReplace.setCompleter(None) self.cbMask.setCompleter(None) self.fsModel = QDirModel(self.cbPath.lineEdit()) self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.cbPath.lineEdit().setCompleter(QCompleter(self.fsModel, self.cbPath.lineEdit())) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemodel - please update when possible.""" self.cbSearch.setCompleter(None) self.pbSearchStop.setVisible(False) self.pbReplaceCheckedStop.setVisible(False) self._progress = QProgressBar(self) self._progress.setAlignment(Qt.AlignCenter) self._progress.setToolTip(self.tr("Search in progress...")) self._progress.setMaximumSize(QSize(80, 16)) core.mainWindow().statusBar().insertPermanentWidget(1, self._progress) self._progress.setVisible(False) # cd up action self.tbCdUp = QToolButton(self.cbPath.lineEdit()) self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png")) self.tbCdUp.setCursor(Qt.ArrowCursor) self.tbCdUp.installEventFilter(self) # for drawing button self.cbSearch.installEventFilter(self) # for catching Tab and Shift+Tab self.cbReplace.installEventFilter(self) # for catching Tab and Shift+Tab self.cbPath.installEventFilter(self) # for catching Tab and Shift+Tab self.cbMask.installEventFilter(self) # for catching Tab and Shift+Tab self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self.hide) # connections self.cbSearch.lineEdit().textChanged.connect(self._onSearchRegExpChanged) self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbRegularExpression.stateChanged.connect(self._onSearchRegExpChanged) self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged) self.tbCdUp.clicked.connect(self._onCdUpPressed) core.mainWindow().hideAllWindows.connect(self.hide) core.workspace().currentDocumentChanged.connect( lambda old, new: self.setVisible(self.isVisible() and new is not None) ) def show(self): """Reimplemented function. Sends signal """ super(SearchWidget, self).show() self.visibilityChanged.emit(self.isVisible()) def hide(self): """Reimplemented function. Sends signal, returns focus to workspace """ super(SearchWidget, self).hide() core.workspace().focusCurrentDocument() self.visibilityChanged.emit(self.isVisible()) def setVisible(self, visible): """Reimplemented function. Sends signal """ super(SearchWidget, self).setVisible(visible) self.visibilityChanged.emit(self.isVisible()) def _regExEscape(self, text): """Improved version of re.escape() Doesn't escape space, comma, underscore. Escapes \n and \t """ text = re.escape(text) # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable for symbol in " ,_='\"/:@#%&": text = text.replace("\\" + symbol, symbol) text = text.replace("\\\n", "\\n") text = text.replace("\\\t", "\\t") return text def _makeEscapeSeqsVisible(self, text): """Replace invisible \n and \t with escape sequences """ text = text.replace("\t", "\\t") text = text.replace("\n", "\\n") return text def setMode(self, mode): """Change search mode. i.e. from "Search file" to "Replace directory" """ if self._mode == mode and self.isVisible(): if core.workspace().currentDocument() is not None and not core.workspace().currentDocument().hasFocus(): self.cbSearch.lineEdit().selectAll() self.cbSearch.setFocus() self._mode = mode # Set Search and Replace text if ( core.workspace().currentDocument() is not None and core.workspace().currentDocument().hasFocus() and core.workspace().currentDocument().selectedText() ): searchText = core.workspace().currentDocument().selectedText() self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText)) if self.cbRegularExpression.checkState() == Qt.Checked: searchText = self._regExEscape(searchText) self.cbSearch.setEditText(searchText) if ( not self.cbReplace.lineEdit().text() and self.cbSearch.lineEdit().text() and not self.cbRegularExpression.checkState() == Qt.Checked ): self.cbReplace.setEditText(self.cbSearch.lineEdit().text()) # Move focus to Search edit self.cbSearch.setFocus() self.cbSearch.lineEdit().selectAll() # Set search path if mode & ModeFlagDirectory and not (self.isVisible() and self.cbPath.isVisible()): try: searchPath = os.path.abspath(unicode(os.path.curdir)) self.cbPath.setEditText(searchPath) except OSError: # current directory might have been deleted pass # Set widgets visibility flag according to state widgets = ( self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath, self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask, ) # wSear pbPrev pbNext pbSear wRepl wPath pbRep pbRAll pbRCHK wOpti wMask visible = { ModeSearch: (1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0), ModeReplace: (1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0), ModeSearchDirectory: (1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1), ModeReplaceDirectory: (1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1), ModeSearchOpenedFiles: (1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1), ModeReplaceOpenedFiles: (1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1), } for i, widget in enumerate(widgets): widget.setVisible(visible[mode][i]) # Search next button text if mode == ModeReplace: self.pbNext.setText("Next") else: self.pbNext.setText(u"Next↵") # Finaly show all with valid size self.show() # show before updating widgets and labels self._updateLabels() self._updateWidgets() def eventFilter(self, object_, event): """ Event filter for mode switch tool button Draws icons in the search and path lineEdits """ if event.type() == QEvent.Paint and object_ is self.tbCdUp: # draw CdUp button in search path QLineEdit toolButton = object_ lineEdit = self.cbPath.lineEdit() lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0) height = lineEdit.height() availableRect = QRect(0, 0, height, height) if toolButton.rect() != availableRect: toolButton.setGeometry(availableRect) painter = QPainter(toolButton) toolButton.icon().paint(painter, availableRect) return True elif event.type() == QEvent.KeyPress: # Tab and Shift+Tab in QLineEdits if event.key() == Qt.Key_Tab: self._moveFocus(1) return True elif event.key() == Qt.Key_Backtab: self._moveFocus(-1) return True return QFrame.eventFilter(self, object_, event) def _onReturnPressed(self): """Return or Enter pressed on widget. Search next or Replace next """ if self.pbReplace.isVisible(): self.pbReplace.click() elif self.pbNext.isVisible(): self.pbNext.click() elif self.pbSearch.isVisible(): self.pbSearch.click() elif self.pbSearchStop.isVisible(): self.pbSearchStop.click() def _moveFocus(self, step): """Move focus forward or backward according to step. Standard Qt Keyboard focus algorithm doesn't allow circular navigation """ allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask) visibleWidgets = [widget for widget in allFocusableWidgets if widget.isVisible()] try: focusedIndex = visibleWidgets.index(QApplication.focusWidget()) except ValueError: print >>sys.stderr, "Invalid focused widget in Search Widget" return nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets) visibleWidgets[nextFocusedIndex].setFocus() visibleWidgets[nextFocusedIndex].lineEdit().selectAll() def _updateLabels(self): """Update 'Search' 'Replace' 'Path' labels geometry """ width = 0 if self.lSearch.isVisible(): width = max(width, self.lSearch.minimumSizeHint().width()) if self.lReplace.isVisible(): width = max(width, self.lReplace.minimumSizeHint().width()) if self.lPath.isVisible(): width = max(width, self.lPath.minimumSizeHint().width()) self.lSearch.setMinimumWidth(width) self.lReplace.setMinimumWidth(width) self.lPath.setMinimumWidth(width) def _updateWidgets(self): """Update geometry of widgets with buttons """ width = 0 if self.wSearchRight.isVisible(): width = max(width, self.wSearchRight.minimumSizeHint().width()) if self.wReplaceRight.isVisible(): width = max(width, self.wReplaceRight.minimumSizeHint().width()) if self.wPathRight.isVisible(): width = max(width, self.wPathRight.minimumSizeHint().width()) self.wSearchRight.setMinimumWidth(width) self.wReplaceRight.setMinimumWidth(width) self.wPathRight.setMinimumWidth(width) def _updateComboBoxes(self): """Update comboboxes with last used texts """ searchText = self.cbSearch.currentText() replaceText = self.cbReplace.currentText() maskText = self.cbMask.currentText() # search if searchText: index = self.cbSearch.findText(searchText) if index == -1: self.cbSearch.addItem(searchText) # replace if replaceText: index = self.cbReplace.findText(replaceText) if index == -1: self.cbReplace.addItem(replaceText) # mask if maskText: index = self.cbMask.findText(maskText) if index == -1: self.cbMask.addItem(maskText) def _searchPatternTextAndFlags(self): """Get search pattern and flags """ pattern = self.cbSearch.currentText() if not self.cbRegularExpression.checkState() == Qt.Checked: pattern = re.escape(pattern) flags = 0 if not self.cbCaseSensitive.checkState() == Qt.Checked: flags = re.IGNORECASE return pattern, flags def getRegExp(self): """Read search parameters from controls and present it as a regular expression """ pattern, flags = self._searchPatternTextAndFlags() return re.compile(pattern, flags) def isSearchRegExpValid(self): """Try to compile search pattern to check if it is valid Returns bool result and text error """ pattern, flags = self._searchPatternTextAndFlags() try: re.compile(pattern, flags) except re.error, ex: return False, unicode(ex) return True, None
class MibMainwindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.__setup_ui() self.__connect_slot() self.__entity_parser = EntityParser() self.__entity_parser.parse() def __setup_ui(self): self.resize(1024, 768) self.__setup_menu() split = QSplitter() self.__mib_tree_widget = MibTreeWidget() split.addWidget(self.__mib_tree_widget) # self.__data_widget = DataWidget() split.addWidget(self.__data_widget) split.setHandleWidth(2) split.setStretchFactor(0, 1) split.setStretchFactor(1, 200) la = QVBoxLayout() self.__progress_bar = QProgressBar() self.__progress_bar.setValue(0) self.__progress_bar.setVisible(False) la.addWidget(self.__progress_bar, 1) la.addWidget(split, 1) cw = QWidget() cw.setLayout(la) self.setCentralWidget(cw) def __setup_menu(self): fileMenu = QMenu("Db", self) profileMenu = QMenu('Profiles', self) self.menuBar().addMenu(fileMenu) self.menuBar().addMenu(profileMenu) self.__new_action = QAction(QIcon(ICON_IDR + 'add_database.png'), "&New", fileMenu) fileMenu.addAction(self.__new_action) self.__profile_action = QAction(QIcon(ICON_IDR + 'profiles.png'), "&Open profiles", profileMenu) profileMenu.addAction(self.__profile_action) tool_bar = QToolBar() tool_bar.setMovable(False) tool_bar.setToolButtonStyle(Qt.ToolButtonIconOnly) tool_bar.addAction(self.__new_action) tool_bar.addAction(self.__profile_action) tool_bar.setIconSize(QSize(30, 30)) #tool_bar.setStyleSheet("QToolButton { padding-left: 5px; padding-right: 5px; } QToolBar{padding-left: 5px; padding-right: 5px}") self.addToolBar(tool_bar) def __connect_slot(self): self.connect(self.__new_action, SIGNAL('triggered()'), self, SLOT('__on_new_action()')) self.connect( self.__mib_tree_widget, SIGNAL( 'table_double_clicked(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)' ), self, SLOT( '__on_table_double_clicked(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)' )) @pyqtSlot() def __on_new_action(self): new_dlg = NewConnectionDlg() if new_dlg.exec_(): # for i in range(0,100): # self.__progress_bar.setValue(i) # time.sleep(0.01) # QApplication.processEvents() # self.__progress_bar.setValue(100) olt_key = ConnectionManager().add_dolt_connection( '192.168.200.131', 9192, '111') onu_key = ConnectionManager().add_onu_communicator(olt_key, slot_id=1, intf_id=0, onu_id=1) task = MibLoadTask() self.__progress_bar.setVisible(True) for i, n in task.do(onu_key): self.__progress_bar.setValue(i * 100 / n) QApplication.processEvents() self.__progress_bar.setValue(100) self.__progress_bar.setVisible(False) mib = task.get_mib() # mib = Mib() # mib.load_from_file() keys = [] for k in mib.get_keys(): keys.append(self.__entity_parser.get_cls_name_by_id(k)) self.__mib_tree_widget.add_mib_tree('192.168.200.131', 9192, 1, 0, 1, keys) @pyqtSlot('PyQt_PyObject', 'PyQt_PyObject', 'PyQt_PyObject') def __on_table_double_clicked(self, identify, me_name, label_color): # table_name type is PyQt4.QtCore.QString,con_key type is str mib = Mib() mib.load_from_file() #self.__data_widget.add_tab(identify, to_python_str(con_key), to_python_str(table_name), label_color) me_id = self.__entity_parser.get_me_id_by_me_name(me_name) self.__data_widget.add_tab(identify, self.__entity_parser.get_cls_by_id(me_id), mib.get_entity_class_value(me_id), label_color)