Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions ARCHITECTURE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
This is a general mapping of the code / code flow


.. image:: https://github.com/teauxfu/scalewiz/blob/main/img/architecture.png
:alt: code graph


::

models/
data models, dict-like collections of tkinter variables that can serialize themselves as JSON
├── project.py
│ organizes a collection of Tests with some metadata
├── test.py
│ organizes a collection of readings for a Test with some metadata
╰── test_handler.py
not really a 'model' nor a 'component' - collects readings over serial, sticks them in a Test in a Project

components/
custom tkinter widgets bundled with a minimum of business logic
├── scalewiz_log_window.py
│ a tkinter ScrolledText that trampolines on the mainloop to poll logging messages from a Queue
├── scalewiz_rinse_window.py
│ a small toplevel that can run the pumps for a user-defined duration
├── scalewiz.py
│ core object of the app, used for setting up logging and ttk styles
│ ╰── scalewiz_main_frame.py
│ the main frame of the application, holds a notebook widget
│ ├── handler_view.py
│ │ represents a tab within the main frame's notebook
│ │ ├── handler_view_devices_entry.py
│ │ │ widget for comboboxes, can poll for COM/serial port devices
│ │ ├── handler_view_info_entry.py
│ │ │ widget for user entry of Test metadata
│ │ ├── handler_view_controls.py
│ │ │ widget that holds the progess bar, readings log, and start/stop buttons
│ │ ╰── handler_view_plot.py
│ │ widget that displays an animated matplotlib plot of the data collected for a running Test
│ ╰── scalewiz_menu_bar.py
│ defines the menu bar that gets loaded on to the main menu
├── project_editor.py
│ toplevel for making/mutating Projects
│ ├── project_editor_info.py
│ │ form for metadata
│ ├── project_editor_params.py
│ │ form for experiment parameters -- affects how Tests are run and scored
│ ╰── project_editor_report.py
│ form for setting exported report preferences
╰── evaluation_window.py
toplevel for displaying a Project summary with a notebook widget
├── evaluation_data_view.py
│ frame that displays a table-like view of data in a Project, giving each Test a row
╰── evaluation_plot_view.py
frame that uses matplotlib to plot a selection of data

helpers/
helper functions that didn't fit elsewhere
├── configuration.py
│ handles read/writing a config TOML file
├── score.py
│ modifies a Project by calculating and assigning a score for each Test, optionally sending a log to a text widget
├── export.py
│ handles exporting a summary of a Project to an output (JSON, CSV, etc.)
├── show_help.py
│ opens a link to the documentation in a browser window
├── sort_nicely.py
│ does some pleasant sorting -- used when sorting Tests within a Project
├── validation.py
│ some functions used for validation in entry widgets
├── set_icon.py
│ sets the icon of a toplevel widget
╰── get_resource.py
fetches a file

main thread -- tkinter mainloop, performs UI updates
can spawn an arbitrary number of TestHandlers/RinseWindows, each with child threads as follows
├── TestHandler's data collection thread -- alive only while a Test is running
│ collects readings on a blocking loop
│ ╰── 2 data collection threads
│ one for each pump -- performs a quick (~30ms) I/O and returns
├── RinseWindow's thread
│ the rinse window can spawn a thread IFF the TestHandler isn't running a Test
╰── ...




14 changes: 14 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ adheres to `Semantic
Versioning <https://semver.org/spec/v2.0.0.html>`_.


[v0.5.8]
--------

Hotfix
======

- plot figure saving fixed

[v0.5.7]
--------

Expand All @@ -35,22 +43,28 @@ Performance

- updated the :code:`TestHandler` to poll for readings asynchronously
- updated the :code:`TestHandler` to be more robust when generating log files
- minor performance buff to log processing
- minor performance buff to the :code:`LivePlot` component
- minor performance buff to :code:`Project` serialization
- minor performance buff to reading user configuration file


Data handling
=============

