From 757353af029abf4cd232f72c7f14b915ba22c810 Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 13:00:40 -0800 Subject: [PATCH 1/7] REF: untangle camera, global config writing and pull out of gui class to avoid deeply nested tab structures. STY: fix flake8 issues --- camviewer_ui_impl.py | 166 +++++++++++++++++++++---------------------- 1 file changed, 80 insertions(+), 86 deletions(-) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index 37196e5..72e04ca 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -30,7 +30,6 @@ QLabel, QMainWindow, QSpacerItem, - QLayout, QFileDialog, QMessageBox, QAction, @@ -495,7 +494,9 @@ def __init__( self.discoTimer.timeout.connect(self.do_disco) self.ui.average.returnPressed.connect(self.onAverageSet) - self.ui.comboBoxOrientation.currentIndexChanged.connect(self.onOrientationSelect) + self.ui.comboBoxOrientation.currentIndexChanged.connect( + self.onOrientationSelect + ) self.ui.orient0.triggered.connect(lambda: self.setOrientation(param.ORIENT0)) self.ui.orient90.triggered.connect(lambda: self.setOrientation(param.ORIENT90)) self.ui.orient180.triggered.connect( @@ -2198,7 +2199,7 @@ def onAdvanced(self, button): self.ui.showexpert.setChecked(False) def validDisplayFormat(self, rawString): - return re.match("^%\d+(\.\d*)?[efg]$", rawString) is not None + return re.match(r"^%\d+(\.\d*)?[efg]$", rawString) is not None def calibPVmon(self, exception=None): if exception is None: @@ -2416,89 +2417,8 @@ def setDispSpec(self, v): def dumpConfig(self): if self.camera is not None and self.options is None: - f = open(self.cfgdir + self.cameraBase, "w") - g = open(self.cfgdir + "GLOBAL", "w") - - f.write("projsize " + str(self.projsize) + "\n") - f.write("viewwidth " + str(self.viewwidth) + "\n") - f.write("viewheight " + str(self.viewheight) + "\n") - g.write("config " + str(int(self.ui.showconf.isChecked())) + "\n") - g.write("projection " + str(int(self.ui.showproj.isChecked())) + "\n") - g.write("markers " + str(int(self.ui.showmarker.isChecked())) + "\n") - f.write( - "portrait " + str(int(param.orientation == param.ORIENT90)) + "\n" - ) - f.write("orientation " + str(param.orientation) + "\n") - f.write( - "autorange " - + str(int(self.ui.checkBoxProjAutoRange.isChecked())) - + "\n" - ) - f.write("use_abs 1\n") - rz = self.ui.display_image.rectZoom.abs() - f.write( - "rectzoom " - + str(rz.x()) - + " " - + str(rz.y()) - + " " - + str(rz.width()) - + " " - + str(rz.height()) - + "\n" - ) - f.write("colormap " + str(self.ui.comboBoxColor.currentText()) + "\n") - f.write("colorscale " + str(self.ui.comboBoxScale.currentText()) + "\n") - f.write("colormin " + self.ui.lineEditRangeMin.text() + "\n") - f.write("colormax " + self.ui.lineEditRangeMax.text() + "\n") - f.write("grayscale " + str(int(self.ui.grayScale.isChecked())) + "\n") - roi = self.ui.display_image.rectRoi.abs() - f.write( - "ROI %d %d %d %d\n" - % (roi.x(), roi.y(), roi.width(), roi.height()) - ) - f.write("globmarks " + str(int(self.useglobmarks)) + "\n") - f.write("globmarks2 " + str(int(self.useglobmarks2)) + "\n") - lMarker = self.ui.display_image.lMarker - for i in range(4): - f.write( - "m%d %d %d\n" - % (i + 1, lMarker[i].abs().x(), lMarker[i].abs().y()) - ) - g.write("dispspec " + str(self.dispspec) + "\n") - f.write( - "projroi " + str(int(self.ui.checkBoxProjRoi.isChecked())) + "\n" - ) - f.write( - "projlineout " - + str(int(self.ui.checkBoxM1Lineout.isChecked())) - + " " - + str(int(self.ui.checkBoxM2Lineout.isChecked())) - + " " - + str(int(self.ui.checkBoxM3Lineout.isChecked())) - + " " - + str(int(self.ui.checkBoxM4Lineout.isChecked())) - + "\n" - ) - f.write("projfit " + str(int(self.ui.checkBoxFits.isChecked())) + "\n") - f.write( - "projfittype " - + str(int(self.ui.radioGaussian.isChecked())) - + " " - + str(int(self.ui.radioSG4.isChecked())) - + " " - + str(int(self.ui.radioSG6.isChecked())) - + "\n" - ) - f.write( - "projconstant " + str(int(self.ui.checkBoxConstant.isChecked())) + "\n" - ) - f.write("projcalib %g\n" % self.calib) - f.write('projcalibPV "%s"\n' % self.calibPVName) - f.write('projdisplayFormat "%s"\n' % self.displayFormat) - - f.close() - g.close() + write_camera_config(self) + write_global_config(self) settings = QSettings("SLAC", "CamViewer") settings.setValue("geometry/%s" % self.cfgname, self.saveGeometry()) @@ -2789,3 +2709,77 @@ def getConfig(self): self.displayFormat = "%12.8g" self.cfg = None + + +def write_camera_config(self: GraphicUserInterface) -> None: + f = open(self.cfgdir + self.cameraBase, "w") + f.write("projsize " + str(self.projsize) + "\n") + f.write("viewwidth " + str(self.viewwidth) + "\n") + f.write("viewheight " + str(self.viewheight) + "\n") + f.write("portrait " + str(int(param.orientation == param.ORIENT90)) + "\n") + f.write("orientation " + str(param.orientation) + "\n") + f.write("autorange " + str(int(self.ui.checkBoxProjAutoRange.isChecked())) + "\n") + f.write("use_abs 1\n") + rz = self.ui.display_image.rectZoom.abs() + f.write( + "rectzoom " + + str(rz.x()) + + " " + + str(rz.y()) + + " " + + str(rz.width()) + + " " + + str(rz.height()) + + "\n" + ) + f.write("colormap " + str(self.ui.comboBoxColor.currentText()) + "\n") + f.write("colorscale " + str(self.ui.comboBoxScale.currentText()) + "\n") + f.write("colormin " + self.ui.lineEditRangeMin.text() + "\n") + f.write("colormax " + self.ui.lineEditRangeMax.text() + "\n") + f.write("grayscale " + str(int(self.ui.grayScale.isChecked())) + "\n") + roi = self.ui.display_image.rectRoi.abs() + f.write("ROI %d %d %d %d\n" % (roi.x(), roi.y(), roi.width(), roi.height())) + f.write("globmarks " + str(int(self.useglobmarks)) + "\n") + f.write("globmarks2 " + str(int(self.useglobmarks2)) + "\n") + lMarker = self.ui.display_image.lMarker + for i in range(4): + f.write( + "m%d %d %d\n" % (i + 1, lMarker[i].abs().x(), lMarker[i].abs().y()) + ) + f.write("projroi " + str(int(self.ui.checkBoxProjRoi.isChecked())) + "\n") + f.write( + "projlineout " + + str(int(self.ui.checkBoxM1Lineout.isChecked())) + + " " + + str(int(self.ui.checkBoxM2Lineout.isChecked())) + + " " + + str(int(self.ui.checkBoxM3Lineout.isChecked())) + + " " + + str(int(self.ui.checkBoxM4Lineout.isChecked())) + + "\n" + ) + f.write("projfit " + str(int(self.ui.checkBoxFits.isChecked())) + "\n") + f.write( + "projfittype " + + str(int(self.ui.radioGaussian.isChecked())) + + " " + + str(int(self.ui.radioSG4.isChecked())) + + " " + + str(int(self.ui.radioSG6.isChecked())) + + "\n" + ) + f.write("projconstant " + str(int(self.ui.checkBoxConstant.isChecked())) + "\n") + f.write("projcalib %g\n" % self.calib) + f.write('projcalibPV "%s"\n' % self.calibPVName) + f.write('projdisplayFormat "%s"\n' % self.displayFormat) + + f.close() + + +def write_global_config(self: GraphicUserInterface) -> None: + g = open(self.cfgdir + "GLOBAL", "w") + g.write("config " + str(int(self.ui.showconf.isChecked())) + "\n") + g.write("projection " + str(int(self.ui.showproj.isChecked())) + "\n") + g.write("markers " + str(int(self.ui.showmarker.isChecked())) + "\n") + g.write("dispspec " + str(self.dispspec) + "\n") + g.close() From 1ef557f7e79c055e7ca78edcbd54f346bfbcc478 Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 13:04:47 -0800 Subject: [PATCH 2/7] REF: self -> gui in standalone functions --- camviewer_ui_impl.py | 70 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index 72e04ca..1ff3a77 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -2711,16 +2711,16 @@ def getConfig(self): self.cfg = None -def write_camera_config(self: GraphicUserInterface) -> None: - f = open(self.cfgdir + self.cameraBase, "w") - f.write("projsize " + str(self.projsize) + "\n") - f.write("viewwidth " + str(self.viewwidth) + "\n") - f.write("viewheight " + str(self.viewheight) + "\n") +def write_camera_config(gui: GraphicUserInterface) -> None: + f = open(gui.cfgdir + gui.cameraBase, "w") + f.write("projsize " + str(gui.projsize) + "\n") + f.write("viewwidth " + str(gui.viewwidth) + "\n") + f.write("viewheight " + str(gui.viewheight) + "\n") f.write("portrait " + str(int(param.orientation == param.ORIENT90)) + "\n") f.write("orientation " + str(param.orientation) + "\n") - f.write("autorange " + str(int(self.ui.checkBoxProjAutoRange.isChecked())) + "\n") + f.write("autorange " + str(int(gui.ui.checkBoxProjAutoRange.isChecked())) + "\n") f.write("use_abs 1\n") - rz = self.ui.display_image.rectZoom.abs() + rz = gui.ui.display_image.rectZoom.abs() f.write( "rectzoom " + str(rz.x()) @@ -2732,54 +2732,54 @@ def write_camera_config(self: GraphicUserInterface) -> None: + str(rz.height()) + "\n" ) - f.write("colormap " + str(self.ui.comboBoxColor.currentText()) + "\n") - f.write("colorscale " + str(self.ui.comboBoxScale.currentText()) + "\n") - f.write("colormin " + self.ui.lineEditRangeMin.text() + "\n") - f.write("colormax " + self.ui.lineEditRangeMax.text() + "\n") - f.write("grayscale " + str(int(self.ui.grayScale.isChecked())) + "\n") - roi = self.ui.display_image.rectRoi.abs() + f.write("colormap " + str(gui.ui.comboBoxColor.currentText()) + "\n") + f.write("colorscale " + str(gui.ui.comboBoxScale.currentText()) + "\n") + f.write("colormin " + gui.ui.lineEditRangeMin.text() + "\n") + f.write("colormax " + gui.ui.lineEditRangeMax.text() + "\n") + f.write("grayscale " + str(int(gui.ui.grayScale.isChecked())) + "\n") + roi = gui.ui.display_image.rectRoi.abs() f.write("ROI %d %d %d %d\n" % (roi.x(), roi.y(), roi.width(), roi.height())) - f.write("globmarks " + str(int(self.useglobmarks)) + "\n") - f.write("globmarks2 " + str(int(self.useglobmarks2)) + "\n") - lMarker = self.ui.display_image.lMarker + f.write("globmarks " + str(int(gui.useglobmarks)) + "\n") + f.write("globmarks2 " + str(int(gui.useglobmarks2)) + "\n") + lMarker = gui.ui.display_image.lMarker for i in range(4): f.write( "m%d %d %d\n" % (i + 1, lMarker[i].abs().x(), lMarker[i].abs().y()) ) - f.write("projroi " + str(int(self.ui.checkBoxProjRoi.isChecked())) + "\n") + f.write("projroi " + str(int(gui.ui.checkBoxProjRoi.isChecked())) + "\n") f.write( "projlineout " - + str(int(self.ui.checkBoxM1Lineout.isChecked())) + + str(int(gui.ui.checkBoxM1Lineout.isChecked())) + " " - + str(int(self.ui.checkBoxM2Lineout.isChecked())) + + str(int(gui.ui.checkBoxM2Lineout.isChecked())) + " " - + str(int(self.ui.checkBoxM3Lineout.isChecked())) + + str(int(gui.ui.checkBoxM3Lineout.isChecked())) + " " - + str(int(self.ui.checkBoxM4Lineout.isChecked())) + + str(int(gui.ui.checkBoxM4Lineout.isChecked())) + "\n" ) - f.write("projfit " + str(int(self.ui.checkBoxFits.isChecked())) + "\n") + f.write("projfit " + str(int(gui.ui.checkBoxFits.isChecked())) + "\n") f.write( "projfittype " - + str(int(self.ui.radioGaussian.isChecked())) + + str(int(gui.ui.radioGaussian.isChecked())) + " " - + str(int(self.ui.radioSG4.isChecked())) + + str(int(gui.ui.radioSG4.isChecked())) + " " - + str(int(self.ui.radioSG6.isChecked())) + + str(int(gui.ui.radioSG6.isChecked())) + "\n" ) - f.write("projconstant " + str(int(self.ui.checkBoxConstant.isChecked())) + "\n") - f.write("projcalib %g\n" % self.calib) - f.write('projcalibPV "%s"\n' % self.calibPVName) - f.write('projdisplayFormat "%s"\n' % self.displayFormat) + f.write("projconstant " + str(int(gui.ui.checkBoxConstant.isChecked())) + "\n") + f.write("projcalib %g\n" % gui.calib) + f.write('projcalibPV "%s"\n' % gui.calibPVName) + f.write('projdisplayFormat "%s"\n' % gui.displayFormat) f.close() -def write_global_config(self: GraphicUserInterface) -> None: - g = open(self.cfgdir + "GLOBAL", "w") - g.write("config " + str(int(self.ui.showconf.isChecked())) + "\n") - g.write("projection " + str(int(self.ui.showproj.isChecked())) + "\n") - g.write("markers " + str(int(self.ui.showmarker.isChecked())) + "\n") - g.write("dispspec " + str(self.dispspec) + "\n") +def write_global_config(gui: GraphicUserInterface) -> None: + g = open(gui.cfgdir + "GLOBAL", "w") + g.write("config " + str(int(gui.ui.showconf.isChecked())) + "\n") + g.write("projection " + str(int(gui.ui.showproj.isChecked())) + "\n") + g.write("markers " + str(int(gui.ui.showmarker.isChecked())) + "\n") + g.write("dispspec " + str(gui.dispspec) + "\n") g.close() From ce4f2ef484c587cdf8050ea26ade89adcea388aa Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 13:06:04 -0800 Subject: [PATCH 3/7] STY: f, g -> fd (file descriptor) for no more single char vars --- camviewer_ui_impl.py | 68 +++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index 1ff3a77..47f6d66 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -2712,16 +2712,16 @@ def getConfig(self): def write_camera_config(gui: GraphicUserInterface) -> None: - f = open(gui.cfgdir + gui.cameraBase, "w") - f.write("projsize " + str(gui.projsize) + "\n") - f.write("viewwidth " + str(gui.viewwidth) + "\n") - f.write("viewheight " + str(gui.viewheight) + "\n") - f.write("portrait " + str(int(param.orientation == param.ORIENT90)) + "\n") - f.write("orientation " + str(param.orientation) + "\n") - f.write("autorange " + str(int(gui.ui.checkBoxProjAutoRange.isChecked())) + "\n") - f.write("use_abs 1\n") + fd = open(gui.cfgdir + gui.cameraBase, "w") + fd.write("projsize " + str(gui.projsize) + "\n") + fd.write("viewwidth " + str(gui.viewwidth) + "\n") + fd.write("viewheight " + str(gui.viewheight) + "\n") + fd.write("portrait " + str(int(param.orientation == param.ORIENT90)) + "\n") + fd.write("orientation " + str(param.orientation) + "\n") + fd.write("autorange " + str(int(gui.ui.checkBoxProjAutoRange.isChecked())) + "\n") + fd.write("use_abs 1\n") rz = gui.ui.display_image.rectZoom.abs() - f.write( + fd.write( "rectzoom " + str(rz.x()) + " " @@ -2732,22 +2732,24 @@ def write_camera_config(gui: GraphicUserInterface) -> None: + str(rz.height()) + "\n" ) - f.write("colormap " + str(gui.ui.comboBoxColor.currentText()) + "\n") - f.write("colorscale " + str(gui.ui.comboBoxScale.currentText()) + "\n") - f.write("colormin " + gui.ui.lineEditRangeMin.text() + "\n") - f.write("colormax " + gui.ui.lineEditRangeMax.text() + "\n") - f.write("grayscale " + str(int(gui.ui.grayScale.isChecked())) + "\n") + fd.write("colormap " + str(gui.ui.comboBoxColor.currentText()) + "\n") + fd.write("colorscale " + str(gui.ui.comboBoxScale.currentText()) + "\n") + fd.write("colormin " + gui.ui.lineEditRangeMin.text() + "\n") + fd.write("colormax " + gui.ui.lineEditRangeMax.text() + "\n") + fd.write("grayscale " + str(int(gui.ui.grayScale.isChecked())) + "\n") roi = gui.ui.display_image.rectRoi.abs() - f.write("ROI %d %d %d %d\n" % (roi.x(), roi.y(), roi.width(), roi.height())) - f.write("globmarks " + str(int(gui.useglobmarks)) + "\n") - f.write("globmarks2 " + str(int(gui.useglobmarks2)) + "\n") + fd.write( + "ROI %d %d %d %d\n" % (roi.x(), roi.y(), roi.width(), roi.height()) + ) + fd.write("globmarks " + str(int(gui.useglobmarks)) + "\n") + fd.write("globmarks2 " + str(int(gui.useglobmarks2)) + "\n") lMarker = gui.ui.display_image.lMarker for i in range(4): - f.write( + fd.write( "m%d %d %d\n" % (i + 1, lMarker[i].abs().x(), lMarker[i].abs().y()) ) - f.write("projroi " + str(int(gui.ui.checkBoxProjRoi.isChecked())) + "\n") - f.write( + fd.write("projroi " + str(int(gui.ui.checkBoxProjRoi.isChecked())) + "\n") + fd.write( "projlineout " + str(int(gui.ui.checkBoxM1Lineout.isChecked())) + " " @@ -2758,8 +2760,8 @@ def write_camera_config(gui: GraphicUserInterface) -> None: + str(int(gui.ui.checkBoxM4Lineout.isChecked())) + "\n" ) - f.write("projfit " + str(int(gui.ui.checkBoxFits.isChecked())) + "\n") - f.write( + fd.write("projfit " + str(int(gui.ui.checkBoxFits.isChecked())) + "\n") + fd.write( "projfittype " + str(int(gui.ui.radioGaussian.isChecked())) + " " @@ -2768,18 +2770,18 @@ def write_camera_config(gui: GraphicUserInterface) -> None: + str(int(gui.ui.radioSG6.isChecked())) + "\n" ) - f.write("projconstant " + str(int(gui.ui.checkBoxConstant.isChecked())) + "\n") - f.write("projcalib %g\n" % gui.calib) - f.write('projcalibPV "%s"\n' % gui.calibPVName) - f.write('projdisplayFormat "%s"\n' % gui.displayFormat) + fd.write("projconstant " + str(int(gui.ui.checkBoxConstant.isChecked())) + "\n") + fd.write("projcalib %g\n" % gui.calib) + fd.write('projcalibPV "%s"\n' % gui.calibPVName) + fd.write('projdisplayFormat "%s"\n' % gui.displayFormat) - f.close() + fd.close() def write_global_config(gui: GraphicUserInterface) -> None: - g = open(gui.cfgdir + "GLOBAL", "w") - g.write("config " + str(int(gui.ui.showconf.isChecked())) + "\n") - g.write("projection " + str(int(gui.ui.showproj.isChecked())) + "\n") - g.write("markers " + str(int(gui.ui.showmarker.isChecked())) + "\n") - g.write("dispspec " + str(gui.dispspec) + "\n") - g.close() + fd = open(gui.cfgdir + "GLOBAL", "w") + fd.write("config " + str(int(gui.ui.showconf.isChecked())) + "\n") + fd.write("projection " + str(int(gui.ui.showproj.isChecked())) + "\n") + fd.write("markers " + str(int(gui.ui.showmarker.isChecked())) + "\n") + fd.write("dispspec " + str(gui.dispspec) + "\n") + fd.close() From 28f71b39542b859c5b162c7ceb9e79df79fc2ee7 Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 13:07:38 -0800 Subject: [PATCH 4/7] FIX: use context manager for robust file closing --- camviewer_ui_impl.py | 136 +++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index 47f6d66..c796ae9 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -2712,76 +2712,76 @@ def getConfig(self): def write_camera_config(gui: GraphicUserInterface) -> None: - fd = open(gui.cfgdir + gui.cameraBase, "w") - fd.write("projsize " + str(gui.projsize) + "\n") - fd.write("viewwidth " + str(gui.viewwidth) + "\n") - fd.write("viewheight " + str(gui.viewheight) + "\n") - fd.write("portrait " + str(int(param.orientation == param.ORIENT90)) + "\n") - fd.write("orientation " + str(param.orientation) + "\n") - fd.write("autorange " + str(int(gui.ui.checkBoxProjAutoRange.isChecked())) + "\n") - fd.write("use_abs 1\n") - rz = gui.ui.display_image.rectZoom.abs() - fd.write( - "rectzoom " - + str(rz.x()) - + " " - + str(rz.y()) - + " " - + str(rz.width()) - + " " - + str(rz.height()) - + "\n" - ) - fd.write("colormap " + str(gui.ui.comboBoxColor.currentText()) + "\n") - fd.write("colorscale " + str(gui.ui.comboBoxScale.currentText()) + "\n") - fd.write("colormin " + gui.ui.lineEditRangeMin.text() + "\n") - fd.write("colormax " + gui.ui.lineEditRangeMax.text() + "\n") - fd.write("grayscale " + str(int(gui.ui.grayScale.isChecked())) + "\n") - roi = gui.ui.display_image.rectRoi.abs() - fd.write( - "ROI %d %d %d %d\n" % (roi.x(), roi.y(), roi.width(), roi.height()) - ) - fd.write("globmarks " + str(int(gui.useglobmarks)) + "\n") - fd.write("globmarks2 " + str(int(gui.useglobmarks2)) + "\n") - lMarker = gui.ui.display_image.lMarker - for i in range(4): + with open(gui.cfgdir + gui.cameraBase, "w") as fd: + fd.write("projsize " + str(gui.projsize) + "\n") + fd.write("viewwidth " + str(gui.viewwidth) + "\n") + fd.write("viewheight " + str(gui.viewheight) + "\n") + fd.write("portrait " + str(int(param.orientation == param.ORIENT90)) + "\n") + fd.write("orientation " + str(param.orientation) + "\n") fd.write( - "m%d %d %d\n" % (i + 1, lMarker[i].abs().x(), lMarker[i].abs().y()) + "autorange " + str(int(gui.ui.checkBoxProjAutoRange.isChecked())) + "\n" ) - fd.write("projroi " + str(int(gui.ui.checkBoxProjRoi.isChecked())) + "\n") - fd.write( - "projlineout " - + str(int(gui.ui.checkBoxM1Lineout.isChecked())) - + " " - + str(int(gui.ui.checkBoxM2Lineout.isChecked())) - + " " - + str(int(gui.ui.checkBoxM3Lineout.isChecked())) - + " " - + str(int(gui.ui.checkBoxM4Lineout.isChecked())) - + "\n" - ) - fd.write("projfit " + str(int(gui.ui.checkBoxFits.isChecked())) + "\n") - fd.write( - "projfittype " - + str(int(gui.ui.radioGaussian.isChecked())) - + " " - + str(int(gui.ui.radioSG4.isChecked())) - + " " - + str(int(gui.ui.radioSG6.isChecked())) - + "\n" - ) - fd.write("projconstant " + str(int(gui.ui.checkBoxConstant.isChecked())) + "\n") - fd.write("projcalib %g\n" % gui.calib) - fd.write('projcalibPV "%s"\n' % gui.calibPVName) - fd.write('projdisplayFormat "%s"\n' % gui.displayFormat) - - fd.close() + fd.write("use_abs 1\n") + rz = gui.ui.display_image.rectZoom.abs() + fd.write( + "rectzoom " + + str(rz.x()) + + " " + + str(rz.y()) + + " " + + str(rz.width()) + + " " + + str(rz.height()) + + "\n" + ) + fd.write("colormap " + str(gui.ui.comboBoxColor.currentText()) + "\n") + fd.write("colorscale " + str(gui.ui.comboBoxScale.currentText()) + "\n") + fd.write("colormin " + gui.ui.lineEditRangeMin.text() + "\n") + fd.write("colormax " + gui.ui.lineEditRangeMax.text() + "\n") + fd.write("grayscale " + str(int(gui.ui.grayScale.isChecked())) + "\n") + roi = gui.ui.display_image.rectRoi.abs() + fd.write( + "ROI %d %d %d %d\n" % (roi.x(), roi.y(), roi.width(), roi.height()) + ) + fd.write("globmarks " + str(int(gui.useglobmarks)) + "\n") + fd.write("globmarks2 " + str(int(gui.useglobmarks2)) + "\n") + lMarker = gui.ui.display_image.lMarker + for i in range(4): + fd.write( + "m%d %d %d\n" + % (i + 1, lMarker[i].abs().x(), lMarker[i].abs().y()) + ) + fd.write("projroi " + str(int(gui.ui.checkBoxProjRoi.isChecked())) + "\n") + fd.write( + "projlineout " + + str(int(gui.ui.checkBoxM1Lineout.isChecked())) + + " " + + str(int(gui.ui.checkBoxM2Lineout.isChecked())) + + " " + + str(int(gui.ui.checkBoxM3Lineout.isChecked())) + + " " + + str(int(gui.ui.checkBoxM4Lineout.isChecked())) + + "\n" + ) + fd.write("projfit " + str(int(gui.ui.checkBoxFits.isChecked())) + "\n") + fd.write( + "projfittype " + + str(int(gui.ui.radioGaussian.isChecked())) + + " " + + str(int(gui.ui.radioSG4.isChecked())) + + " " + + str(int(gui.ui.radioSG6.isChecked())) + + "\n" + ) + fd.write("projconstant " + str(int(gui.ui.checkBoxConstant.isChecked())) + "\n") + fd.write("projcalib %g\n" % gui.calib) + fd.write('projcalibPV "%s"\n' % gui.calibPVName) + fd.write('projdisplayFormat "%s"\n' % gui.displayFormat) def write_global_config(gui: GraphicUserInterface) -> None: - fd = open(gui.cfgdir + "GLOBAL", "w") - fd.write("config " + str(int(gui.ui.showconf.isChecked())) + "\n") - fd.write("projection " + str(int(gui.ui.showproj.isChecked())) + "\n") - fd.write("markers " + str(int(gui.ui.showmarker.isChecked())) + "\n") - fd.write("dispspec " + str(gui.dispspec) + "\n") - fd.close() + with open(gui.cfgdir + "GLOBAL", "w") as fd: + fd.write("config " + str(int(gui.ui.showconf.isChecked())) + "\n") + fd.write("projection " + str(int(gui.ui.showproj.isChecked())) + "\n") + fd.write("markers " + str(int(gui.ui.showmarker.isChecked())) + "\n") + fd.write("dispspec " + str(gui.dispspec) + "\n") From 938b92fff223ce78e6b86720ba61086bc6f005e9 Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 14:03:24 -0800 Subject: [PATCH 5/7] ENH: ensure atomic writes to the final dir for config cache files --- camviewer_ui_impl.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index c796ae9..8c17a77 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -24,6 +24,10 @@ import time import functools import numpy as np +import tempfile +import shutil +import typing +import contextlib from PyQt5.QtWidgets import ( QSizePolicy, @@ -2712,7 +2716,7 @@ def getConfig(self): def write_camera_config(gui: GraphicUserInterface) -> None: - with open(gui.cfgdir + gui.cameraBase, "w") as fd: + with atomic_writer(gui.cfgdir + gui.cameraBase) as fd: fd.write("projsize " + str(gui.projsize) + "\n") fd.write("viewwidth " + str(gui.viewwidth) + "\n") fd.write("viewheight " + str(gui.viewheight) + "\n") @@ -2780,8 +2784,18 @@ def write_camera_config(gui: GraphicUserInterface) -> None: def write_global_config(gui: GraphicUserInterface) -> None: - with open(gui.cfgdir + "GLOBAL", "w") as fd: + with atomic_writer(gui.cfgdir + "GLOBAL") as fd: fd.write("config " + str(int(gui.ui.showconf.isChecked())) + "\n") fd.write("projection " + str(int(gui.ui.showproj.isChecked())) + "\n") fd.write("markers " + str(int(gui.ui.showmarker.isChecked())) + "\n") fd.write("dispspec " + str(gui.dispspec) + "\n") + + +@contextlib.contextmanager +def atomic_writer(path: str) -> typing.Iterator[typing.TextIO]: + with tempfile.NamedTemporaryFile("w", delete=False) as fd: + yield fd + fd.close() + # Set -rw-r--r-- instead of temp file default -rw------- + os.chmod(fd.name, 0o644) + shutil.move(fd.name, path) From ad6165bfda5533a7ee28d3623a4784aedff61ab8 Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 14:04:10 -0800 Subject: [PATCH 6/7] DOC: add a note about closing the file early --- camviewer_ui_impl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index 8c17a77..e42d332 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -2795,6 +2795,7 @@ def write_global_config(gui: GraphicUserInterface) -> None: def atomic_writer(path: str) -> typing.Iterator[typing.TextIO]: with tempfile.NamedTemporaryFile("w", delete=False) as fd: yield fd + # File must be closed before we can chmod and move it fd.close() # Set -rw-r--r-- instead of temp file default -rw------- os.chmod(fd.name, 0o644) From 0d6a35283b65d1dff57d7a97ce8060c88f9db8d1 Mon Sep 17 00:00:00 2001 From: Zachary Lentz Date: Fri, 22 Nov 2024 14:27:51 -0800 Subject: [PATCH 7/7] ENH: error handling to avoid half-written files --- camviewer_ui_impl.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/camviewer_ui_impl.py b/camviewer_ui_impl.py index e42d332..1e7af52 100644 --- a/camviewer_ui_impl.py +++ b/camviewer_ui_impl.py @@ -2794,9 +2794,19 @@ def write_global_config(gui: GraphicUserInterface) -> None: @contextlib.contextmanager def atomic_writer(path: str) -> typing.Iterator[typing.TextIO]: with tempfile.NamedTemporaryFile("w", delete=False) as fd: - yield fd - # File must be closed before we can chmod and move it - fd.close() - # Set -rw-r--r-- instead of temp file default -rw------- - os.chmod(fd.name, 0o644) - shutil.move(fd.name, path) + try: + yield fd + except Exception as exc: + # There is some issue and the temp file is not complete. + # Avoid the else block, we don't want to keep the corrupt file. + # Show some error instead of bricking the gui + print(f"Error writing {path}: {exc}") + else: + # File must be closed before we can chmod and move it + fd.close() + # Set -rw-r--r-- instead of temp file default -rw------- + os.chmod(fd.name, 0o644) + shutil.move(fd.name, path) + # If the tempfile still exists, we should clean it up. + if os.path.exists(fd.name): + os.remove(fd.name)