Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

threading-dev #32

Draft
wants to merge 52 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e114be7
prep for refactors
teauxfu May 11, 2021
db9e39e
prep for refactors
teauxfu May 13, 2021
3335d6d
fix typo
teauxfu May 13, 2021
43ca4ae
live plot still acting weird
teauxfu May 13, 2021
d916f6f
ok
teauxfu May 14, 2021
5999538
most of handlerview refactor done
teauxfu May 14, 2021
1cf8c70
cleaning, updating to Pathlib operations
teauxfu May 17, 2021
5b51bb7
hanging from surface
teauxfu May 18, 2021
bd1bf1e
plot cleaning
teauxfu May 18, 2021
595815d
fix logfile creation
teauxfu May 18, 2021
2c48c90
close but not quite
teauxfu May 18, 2021
e359746
mostly done with eval refactor
teauxfu May 19, 2021
214adc8
move scoring out of eval window
teauxfu May 24, 2021
06f28ff
cleaning
teauxfu May 24, 2021
5a44ab4
clean
teauxfu May 25, 2021
324c74c
cleaning / add export dialog
teauxfu May 25, 2021
f214284
cleaning
teauxfu May 25, 2021
98ebd3e
yaaaas bihh!!!
teauxfu May 26, 2021
570d14f
rename components
teauxfu May 26, 2021
01951a3
new plot widget
teauxfu May 26, 2021
22e1bee
oops
teauxfu May 26, 2021
cad3b99
ok
teauxfu May 26, 2021
f96613e
- update docs/changelog
teauxfu May 26, 2021
f7527dd
cleaning up path operations and annotations
teauxfu May 27, 2021
25d3672
oops
teauxfu May 27, 2021
3395f90
ok
teauxfu May 27, 2021
a2eaa7b
Merge branch 'dev' of https://github.com/teauxfu/pct-scalewiz into dev
teauxfu May 27, 2021
e4ae764
ok
teauxfu May 27, 2021
09e1881
prep for using root mainloop
teauxfu May 27, 2021
1baf6bf
cleaning
teauxfu May 28, 2021
4e481fc
.
teauxfu May 28, 2021
d08d5bf
almost!
teauxfu May 28, 2021
43f4d56
update to single-threaded rinse and readings
teauxfu May 29, 2021
bf16f08
cleaning
teauxfu Jun 1, 2021
4b9f44d
cleaning / update screenshots
teauxfu Jun 1, 2021
58066bc
back to multithreading
teauxfu Jun 1, 2021
e77e8f7
cleaning / update screenshots
teauxfu Jun 1, 2021
e3c1ddc
typo
teauxfu Jun 1, 2021
8002300
somewhat more elegant multithreading / update docs
teauxfu Jun 2, 2021
0d76e0a
docs
teauxfu Jun 2, 2021
6e569d6
docs
teauxfu Jun 2, 2021
98141a0
Update README.rst
teauxfu Jun 2, 2021
1dc4cea
cleaning / add sample
teauxfu Jun 2, 2021
6c4309f
get pressures asynchronously
teauxfu Jun 2, 2021
c299dda
Merge branch 'dev' of https://github.com/teauxfu/scalewiz into dev
teauxfu Jun 2, 2021
aa3cb53
try after again
teauxfu Jun 2, 2021
74a8340
cleaning
teauxfu Jun 2, 2021
15bbbaa
.
teauxfu Jun 2, 2021
ce4df98
tighter log polling
teauxfu Jun 2, 2021
612b81e
pullup (#29)
teauxfu Jul 20, 2021
96b80d0
Threading dev2 (#30)
teauxfu Jul 20, 2021
1873eda
Merge branch 'main' into threading-dev
teauxfu Jul 20, 2021
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
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Hotfix
[v0.5.7]
--------

Added
~~~~~

- the :code:`Project` data model now records calcium concentration

Changed
~~~~~~~

Expand Down
10 changes: 9 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Or, if you use :code:`pipx` (`try it!`_ 😉) ::

pipx install scalewiz

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,7 +80,7 @@ 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
Expand Down
44 changes: 44 additions & 0 deletions sample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import tkinter as tk
from time import time


class App(tk.Frame):
def __init__(self, parent) -> None:
super().__init__(parent)

self.count = tk.IntVar()
self.delay = tk.DoubleVar()
self.text = tk.StringVar()

for variable in (self.count, self.delay, self.text):
variable.set(None)

tk.Label(self, textvariable=self.delay).pack(padx=20, pady=20)
tk.Label(self, textvariable=self.count).pack(padx=20, pady=20)
self.cycle(1000)

def cycle(self, interval_ms: int, start=None, count=0) -> None:
print("count is", count)
if start is None:
start = time()
if count < 100:
self.count.set(count)
x = [i for i in range(1 * 10 ** 6)]
[i + 1 for i in x]
[i * 2 for i in x]
# this is approximate
self.delay.set(interval_ms - (((time() - start) * 1000) % interval_ms))
print("delay is", self.delay.get())
self.after(
round(interval_ms - (((time() - start) * 1000) % interval_ms)),
self.cycle,
interval_ms,
start,
count + 1,
)


if __name__ == "__main__":
root = tk.Tk()
App(root).pack()
root.mainloop()
8 changes: 8 additions & 0 deletions scalewiz/components/evaluation_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def build(self, reload: bool = False) -> None:
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 All @@ -111,6 +112,13 @@ def plot(self) -> None:

def save(self) -> None:
"""Saves to file the project, most recent plot, and calculations log."""
if self.handler.is_running:
messagebox.showwarning(
"Can't save to this Project right now",
"Can't save while a Test in this Project is running",
)
return

# update image
plot_output = (
f"{self.editor_project.numbers.get().replace(' ', '')} "
Expand Down
7 changes: 4 additions & 3 deletions scalewiz/components/handler_view_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@ def animate(self, interval: float) -> None:
The interval argument is used by matplotlib internally.
"""
# # we can just skip this if the test isn't running
if len(self.handler.readings) > 0:
if self.handler.readings.qsize() > 0:
if self.handler.is_running and not self.handler.is_done:
with self.handler.readings.mutex:
readings = tuple(self.handler.readings.queue)
pump1 = []
pump2 = []
elapsed = [] # we will share this series as an axis
# cast to tuple in case the list changes during iteration
readings = tuple(self.handler.readings)

for reading in readings:
pump1.append(reading.pump1)
pump2.append(reading.pump2)
Expand Down
2 changes: 2 additions & 0 deletions scalewiz/components/project_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def build(self, reload: bool = False) -> None:
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 @@ -86,6 +87,7 @@ 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
8 changes: 8 additions & 0 deletions scalewiz/components/scalewiz.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,17 @@ 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"
# )
# 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"
date_fmt = "%Y-%m-%d %H:%M:%S"
formatter = logging.Formatter(
fmt,
Expand Down
33 changes: 16 additions & 17 deletions scalewiz/components/scalewiz_menu_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import logging
import tkinter as tk
from pathlib import Path

# from time import time
from time import time
from tkinter.messagebox import showinfo
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -47,7 +46,8 @@ 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)
menubar.add_command(label="Debug", command=self._debug)

self.menubar = menubar

def spawn_editor(self) -> None:
Expand Down Expand Up @@ -99,17 +99,16 @@ def about(self) -> None:

def _debug(self) -> None:
"""Used for debugging."""
pass
# 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()
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()
1 change: 1 addition & 0 deletions scalewiz/helpers/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def export(project: Project) -> Tuple[int, Path]:
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
3 changes: 2 additions & 1 deletion scalewiz/helpers/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ 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(result)

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

if isinstance(log_widget, tk.Text):
to_log(log, log_widget)
Expand Down
59 changes: 34 additions & 25 deletions scalewiz/models/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from datetime import date
from logging import DEBUG, FileHandler, Formatter, getLogger
from pathlib import Path
from queue import Queue
from queue import Empty, Queue
from time import sleep, time
from tkinter import filedialog, messagebox
from typing import TYPE_CHECKING
Expand All @@ -34,7 +34,9 @@ def __init__(self, name: str = "Nemo") -> None:
self.logger: Logger = getLogger(f"scalewiz.{name}")
self.project: Project = Project()
self.test: Test = None
self.readings: List[Reading] = []

self.readings: Queue = Queue()

self.max_readings: int = None # max # of readings to collect
self.limit_psi: int = None
self.max_psi_1: int = None
Expand Down Expand Up @@ -63,7 +65,7 @@ def can_run(self) -> bool:
return (
(self.max_psi_1 < self.limit_psi or self.max_psi_2 < self.limit_psi)
and self.elapsed_min < self.limit_minutes
and len(self.readings) < self.max_readings
and self.readings.qsize() < self.max_readings
and not self.stop_requested
)

Expand Down Expand Up @@ -110,19 +112,15 @@ def start_test(self) -> None:
for pump in (self.pump1, self.pump2):
pump.close()
else:
self.readings.clear()

self.stop_requested = False
self.is_done = False
self.is_running = True
self.rebuild_views()
self.pool.submit(self.uptake_cycle)

def uptake_cycle(self) -> None:
"""Get ready to take readings.

Meant to be run from a worker thread.
"""
self.logger.info("Starting an uptake cycle")
"""Get ready to take readings."""
uptake = self.project.uptake_seconds.get()
step = uptake / 100 # we will sleep for 100 steps
self.pump1.run()
Expand All @@ -146,6 +144,7 @@ def take_readings(self) -> None:

def get_pressure(pump: NextGenPump) -> Union[float, int]:
self.logger.info("collecting a reading from %s", pump.serial.name)

return pump.pressure

interval = self.project.interval_seconds.get()
Expand All @@ -159,6 +158,7 @@ def get_pressure(pump: NextGenPump) -> Union[float, int]:
psi1, psi2 = psi1.result(), psi2.result()
t1 = time()
self.logger.warn("got both in %s s", t1 - t0)

average = round(((psi1 + psi2) / 2))
reading = Reading(
elapsedMin=self.elapsed_min, pump1=psi1, pump2=psi2, average=average
Expand All @@ -167,10 +167,12 @@ def get_pressure(pump: NextGenPump) -> Union[float, int]:
msg = "@ {:.2f} min; pump1: {}, pump2: {}, avg: {}".format(
self.elapsed_min, psi1, psi2, average
)
self.readings.append(reading)

self.readings.put(reading)
self.log_queue.put(msg)
self.logger.debug(msg)
prog = round((len(self.readings) / self.max_readings) * 100)
prog = round((self.readings.qsize() / self.max_readings) * 100)

self.progress.set(prog)

if psi1 > self.max_psi_1:
Expand All @@ -181,7 +183,8 @@ def get_pressure(pump: NextGenPump) -> Union[float, int]:
# TYSM https://stackoverflow.com/a/25251804
sleep(interval - ((time() - start_time) % interval))
else:
self.root.after(0, self.stop_test, {"save": True})

self.stop_test(save=True)

def request_stop(self) -> None:
"""Requests that the Test stop."""
Expand All @@ -190,7 +193,16 @@ def request_stop(self) -> None:

def stop_test(self, save: bool = False, rinsing: bool = False) -> None:
"""Stops the pumps, closes their ports."""
self.close_pumps()

for pump in (self.pump1, self.pump2):
if pump.is_open:
pump.stop()
pump.close()
self.logger.info(
"Stopped and closed the device @ %s",
pump.serial.name,
)

if not rinsing:
self.is_done = True
self.is_running = False
Expand All @@ -206,7 +218,14 @@ def save_test(self) -> None:
self.logger.info(
"Saving %s to %s", self.test.name.get(), self.project.name.get()
)
self.test.readings.extend(self.readings)
while True:
try:
reading = self.readings.get(block=False)
except Empty:
break
else:
self.test.readings.append(reading)

self.project.tests.append(self.test)
self.project.dump_json()
# refresh data / UI
Expand Down Expand Up @@ -239,17 +258,6 @@ def setup_pumps(self, issues: List[str] = None) -> None:
pump.flowrate = flowrate
self.logger.info("Set flowrates to %s", pump.flowrate)

def close_pumps(self) -> None:
"""Tears down the pumps."""
for pump in (self.pump1, self.pump2):
if pump.is_open:
pump.stop()
pump.close()
self.logger.info(
"Stopped and closed the device @ %s",
pump.serial.name,
)

def load_project(
self,
path: Union[str, Path] = None,
Expand All @@ -258,6 +266,7 @@ def load_project(
) -> None:
"""Opens a file dialog then loads the selected Project file.


`loaded` gets built from scratch every time it is passed in -- no need to update
"""
if path is None:
Expand Down
Loading