- the :code:`Project` data model now records calcium concentration
- updated the :code:`Test` object model to handle the :code:`Reading` class
- updated the :code:`Project` object model to be more backwards compatible
- refactored data analysis out of the :code:`EvaluationWindow` and into its own :code:`score` function
- calculations log is a bit more verbose now
- updated :code:`score` function to handle the :code:`Reading` class

Misc
====

- update all :code:`os.path` operations to fancy :code:`pathlib.Path` operations
- update all :code:`matplotlib` code to use the object oriented API
- fixed some lag that would accumulate when displaying log messages in the main menu
- lots of misc. code cleanup / reorganizing


Expand Down
16 changes: 12 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
========================================================================
scalewiz |license| |python| |pypi| |build-status| |style| |code quality|
========================================================================
===========================================================================================
scalewiz |license| |python| |pypi| |build-status| |style| |code quality| |maintainability|
===========================================================================================

A graphical user interface designed to work with `Teledyne SSI MX-class
HPLC pumps`_ for the purpose of calcite scale inhibitor chemical
Expand All @@ -26,6 +26,10 @@ Or, if you use :code:`pipx` (`try it!`_ 😉) ::

pipx install scalewiz

Or, if you use :code:`pipx` (`try it!`_ 😉) ::

pipx install scalewiz

Usage
=====

Expand Down Expand Up @@ -72,6 +76,11 @@ Acknowledgements
.. |code quality| image:: https://img.shields.io/badge/code%20quality-flake8-black
:target: https://gitlab.com/pycqa/flake8
:alt: Code quality

.. |maintainability| image:: https://api.codeclimate.com/v1/badges/9f4d424afac626a8b2e3/maintainability
:target: https://codeclimate.com/github/teauxfu/scalewiz/maintainability
:alt: Maintainability


.. _`Premier Chemical Technologies, LLC`: https://premierchemical.tech
.. _`@balacla`: https://github.com/balacla
Expand All @@ -80,4 +89,3 @@ Acknowledgements
.. _`docs`: https://github.com/teauxfu/scalewiz/blob/main/doc/index.rst#scalewiz-user-guide
.. _`issue`: https://github.com/teauxfu/scalewiz/issues
.. _`try it!`: https://pypa.github.io/pipx/

Binary file added img/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[tool.poetry]
name = "scalewiz"
version = "0.5.7"
version = "0.5.8"
description = "A graphical user interface for chemical performance testing designed to work with Teledyne SSI MX-class HPLC pumps."
readme = "README.rst"
license = "GPL-3.0-or-later"
license = "GPL-3.0"
authors = ["Alex Whittington <alex@southsun.tech>"]
packages = [
{include = "scalewiz"}
Expand Down
11 changes: 9 additions & 2 deletions scalewiz/components/evaluation_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@ def build(self, reload: bool = False) -> None:
# finished adding to tab control

button_frame = ttk.Frame(self)
save_btn = ttk.Button(button_frame, text="Save", command=self.save, width=10)
if self.handler.is_running:
state = "disabled"
else:
state = "normal"
save_btn = ttk.Button(
button_frame, text="Save", command=self.save, width=10, state=state
)

save_btn.grid(row=0, column=0, padx=5)
export_btn = ttk.Button(
button_frame,
Expand Down Expand Up @@ -120,7 +127,7 @@ def save(self) -> None:
)
parent_dir = Path(self.editor_project.path.get()).parent
plot_output = Path(parent_dir, plot_output).resolve()
self.plot_view.fig.savefig(plot_output)
self.plot_view.fig.savefig(str(plot_output))
self.editor_project.plot.set(str(plot_output))
# update log
log_output = (
Expand Down
27 changes: 18 additions & 9 deletions scalewiz/components/project_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,22 @@ def build(self, reload: bool = False) -> None:
)

button_frame = ttk.Frame(self)
ttk.Button(button_frame, text="Save", width=7, command=self.save).grid(
row=0, column=0, padx=5
)
ttk.Button(button_frame, text="Save as", width=7, command=self.save_as).grid(
row=0, column=1, padx=10
)
ttk.Button(button_frame, text="New", width=7, command=self.new).grid(
row=0, column=2, padx=5
)

if self.handler.is_running:
state = "disabled"
else:
state = "normal"

ttk.Button(
button_frame, text="Save", width=7, command=self.save, state=state
).grid(row=0, column=0, padx=5)
ttk.Button(
button_frame, text="Save as", width=7, command=self.save_as, state=state
).grid(row=0, column=1, padx=10)
ttk.Button(
button_frame, text="New", width=7, command=self.new, state=state
).grid(row=0, column=2, padx=5)

ttk.Button(
button_frame, text="Edit defaults", width=10, command=self.edit
).grid(row=0, column=3, padx=5)
Expand All @@ -79,6 +86,8 @@ def new(self) -> None:

def save(self) -> None:
"""Save the current Project to file as JSON."""
# todo don't allow saving if saving to current project - otherwise fine

if not self.handler.is_running:
if self.editor_project.path.get() == "":
self.save_as()
Expand Down
5 changes: 4 additions & 1 deletion scalewiz/components/scalewiz.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ def __init__(self, parent) -> None:
# configure logging functionality
self.log_queue = Queue()
queue_handler = QueueHandler(self.log_queue)

# this is for inspecting the multithreading
fmt = "%(asctime)s - %(thread)d - %(levelname)s - %(name)s - %(message)s"
# fmt = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"
# fmt = (
# "%(asctime)s - %(func)s - %(thread)d "
# "- %(levelname)s - %(name)s - %(message)s"
# )
fmt = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"

date_fmt = "%Y-%m-%d %H:%M:%S"
formatter = logging.Formatter(
fmt,
Expand Down
2 changes: 1 addition & 1 deletion scalewiz/components/scalewiz_log_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class LogWindow(tk.Toplevel):
"""A Toplevel with a ScrolledText. Displays messages from a Logger."""

def __init__(self, core: ScaleWiz) -> None:
tk.Toplevel.__init__(self)
super().__init__()
self.log_queue = core.log_queue
self.title("Log Window")
# replace the window closing behavior with withdrawing instead 🐱‍👤
Expand Down
13 changes: 13 additions & 0 deletions scalewiz/components/scalewiz_menu_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import logging
import tkinter as tk
from pathlib import Path

# from time import time

from tkinter.messagebox import showinfo
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -45,6 +48,7 @@ def __init__(self, parent: MainFrame) -> None:
menubar.add_command(label="Help", command=show_help)
menubar.add_command(label="About", command=self.about)


menubar.add_command(label="Debug", command=self._debug)
self.menubar = menubar

Expand Down Expand Up @@ -98,7 +102,16 @@ def about(self) -> None:
def _debug(self) -> None:
"""Used for debugging."""
LOGGER.warn("DEBUGGING")

current_tab = self.parent.tab_control.select()
widget: TestHandlerView = self.parent.nametowidget(current_tab)
widget.handler.setup_pumps()
t0 = time()
widget.handler.pump1.pressure
widget.handler.pump2.pressure
t1 = time()
widget.handler.close_pumps()
LOGGER.warn("collected 2 pressures in %s", t1 - t0)
widget.handler.rebuild_views()
widget.bell()

9 changes: 5 additions & 4 deletions scalewiz/helpers/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ def export(project: Project) -> Tuple[int, Path]:
"plotPath": project.plot.get(),
}
# filter the blanks and trials to sort them
blanks = {
blanks = [
test
for test in project.tests
if test.include_on_report.get() and test.is_blank.get()
}
trials = {
]
trials = [
test
for test in project.tests
if test.include_on_report.get() and not test.is_blank.get()
}
]

tests = blanks + trials
# we use lists here instead of sets since sets aren't JSON serializable
output_dict["name"] = [test.name.get() for test in tests]
Expand Down
1 change: 1 addition & 0 deletions scalewiz/helpers/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def score(project: Project, log_widget: ScrolledText = None, *args) -> None:
f"Result: 1 - ({int_psi} - {baseline_area}) / {avg_protectable_area}"
)
log.append(f"Result: {result} \n")

trial.result.set(f"{result:.2f}")

if isinstance(log_widget, tk.Text):
Expand Down
Loading