Commit 3dac2d70 authored by Brian King's avatar Brian King
Browse files

Adding a bunch of files to ensure entire project can be built so far.

parent 09e062b4
"""
menus - Contains all of the menu actions
"""
# File menu items
"""
Model
"""
import code.model.setup
import code.model.run
import code.model.dma1
import code.model.scan
class Model:
def __init__(self):
self.setup = code.model.setup.Setup()
self.run_of_scans = code.model.run.RunOfScans()
self.dma1 = code.model.dma1.DMA_1(debug=False)
self.current_scan: code.model.scan.Scan = None
self.current_scan_num: int = None
def process_new_file(self, filename):
self.setup.read_file(filename)
self.run_of_scans.read_file(filename)
self.dma1.update_from_setup_and_run(self.setup, self.run_of_scans)
self.current_scan_num = 0
self._update_selected_scan_in_model()
def select_scan_num(self, scan_num: int) -> bool:
"""
Select a specified scan number
:return: True if the scan could be selected, False if it was out of range
"""
if scan_num >= 0 and scan_num < self.run_of_scans.get_num_scans():
self.current_scan_num = scan_num
self._update_selected_scan_in_model()
return True
else:
return False
def select_next_scan_num(self) -> bool:
"""
Select the next scan from the collection of scans contained in the model.
:return: True if the next scan was selected successfully, False if there were no
more scans that could be selected
"""
if self.current_scan_num + 1 == self.run_of_scans.get_num_scans():
return False
else:
self.current_scan_num += 1
self._update_selected_scan_in_model()
return True
def select_prev_sample_num(self) -> bool:
"""
Select the previous scan from the collection of scans contained in the model.
:return True if the previous scan was selected successfully, False if the current
scan is already the first one
"""
if self.current_scan_num == 0:
return False
else:
self.current_scan_num -= 1
self._update_selected_scan_in_model()
return True
def _update_selected_scan_in_model(self):
"""
Retrieve a scan from all of the scans based on the internal value of
self.current_scan_num. This is not to be called outside of this class.
"""
self.current_scan = self.run_of_scans.get_scan(self.current_scan_num)
import sys
from PySide2.QtWidgets import (QApplication, QMainWindow, QTextEdit, QDockWidget, QListWidget)
from PySide2.QtCore import Qt
class MainWindow(QMainWindow):
# def __init__(self, parent, *args, **kwargs):
def __init__(self):
super().__init__()
self.resize(800, 600)
dockWidget = QDockWidget('Dock', self)
self.textEdit = QTextEdit()
self.textEdit.setFontPointSize(16)
self.listWidget = QListWidget()
self.listWidget.addItem('Google')
self.listWidget.addItem('Facebook')
self.listWidget.addItem('Microsoft')
self.listWidget.addItem('Apple')
self.listWidget.itemDoubleClicked.connect(self.get_list_item)
dockWidget.setWidget(self.listWidget)
dockWidget.setFloating(False)
self.setCentralWidget(self.textEdit)
self.addDockWidget(Qt.RightDockWidgetArea, dockWidget)
def get_list_item(self):
self.textEdit.setPlainText(self.listWidget.currentItem().text())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
\ No newline at end of file
import sys
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from code.model.model import Model
mpl.use('Qt5Agg')
class DMA_1_Graph_Widget(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = plt.Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
super(DMA_1_Graph_Widget, self).__init__(self.fig)
self.model = None
def set_model(self, model: Model):
self.model = model
self.update_plot()
def update_plot(self):
# Remove the previous axes
self.fig.delaxes(self.axes)
# Create a new axes
self.axes = self.fig.add_subplot(111)
# Update it from the model
self.model.dma1.plot(self.axes)
# TODO - Not sure which if any of these draw methods are needed
# self.draw_idle()
self.fig.canvas.draw_idle()
from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, QFrame
class TitleHLine(QWidget):
"""
Creates a widget that can be used to create a title that is centered with a horizontal line on
both sides of it.
:param str title: The title to appear centered between the horizontal lines
"""
def __init__(self, title):
super(self.__class__, self).__init__()
h_layout = QHBoxLayout()
h_layout.addWidget(HLineRaised())
h_layout.addWidget(QLabel(title))
h_layout.addWidget(HLineRaised())
self.setLayout(h_layout)
class VLineSunk(QFrame):
"""
Creates a vertical line QFrame.
"""
def __init__(self):
super(VLineSunk, self).__init__()
self.setFrameShape(QFrame.VLine)
self.setFrameShadow(QFrame.Sunken)
class HLineRaised(QFrame):
"""
Creates a widget that can be used to create a horizontal line
"""
def __init__(self):
super(HLineRaised, self).__init__()
self.setFrameShape(QFrame.HLine)
self.setFrameShadow(QFrame.Raised)
\ No newline at end of file
from PySide2.QtCore import Qt
from PySide2.QtWidgets import (
QLabel,
QMainWindow,
QWidget,
QStatusBar,
QDockWidget,
QFormLayout,
QLineEdit, QTabWidget, QAction, QSlider
)
import PySide2.QtWidgets as Qw
#from layout_colorwidget import Color
import code.model.model as model_pkg
from code.model.scan import MAX_PEAKS_TO_FIT
from code.view.helper_widgets import TitleHLine
from code.view.dma_1_graph import DMA_1_Graph_Widget
from code.view.scan_data_graph import Scan_Data_Graph_Widget
class MainWindow(QMainWindow):
def __init__(self, model: model_pkg.Model):
super().__init__()
self.model = model
self.setWindowTitle("HTDMA ")
self.resize(1200,800)
# Set up the statusbar
self.setStatusBar(QStatusBar(self))
self.statusBar().showMessage("Ready")
# Set up the menu for the application
self.create_menu()
# Create ALL of the fields that will be managed by this view and connected
# to the model
self.create_view_fields()
# Now, create all of the extraneous controls. These are things like sliders and stuff
# that are are connected to one of the primary fields
self.create_view_controls()
# Create all of the graph widgets
self.create_view_graphs()
# Set up the dock widget. Then, create the tabs that will be placed in the dock
dockWidget = QDockWidget("Setup Info", self)
self.tabs = self.create_tab_widget_for_docker()
dockWidget.setWidget(self.tabs)
dockWidget.setFloating(False)
self.addDockWidget(Qt.LeftDockWidgetArea,dockWidget)
# Finally, set up the main view itself
self.setCentralWidget(self.create_center_widget())
def create_view_fields(self):
"""
This is a pretty important function. It creates the primary widgets that will be connected
to the model.
"""
self.run_name_lineedit = QLineEdit()
self.run_name_lineedit.setText("HTDMA test")
self.run_name_lineedit.setEnabled(False)
self.q_sh_lineedit = QLineEdit()
self.q_sh_lineedit.setText("0.0")
self.q_aIn_lineedit = QLineEdit()
self.q_aIn_lineedit.setText("0.0")
self.q_aOut_lineedit = QLineEdit()
self.q_aOut_lineedit.setText("0.0")
self.q_excess_lineedit = QLineEdit()
self.q_excess_lineedit.setText("0.0")
self.voltage_lineedit = QLineEdit()
self.voltage_lineedit.setText("0")
self.dp_center_label = QLabel("0")
self.dp_range_label = QLabel("[ 0 - 0 ]")
self.dp_spread_label = QLabel("0")
self.scan_num_lineedit = QLineEdit()
self.scan_num_lineedit.setText("0")
self.scan_timestamp_label = QLabel("00:00:00")
self.scan_up_time_label = QLabel()
self.scan_down_time_label = QLabel()
self.low_V_label = QLabel()
self.high_V_label = QLabel()
self.scan_fit_num_peaks_spinbox = Qw.QSpinBox()
self.scan_fit_num_peaks_spinbox.setRange(1,MAX_PEAKS_TO_FIT)
def create_view_controls(self):
"""
This sets up controls used by the user to navigate settings
and work through scans
"""
self.voltage_slider = QSlider(Qt.Horizontal)
self.voltage_slider.setMinimum(0)
self.voltage_slider.setMaximum(10000)
self.voltage_slider.setValue(0)
self.voltage_slider.setTickPosition(QSlider.TicksBelow)
self.voltage_slider.setTickInterval(2500)
# Create the buttons to step through scans
self.next_scan_button = Qw.QPushButton("Next")
self.prev_scan_button = Qw.QPushButton("Prev")
# Curve fitting button
self.peak_fit_button = Qw.QPushButton("Fit Peaks")
def create_view_graphs(self):
self.dma_1_graph_widget = DMA_1_Graph_Widget()
self.dma_1_graph_widget.set_model(self.model)
self.scan_data_graph_widget = Scan_Data_Graph_Widget()
self.scan_data_graph_widget.set_model(self.model)
def create_tab_widget_for_docker(self):
# Initialize tab screen
tabs = QTabWidget()
tabs.setTabPosition(QTabWidget.West)
tab1 = QWidget()
tab2 = QWidget()
# self.tabs.resize(300, 200)
# Add tabs
tabs.addTab(tab1, "DMA 1")
tabs.addTab(tab2, "Scan")
# Create first tab
tab1.setLayout(self.create_dma_1_dock_tab_layout())
tab2.setLayout(self.create_scan_dock_tab_layout())
return tabs
def create_dma_1_dock_tab_layout(self) -> QFormLayout:
"""
Create the layout that will show dma 1
"""
# We'll use a form layout container for the docker
form = QFormLayout(self)
# Start adding info
form.addRow("Name", self.run_name_lineedit)
form.addRow(TitleHLine("Flow settings"))
form.addRow("Sheath Flow", self.q_sh_lineedit)
form.addRow("Aerosol In", self.q_aIn_lineedit)
form.addRow("Aerosol Out", self.q_aOut_lineedit)
form.addRow("Excess Out", self.q_excess_lineedit)
form.addRow(QLabel(""))
form.addRow(TitleHLine("Voltage"))
form.addRow("Voltage", self.voltage_lineedit)
form.addRow(self.voltage_slider)
form.addRow(QLabel(""))
form.addRow(TitleHLine("Theoretical dP Distribution"))
form.addRow("dP (nm)", self.dp_center_label)
form.addRow("dP range", self.dp_range_label)
form.addRow("dp spread", self.dp_spread_label)
return form
def create_scan_dock_tab_layout(self) -> QFormLayout:
# We'll use a form layout container for the docker
form = QFormLayout(self)
# Start adding info
form.addRow("Name", self.run_name_lineedit)
form.addRow(QLabel(""))
form.addRow(TitleHLine("Run Information"))
form.addRow(QLabel(""))
form.addRow(TitleHLine("Scan Details"))
form.addRow("Scan #", self.scan_num_lineedit)
form.addRow("Time", self.scan_timestamp_label)
form.addRow("Low Voltage",self.low_V_label)
form.addRow("High Voltage", self.high_V_label)
form.addRow("Scan Up Time", self.scan_up_time_label)
form.addRow("Scan Down Time", self.scan_down_time_label)
form.addRow(QLabel(""))
hbox = Qw.QHBoxLayout()
hbox.addWidget(self.prev_scan_button)
hbox.addWidget(self.next_scan_button)
form.addRow(hbox)
form.addRow(QLabel(""))
form.addRow(TitleHLine("Peak Fitting"))
form.addRow("Number of peaks to fit", self.scan_fit_num_peaks_spinbox)
form.addRow(self.peak_fit_button)
return form
def create_center_widget(self):
splitter1 = Qw.QSplitter(Qt.Vertical)
splitter1.addWidget(self.dma_1_graph_widget)
splitter1.addWidget(self.scan_data_graph_widget)
layout = Qw.QVBoxLayout()
layout.addWidget(splitter1)
# THe Qt way - a layout always needs a dummy widget
frame = Qw.QFrame()
frame.setLayout(layout)
return frame
def create_menu(self):
self.menu = self.menuBar()
self.file_menu = self.menu.addMenu("&File")
self.file_open_action = QAction("&Open...",self)
self.file_menu.addAction(self.file_open_action)
self.help_menu = self.menu.addMenu("&Help")
self.help_menu_action = QAction("Help",self)
self.help_menu.addAction(self.help_menu_action)
def update_dma1_widget_views_from_model(self):
"""
Update all widgets related to dma1 based on whatever values the model contains
"""
super().update()
self.q_sh_lineedit.setText("{:.1f}".format(self.model.dma1.q_sh_lpm))
self.q_aIn_lineedit.setText("{:.1f}".format(self.model.dma1.q_aIn_lpm))
self.q_aOut_lineedit.setText("{:.1f}".format(self.model.dma1.q_aOut_lpm))
self.q_excess_lineedit.setText("{:1f}".format(self.model.dma1.q_excess_lpm))
self.voltage_lineedit.setText("{:.0f}".format(self.model.dma1.voltage))
self.voltage_slider.setValue(self.model.dma1.voltage)
self.dp_center_label.setText("{:.1f}".format(self.model.dma1.dp_center))
self.dp_range_label.setText("[ {:.1f} - {:.1f} ]".format(self.model.dma1.dp_left_bottom,self.model.dma1.dp_right_bottom))
self.dp_spread_label.setText("{:.1f}".format(self.model.dma1.dp_right_bottom - self.model.dma1.dp_left_bottom))
self.dma_1_graph_widget.update_plot()
def update_scan_widget_views_from_model(self):
"""
Update all widgets related to a single scan based on whatever values the model contains
"""
print("update_scan_widget_views: " + repr(self.model.current_scan))
if self.model.current_scan is not None:
self.scan_num_lineedit.setText(str(self.model.current_scan.scan_id_from_data))
self.scan_timestamp_label.setText(self.model.current_scan.time_stamp.strftime("%H:%M:%S"))
self.low_V_label.setText("{:.0f}".format(self.model.current_scan.low_V))
self.high_V_label.setText("{:.0f}".format(self.model.current_scan.high_V))
self.scan_up_time_label.setText("{:.0f}".format(self.model.current_scan.scan_up_time))
self.scan_down_time_label.setText("{:.0f}".format(self.model.current_scan.scan_down_time))
else:
self.scan_num_lineedit.setText("")
self.scan_timestamp_label.setText("00:00:00")
self.low_V_label.setText("0")
self.high_V_label.setText("0")
self.scan_up_time_label.setText("")
self.scan_down_time_label.setText("")
self.scan_data_graph_widget.update_plot()
"""
This represents the widget that will encapsulate the actual scan data and
associated plot
"""
import sys
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from code.model.model import Model
mpl.use('Qt5Agg')
class Scan_Data_Graph_Widget(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = plt.Figure(figsize=(width, height), dpi=dpi)
self.gridspec = self.fig.add_gridspec(4,1)
super(Scan_Data_Graph_Widget, self).__init__(self.fig)
self.ax_data = None
self.ax_residuals = None
self.model = None
def set_model(self, model: Model):
self.model = model
self.update_plot()
def update_plot(self):
# Remove the previous axes
if self.ax_data:
self.fig.delaxes(self.ax_data)
self.ax_data = None
if self.ax_residuals:
self.fig.delaxes(self.ax_residuals)
self.ax_residuals = None
# Create a new axes by dividing up the region into 4 parts, 3 will be
# dedicated to the data, and 1 to the residuals
self.ax_data = self.fig.add_subplot(self.gridspec[0:3,0])
self.ax_residuals = self.fig.add_subplot(self.gridspec[3,0],
sharex=self.ax_data)
# Turn off the axis labels on the top
plt.setp(self.ax_data.get_xticklabels(), visible=False)
# Update it from the model
if self.model.current_scan:
self.model.current_scan.plot(self.ax_data, self.ax_residuals)
self.gridspec.tight_layout(self.fig)
# TODO - Not sure which if any of these draw methods are needed
# self.draw_idle()
self.fig.canvas.draw_idle()
"""
# REVIEW Documentation
"""
import logging
import logging.config
def configure_logger_env():
"""
# REVIEW Documentation
"""
logging.config.dictConfig({
'version': 1,
'formatters': {
'formatter': {'format': '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
'datefmt': '%m/%d/%Y %I:%M:%S %p'}
},
'handlers': {
'stream_handler': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'formatter',
'stream': 'ext://sys.stdout'
}
},
'loggers': {
'root': {
'level': 'DEBUG',
'handlers': ['stream_handler']
},
'main': {
'level': 'DEBUG',
'handlers': ['stream_handler'],
'qualname': 'main',
'propagate': 0
},
'controller': {
'level': 'DEBUG',
'handlers': ['stream_handler'],
'qualname': 'controller',
'propagate': 0
},
'scan': {
'level': 'DEBUG',
'handlers': ['stream_handler'],
'qualname': 'scan',
'propagate': 0
}
},
'disable_existing_loggers': False
})
def configure_logger_frz(log_path):
"""
# REVIEW Documentation
"""
logging.config.dictConfig({
'version': 1,
'formatters': {
'formatter': {'format': '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
'datefmt': '%m/%d/%Y %I:%M:%S %p'}
},
'handlers': {
'stream_handler': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'f