diff --git a/fixhdr/fixhdr.py b/fixhdr/fixhdr.py index 336430784b8d2343d86d856613b2998217212207..df5d68e84907f7594262f42f290d304b467b8696 100644 --- a/fixhdr/fixhdr.py +++ b/fixhdr/fixhdr.py @@ -302,6 +302,7 @@ # ie, self.DataDirs_e for the tkinter Entry widget was changed to # self.DataDirs_le for the PySide LineEdit widget. # Added a button in Global Modify to clear location codes. +# Implemented heavy functions to run as threads. ################################################################ """ @@ -318,8 +319,8 @@ from PySide2.QtWidgets import (QApplication, QWidget, QComboBox, QLineEdit, QHBoxLayout, QGridLayout, QGroupBox, QLabel, QFormLayout, QSizePolicy, QSpacerItem, QTextEdit, QFileDialog, - QMessageBox) -from PySide2.QtCore import Qt + QMessageBox, QProgressDialog) +from PySide2.QtCore import Qt, QThread, QObject, Signal, QEventLoop, QTimer from PySide2.QtGui import QColor, QTextCursor from fixhdr.LibTrace import * @@ -2283,15 +2284,22 @@ records have no blockettes, so the strings start at offset statsel = statsel.strip() self.StatSelList.append(statsel) - # find & ID traces in DataDirList, setup various list for nb's - # first I set self.RunBuilddb so we know that this is running and I - # have a variable to watch status of process - # next I spawn a thread to FindTrace self.RunBuilddb = 1 - # tRun=threading.Thread(target=self.LaunchFindTrace) - self.launchfindTrace() + # run findTrace as a thread + if not BATCHMODE: + self.initThread("Build Trace db", + self.launchfindTrace, + self.afterBuildTrace) + else: + self.launchfindTrace() + +######################################## + def afterBuildTrace(self): + """ + Update fields after findTrace finishes + """ # fill/update notebooks if not running batch mode if not BATCHMODE: info = "Building lists, updating frames" @@ -2334,19 +2342,12 @@ records have no blockettes, so the strings start at offset # since we just rebuilt db no files have been altered self.AlteredText = "" -######################################## - def launchfindTrace(self): """ Separated out so FindTrace could be run as a thread """ - (self.TraceDict, self.TraceTimeDict, NumFiles, rwError) = \ - self.findTrace(self.DataDirList) - - self.ActiveText = "" - self.FoundFiles = NumFiles - self.RWErrors = rwError + self.findTrace(self.DataDirList) ######################################## @@ -2408,7 +2409,10 @@ records have no blockettes, so the strings start at offset if mod(cnt, 10): pass else: - self.wait("Examining File: ", cnt) + if not BATCHMODE: + self.funcWorker.update.emit(cnt) + else: + self.wait("Examining File: ", cnt) # build up list of mseed files w/ full pathnames fullname = losjoin(directory, file) if losisfile(fullname): @@ -2490,7 +2494,12 @@ records have no blockettes, so the strings start at offset not losisfile(fullname)): if fullname not in stack: stackappend(fullname) - return file_list, time_list, NumMseedFiles, rwError + + self.TraceDict = file_list + self.TraceTimeDict = time_list + self.ActiveText = "" + self.FoundFiles = NumMseedFiles + self.RWErrors = rwError ######################################## @@ -2601,19 +2610,11 @@ records have no blockettes, so the strings start at offset # if the user ignores warning proceed self.RunModHdrs = 1 - - # mRun=threading.Thread(target=self.ModHdrs) - self.modHdrs() - # mRun.start() - # stay here until thread dies - # mRun.join() - - ''' - if self.RunModHdrs: - self.killshowCancelTL(self.RunModHdrs) - ''' self.IgnoreWarn = 0 + # run as thread + self.initThread("Modify Headers", self.modHdrs) + ######################################## def modHdrs(self): @@ -2689,7 +2690,10 @@ records have no blockettes, so the strings start at offset if mod(cnt, 10): pass else: - self.wait("Modifying Trace: ", cnt) + if not BATCHMODE: + self.funcWorker.update.emit(cnt) + else: + self.wait("Modifying Trace: ", cnt) # determine file size and get block size from # Blockette 1000 @@ -2975,23 +2979,8 @@ records have no blockettes, so the strings start at offset # write log and user information self.writeLog("\n<Modify Time>", "Time", "blue") - # tRun=threading.Thread(target=self.ApplyTimeCor) - self.applyTimeCor() - # tRun.start() - # stay here until thread dies - # tRun.join() - - # nuke top level CancelTL - """ - if self.RunApplyTimeCor: - self.killshowCancelTL(self.RunApplyTimeCor) - """ - - # some cleanup - self.writeLog("<end>", "Time", "blue") - self.resetTimeShift() - self.IgnoreWarn = 0 - self.UseNewOnlyTime = 0 + # run as thread + self.initThread("Apply Time Corrections", self.applyTimeCor) ######################################## @@ -3180,7 +3169,10 @@ records have no blockettes, so the strings start at offset if mod(num_cnt, 10): pass else: - self.wait(textstr, num_cnt) + if not BATCHMODE: + self.funcWorker.update.emit(num_cnt) + else: + self.wait(textstr, num_cnt) # keep track of time corrections applied if key in self.UpdateTimeDict: @@ -3222,6 +3214,13 @@ records have no blockettes, so the strings start at offset self.ActiveText = "" # self.writeLog("<end>\n", "Time", "blue") + # some cleanup + self.writeLog("<end>", "Time", "blue") + if not BATCHMODE: + self.resetTimeShift() + self.IgnoreWarn = 0 + self.UseNewOnlyTime = 0 + ######################################## def launchUndoTimeCor(self): @@ -3704,8 +3703,8 @@ records have no blockettes, so the strings start at offset else: self.TSEndTime = end # first record has min/max time for keyname - cnt = len(self.TraceTimeDict[keyname]) - 1 - textstr = str(cnt) + " traces selected for key: " + keyname + self.timeCnt = len(self.TraceTimeDict[keyname]) - 1 + textstr = str(self.timeCnt) + " traces selected for key: " + keyname self.addTextInfoBar(textstr, 'lightblue') ######################################## @@ -3814,20 +3813,15 @@ records have no blockettes, so the strings start at offset if endian == "big": self.ToEndian = "big" + title = "Change Endian To Big" else: self.ToEndian = "little" + title = "Change Endian To Little" # launch thread - # eRun=threading.Thread(target=self.ChangeEndian) - self.changeEndian() - # eRun.start() - # stay here until thread dies - # eRun.join() - - ''' - if self.RunChangeEndian.get(): - self.killshowCancelTL(self.RunChangeEndian) - ''' - self.ActiveText = "" + + # run as thread + # func = lambda: self.changeEndian() + self.initThread(title, self.changeEndian) ######################################## @@ -3856,8 +3850,11 @@ records have no blockettes, so the strings start at offset return files_left = numfiles - textstr = "Files Remaining: " - self.wait(textstr, files_left) + if BATCHMODE: + textstr = "Files Remaining: " + self.wait(textstr, files_left) + else: + self.addTextInfoBar() # write log and user information self.writeLog("\n<Change Endianess>", "Endian", "blue") @@ -3877,7 +3874,10 @@ records have no blockettes, so the strings start at offset if mod(files_left, 5): pass else: - self.wait(textstr, files_left) + if BATCHMODE: + self.wait(textstr, files_left) + else: + self.funcWorker.update.emit(corr_files) files_left -= 1 msfile = Mseed(ifile) @@ -3929,8 +3929,25 @@ records have no blockettes, so the strings start at offset n += numblocks break else: - bytes_written = PutBlk( - blktype, blklist, endian, seekval) + try: + bytes_written = PutBlk( + blktype, blklist, endian, seekval) + except Exception as e: + # if function not found + # undo endian changes + err = ("\n\tError at file " + str(ifile) + + ":\n\tself.Writeblk") + err += (str(blktype) + " not in LibTrace.py") + self.writeLog(err, "Endian", "red") + msfile.close() + self.undoEndian(ifile, n, b, endian) + # remove file from list + outlist.pop() + # set error flag + ErrFlag = 1 + # ensure outer while block will end + n += numblocks + break addseek = next b += 1 n += 1 @@ -3962,6 +3979,8 @@ records have no blockettes, so the strings start at offset infotext = "\nDone. Converted: " + str(corr_files) + " files." self.addTextInfoBar(infotext, 'green') + self.ActiveText = "" + ######################################## def undoEndian(self, infile, recnum, blknum, endian): @@ -4441,12 +4460,15 @@ records have no blockettes, so the strings start at offset """ Sets Value of var """ + print(var) + print(value) if var == "DataDirs": for le in self.DataDirLeList: le.clear() else: var = value + print("hi?") ######################################## @@ -5165,6 +5187,111 @@ records have no blockettes, so the strings start at offset # set cursor to beginning CorText.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor) +######################################## + + def initThread(self, title, func, afterFunc=''): + """ + Create thread to run function + """ + + self.thread = QThread(self) + self.funcWorker = Worker(self, func) + + # set signals/slots + self.funcWorker.update.connect(self.updateProg) + self.thread.started.connect(self.funcWorker.run) + # function to run after thread finishes + if afterFunc: + self.funcWorker.finished.connect(afterFunc) + + # delete worker and thread when done + self.funcWorker.finished.connect(self.thread.quit) + self.funcWorker.finished.connect(self.funcWorker.deleteLater) + self.thread.finished.connect(self.thread.deleteLater) + + # launch progress window + if not BATCHMODE: + # get file num for progress window + numFiles = 0 + if title == "Build Trace db": + for dir in self.DataDirList: + numFiles += ( + sum([len(files) for r, d, files in os.walk(dir)])) + + elif title == "Modify Headers": + inlist = sorted(list(self.UpdateHdrDict.keys())) + for key in inlist: + ifilelist = (ifile for ifile in self.TraceDict[key]) + numFiles += (sum(1 for _ in ifilelist)) + + elif title == "Apply Time Corrections": + numFiles = self.timeCnt + + elif title == "Change Endian To Little": + numFiles = len(self.BigEndianList) + + elif title == "Change Endian To Big": + numFiles = len(self.LittleEndianList) + + self.initProgWindow(title, numFiles) + self.funcWorker.finished.connect(lambda: self.progWin.done(0)) + loop = QEventLoop(self) + QTimer.singleShot(100, loop.quit) + loop.exec_() + + self.thread.start() + +######################################## + + def initProgWindow(self, title, numFiles): + """ + Create progress dialog to update user of + current process status + """ + + text = title + " is Active. Please wait." + self.progWin = QProgressDialog( + labelText=text, minimum=0, + maximum=numFiles, parent=self) + + cancel_b = QPushButton("Cancel") + cancel_b.setStyleSheet( + "QPushButton::hover{background-color:red;}") + cancel_b.clicked.connect(lambda: self.cancelFunc(title)) + self.progWin.setCancelButton(cancel_b) + + self.progWin.open() + +######################################## + + def cancelFunc(self, title): + """ + If user cancels current progress + set loop var to 0 + """ + if title == "Build Trace db": + self.RunBuilddb = 0 + elif title == "Modify Headers": + self.RunModHdrs = 0 + elif title == "Apply Time Corrections": + self.RunApplyTimeCor = 0 + else: + self.RunChangeEndian = 0 + +######################################## + + def updateProg(self, cnt): + """ + Update progress for user + """ + if not BATCHMODE: + self.progWin.setValue(cnt) + else: + self.wait(cnt) + + def setProgMax(self, max): + self.progWin.setMaximum(max) + ######################################## def closeEvent(self, event): @@ -5177,6 +5304,24 @@ records have no blockettes, so the strings start at offset # end of main class +class Worker(QObject): + """ + Thread worker + """ + update = Signal(int) + new_max = Signal(int) + finished = Signal() + + def __init__(self, parent, func): + super().__init__() + self.parent = parent + self.func = func + + def run(self): + self.func() + self.finished.emit() + + def main(): app = QApplication() if BatchFile: