From c8454694029946c52f36b9f2278b5acb7632ab78 Mon Sep 17 00:00:00 2001 From: Alex Page Date: Mon, 20 Feb 2023 11:18:36 -0600 Subject: [PATCH] RSG005: Generic "Sample Name" Input * Change the "Sample #" item on the data selection screen to a "Sample Name" item. * Update all references to a sample's "number" to use the "name" field instead * Backwards-compatibility: Old projects that save samples with a "number" field will be loaded with a sample name of "Sample {number}" --- app/gui/app_window.py | 4 +--- app/gui/frame/asc_data_frame.py | 21 ++++++++++--------- app/gui/frame/data_trim_frame.py | 4 +--- app/gui/frame/final_plot_frame.py | 18 +++++++--------- app/gui/frame/sample_edit_frame.py | 2 +- app/gui/input_group/precision_input.py | 2 +- app/gui/input_group/sample_file_input.py | 2 +- ...mple_num_input.py => sample_name_input.py} | 4 ++-- app/project/project_dir.py | 3 +++ app/project/sample.py | 12 +++++++---- app/util/pdf.py | 3 +-- poetry.lock | 20 +++++++++++++++++- run.py | 3 +-- 13 files changed, 58 insertions(+), 40 deletions(-) rename app/gui/input_group/{sample_num_input.py => sample_name_input.py} (87%) diff --git a/app/gui/app_window.py b/app/gui/app_window.py index 9e04d77..ea236c5 100644 --- a/app/gui/app_window.py +++ b/app/gui/app_window.py @@ -28,9 +28,7 @@ def content_update(self): if proj is None: self.title(PROJECT_TITLE) else: - self.title( - proj.title + (" - Sample {}".format(cs.num) if cs is not None else "") - ) + self.title(proj.title + (f" - {cs.name}" if cs is not None else "")) self._main_frame.content_update() def bind_menu_actions(self): diff --git a/app/gui/frame/asc_data_frame.py b/app/gui/frame/asc_data_frame.py index 0fbf4e7..f30ce73 100644 --- a/app/gui/frame/asc_data_frame.py +++ b/app/gui/frame/asc_data_frame.py @@ -3,7 +3,7 @@ from app.gui.frame.abstract_tab_frame import AbstractTabFrame from app.gui import GUI_FONT from app.gui.input_group.sample_file_input import SampleFileInputGroup -from app.gui.input_group.sample_num_input import SampleNumberInputGroup +from app.gui.input_group.sample_name_input import SampleNameInputGroup class ASCDataFrame(AbstractTabFrame): @@ -11,20 +11,21 @@ class ASCDataFrame(AbstractTabFrame): def __init__(self, parent, proj_ptr, next_frame): super().__init__(parent, "Raw Data", proj_ptr, next_frame) - self.num_input = SampleNumberInputGroup(self, font=GUI_FONT) + self.name_input = SampleNameInputGroup(self, font=GUI_FONT) self.dir_input = SampleFileInputGroup(self, font=GUI_FONT) - self._num = 0 - self.num_input.pack(pady=15) + self.name_input.pack(pady=15) self.dir_input.pack(pady=15) Button(self, font=GUI_FONT, text="Done", command=self.on_next).pack() def content_update(self): s = self._proj_handle.curr_sample - if s.num is not None: - self.num_input.set(s.num) + + # Create a "suggested name" based on current number of samples in the project. + if s.name is not None: + self.name_input.set(s.name) else: - self._num += 1 - self.num_input.set(self._num) + n_samples = self._proj_handle.project.num_samples() + self.name_input.set(f"Sample {n_samples}") if s.dir is None: self.dir_input.set_dir("") @@ -33,9 +34,9 @@ def content_update(self): self.dir_input.set_initialdir(self._proj_handle.project.data_dir) def is_done(self): - return self.num_input.entries_valid() and self.dir_input.entries_valid() + return self.name_input.entries_valid() and self.dir_input.entries_valid() def unload(self): s = self._proj_handle.curr_sample - s.num = int(self.num_input.get_num()) + s.name = self.name_input.get_value() s.set_data_from_file(self.dir_input.get_dir()) diff --git a/app/gui/frame/data_trim_frame.py b/app/gui/frame/data_trim_frame.py index 2f72729..876d53a 100644 --- a/app/gui/frame/data_trim_frame.py +++ b/app/gui/frame/data_trim_frame.py @@ -111,9 +111,7 @@ def on_cutoff_set(self): self.canvas.draw() def on_click(self, event): - if not event.inaxes: - return - if self.nav._active == "ZOOM": + if not event.inaxes or event.inaxes.get_navigate_mode() == "ZOOM": return self._cs.set_zero(event.xdata) self.set_zeroline(event.xdata) diff --git a/app/gui/frame/final_plot_frame.py b/app/gui/frame/final_plot_frame.py index 6a4bf61..d47a5c1 100644 --- a/app/gui/frame/final_plot_frame.py +++ b/app/gui/frame/final_plot_frame.py @@ -46,39 +46,37 @@ def unload(self): project = self._proj_handle.project pdf_dir = project.pdf_dir - info_file = "{}temp/S{}_INFO.pdf".format(pdf_dir, s.num) + info_file = "{}temp/S_{}_INFO.pdf".format(pdf_dir, s.name) generate_sample_layer(s, info_file) - pl_file = "{}temp/S{}_PL.pdf".format(pdf_dir, s.num) + pl_file = "{}temp/S_{}_PL.pdf".format(pdf_dir, s.name) self.peakloadframe.canvas.figure.savefig(pl_file) create_pdf( info_file, project.template_file, pl_file, - pdf_dir + "Sample #{} (PeakLoad).pdf".format(s.num), + pdf_dir + "{} (PeakLoad).pdf".format(s.name), ) - uts_file = "{}temp/S{}_UTS.pdf".format(pdf_dir, s.num) + uts_file = "{}temp/S_{}_UTS.pdf".format(pdf_dir, s.name) self.utsframe.canvas.figure.savefig(uts_file) create_pdf( info_file, project.template_file, uts_file, - pdf_dir + "Sample #{} (UTS).pdf".format(s.num), + pdf_dir + "{} (UTS).pdf".format(s.name), ) - yl_file = "{}temp/S{}_YL.pdf".format(pdf_dir, s.num) + yl_file = "{}temp/S_{}_YL.pdf".format(pdf_dir, s.name) self.yieldloadframe.canvas.figure.savefig(yl_file) create_pdf( info_file, project.template_file, yl_file, - pdf_dir + "Sample #{} (YieldLoad).pdf".format(s.num), + pdf_dir + "{} (YieldLoad).pdf".format(s.name), ) - messagebox.showinfo( - title="Success", message="Created 3 files in {}".format(pdf_dir) - ) + messagebox.showinfo(title="Success", message=f"Created 3 files in {pdf_dir}") def build(self): self.canvasnotebook.pack() diff --git a/app/gui/frame/sample_edit_frame.py b/app/gui/frame/sample_edit_frame.py index 38fb890..715698a 100644 --- a/app/gui/frame/sample_edit_frame.py +++ b/app/gui/frame/sample_edit_frame.py @@ -64,7 +64,7 @@ def content_update(self): Call propagates downward to the visible editing frame""" p = self._project_handler.project s = self._project_handler.curr_sample - self.curr_label["text"] = "RSG {0:0>4}, Sample {1}".format(p.number, s.num) + self.curr_label["text"] = f"RSG {p.number:0>4}, {s.name}" if self._recent_root != p.root: # New Project, go to infoframe self.set_frame(0) self.progress_frame.set(0, False) diff --git a/app/gui/input_group/precision_input.py b/app/gui/input_group/precision_input.py index 266959f..ed88a2d 100644 --- a/app/gui/input_group/precision_input.py +++ b/app/gui/input_group/precision_input.py @@ -11,7 +11,7 @@ def __init__(self, parent, font): f = Frame(self) for i in range(3): r = Radiobutton( - f, variable=self._var, val=i, text="{}".format(10 ** i), font=font + f, variable=self._var, val=i, text="{}".format(10**i), font=font ) self._buttons.append(r) r.pack(side=LEFT) diff --git a/app/gui/input_group/sample_file_input.py b/app/gui/input_group/sample_file_input.py index 2720a12..a746b5d 100644 --- a/app/gui/input_group/sample_file_input.py +++ b/app/gui/input_group/sample_file_input.py @@ -8,7 +8,7 @@ class SampleFileInputGroup(Frame): def __init__(self, parent, font, **kwargs): super().__init__(parent, **kwargs) self.parent = parent - self.sample_dir = Entry(self, width=30, font=font) + self.sample_dir = Entry(self, width=40, font=font) Label(self, text="ASC File ", font=font).pack(side=LEFT) self.sample_dir.pack(side=LEFT) Button(self, text="Browse", font=font, command=self.browse).pack(side=LEFT) diff --git a/app/gui/input_group/sample_num_input.py b/app/gui/input_group/sample_name_input.py similarity index 87% rename from app/gui/input_group/sample_num_input.py rename to app/gui/input_group/sample_name_input.py index 05b7d7c..55bff8d 100644 --- a/app/gui/input_group/sample_num_input.py +++ b/app/gui/input_group/sample_name_input.py @@ -3,12 +3,12 @@ import re -class SampleNumberInputGroup(Frame): +class SampleNameInputGroup(Frame): """Input sample number, which MUST be a positive whole number""" def __init__(self, parent, font, **kwargs): super().__init__(parent, **kwargs) - self.sample_num = Entry(self, width=3, font=font) + self.sample_num = Entry(self, width=40, font=font) Label(self, text="Sample # ", font=font).pack(side=LEFT) self.sample_num.pack(side=LEFT) self.re = re.compile("^\d+$") diff --git a/app/project/project_dir.py b/app/project/project_dir.py index e2db86d..0c9b7dd 100644 --- a/app/project/project_dir.py +++ b/app/project/project_dir.py @@ -34,6 +34,9 @@ def add_blank_sample(self): def delete_sample(self, sample): self.samples.remove(sample) + def num_samples(self): + return len(self.samples) + @property def has_samples(self): return len(self.samples) != 0 diff --git a/app/project/sample.py b/app/project/sample.py index 97c2875..4917707 100644 --- a/app/project/sample.py +++ b/app/project/sample.py @@ -12,7 +12,7 @@ def __init__(self, area=None, length=None, titles=None, plotrange=None): self._data_path = None # Path to ASC file self.area = area # cross-sectional area self.length = length # Pull length - self.num = None # Sample Number + self.name = None # Sample Number self.precision = 0 self.titles = ( titles if titles is not None else [None, None, None] @@ -69,7 +69,7 @@ def is_complete(self): self._data_path is not None and self.area is not None and self.length is not None - and self.num is not None + and self.name is not None and self.titles != [None, None, None] and self._elastic_interval != [None, None] and self.plotrange != [None, None] @@ -111,7 +111,7 @@ def peak_load(self): @property def json(self): data = { - "number": self.num, + "name": self.name, "area": self.area, "length": self.length, "cutoff_pct": self._cutoff_pct, @@ -134,7 +134,11 @@ def from_json(data): ret._elastic_interval = data["elastic_zone"] ret._zero_ind = data["zero_ind"] - ret.num = data["number"] + if "name" in data: + ret.name = data["name"] + # Backwards-compatibility: Support projects that used sample # + elif "number" in data: + ret.name = f"Sample {data['number']}" ret.area = data["area"] ret.length = data["length"] diff --git a/app/util/pdf.py b/app/util/pdf.py index babb935..059fd92 100644 --- a/app/util/pdf.py +++ b/app/util/pdf.py @@ -46,12 +46,11 @@ def generate_project_layer(proj_num, proj_date, pdf_dir): def generate_sample_layer(sample, filename): """Layer 2: SAMPLE (sample #, titles)""" - sample_num_str = "Sample #{}".format(sample.num) pagewidth, pageheight = landscape(letter) c = canvas.Canvas(filename, pagesize=(pagewidth, pageheight)) c.setFont("Helvetica-Bold", 16) c.drawRightString( - pagewidth - TEXT_MARGIN, pageheight - 35 - TEXT_MARGIN, sample_num_str + pagewidth - TEXT_MARGIN, pageheight - 35 - TEXT_MARGIN, sample.name ) titles = sample.titles diff --git a/poetry.lock b/poetry.lock index b447927..5f52528 100644 --- a/poetry.lock +++ b/poetry.lock @@ -187,6 +187,24 @@ ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=14.0.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +[[package]] +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + [[package]] name = "kiwisolver" version = "1.4.4" @@ -727,4 +745,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "6b38a173b9909c4e3c388c7e52d4cc2b605006fab7c045a74eb4cefb6c06a14a" +content-hash = "d5c5c7d2fedc084301a2ef70ad075149c36ebfe134a10c466215d2130ee259f3" diff --git a/run.py b/run.py index 99fe3d9..6a73e5c 100644 --- a/run.py +++ b/run.py @@ -1,4 +1,3 @@ - from app.gui.app_window import AppWindow import sys @@ -13,4 +12,4 @@ else: p = None root = AppWindow(p) - root.run() \ No newline at end of file + root.run()