def __init__(self, config=None): QtWidgets.QMainWindow.__init__(self) self.rti_config = RtiConfig() self.rti_config.init_terminal_config() self.rti_config.init_plot_server_config() # Initialize the pages self.mgr = RiverManager.RiverManager(self, self.rti_config) # Initialize the window self.main_window_init()
class MainWindow(QtWidgets.QMainWindow): """ Main window for the application """ def __init__(self, config=None): QtWidgets.QMainWindow.__init__(self) self.rti_config = RtiConfig() self.rti_config.init_terminal_config() self.rti_config.init_plot_server_config() # Initialize the pages self.mgr = RiverManager.RiverManager(self, self.rti_config) # Initialize the window self.main_window_init() def main_window_init(self): # Set the title of the window self.setWindowTitle("Rowe Technologies, Inc. - RiveR") self.setWindowIcon(QtGui.QIcon(":rti.ico")) # Initialize Terminal self.Terminal = TerminalVM(self, self.rti_config) docked_terminal = QtWidgets.QDockWidget("Terminal", self) docked_terminal.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) docked_terminal.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable) docked_terminal.setWidget(self.Terminal) self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, docked_terminal) # Show the main window self.show() def closeEvent(self, event): """ Generate 'question' dialog on clicking 'X' button in title bar. Reimplement the closeEvent() event handler to include a 'Question' dialog with options on how to proceed - Close, Cancel buttons """ reply = QtWidgets.QMessageBox.question( self, "Message", "Are you sure you want to quit?", QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel) if reply == QtWidgets.QMessageBox.Close: event.accept() else: event.ignore()
class VectorManager: def __init__(self, parent): # Set the parent self.parent = parent # Setup the Average VM #self.avg_vm = average_vm.AverageVM(self.parent) self.rti_config = RtiConfig() self.rti_config.init_average_waves_config() self.rti_config.init_terminal_config() self.rti_config.init_waves_config() self.rti_config.init_plot_server_config() self.plot_manager = RtiBokehPlotManager(self.rti_config) self.plot_manager.start() self.bokeh_server = RtiBokehServer(self.rti_config, self.plot_manager) menu = menu_view.MenuView() menu_panel = Panel(child=menu.get_layout(), title="menu") main_lo = Tabs(tabs=[menu_panel]) #show(main_lo) #Dashboard #self.dashboard = Dashboard.Dashboard() #self.dashboard.start() #self.dashboard.app_setup() def set_mag_df(self, df): self.dashboard.set_vel_df(df)
def __init__(self, rti_config: RtiConfig): """ Initialize the list of projects. It will look in the directory listed in the config.ini file. :param rti_config: RTI Config config.ini file. """ # Set Config file self.config = rti_config rti_config.init_river_project_config() # List of all the projects available self.projects = {} # Search for all available projects self.search_for_projects()
def __init__(self): # RTI Config file self.rti_config = RtiConfig() self.rti_config.init_terminal_config() # Terminal Options self.rti_config.init_timeseries_plot_config() # Time Series Options self.adcp_codec = AdcpCodec() self.adcp_codec.ensemble_event += self.handle_ensemble_data self.tabular_vm = TabularDataVM() self.amp_vm = AmplitudeVM() self.contour_vm = ContourVM() self.shiptrack_vm = ShipTrackVM() self.timeseries_vm = TimeSeriesVM(rti_config=self.rti_config) self.adcp_terminal = AdcpTerminalVM(self.rti_config) self.adcp_terminal.on_serial_data += self.handle_adcp_serial_data # ZeroRPC Manager and Thread self.zero_rpc = ZeroRpcManager(rti_config=self.rti_config, data_mgr=self, tabular_vm=self.tabular_vm, amp_vm=self.amp_vm, contour_vm=self.contour_vm, shiptrack_vm=self.shiptrack_vm, timeseries_vm=self.timeseries_vm, adcp_terminal=self.adcp_terminal) self.zero_rpc_thread = Thread(name="ZeroRPC", target=self.zero_rpc.run_server, args=(4241, )) # Ensemble processing thread self.ens_thread_alive = True self.ens_queue = deque(maxlen=1000) self.ens_thread_event = Event() self.ens_thread = Thread(name="DataManager", target=self.ens_thread_run) # Used to remove vessel speed self.prev_bt_east = Ensemble.BadVelocity self.prev_bt_north = Ensemble.BadVelocity self.prev_bt_vert = Ensemble.BadVelocity
def __init__(self, parent): # Set the parent self.parent = parent # Setup the Average VM #self.avg_vm = average_vm.AverageVM(self.parent) self.rti_config = RtiConfig() self.rti_config.init_average_waves_config() self.rti_config.init_terminal_config() self.rti_config.init_waves_config() self.rti_config.init_plot_server_config() self.plot_manager = RtiBokehPlotManager(self.rti_config) self.plot_manager.start() self.bokeh_server = RtiBokehServer(self.rti_config, self.plot_manager) menu = menu_view.MenuView() menu_panel = Panel(child=menu.get_layout(), title="menu") main_lo = Tabs(tabs=[menu_panel])
def __init__(self, socketio): self.plot = self.create_plot() self.socketio = socketio # RTI Configuration self.rti_config = RtiConfig(file_path="config.ini") self.rti_config.init_river_project_config() # Plot manager to keep track of plots self.plot_mgr = PlotManager(app_mgr=self, socketio=socketio) # River Project Manager to keep track of River projects self.river_prj_mrg = RiverProjectManager(self.rti_config) # ADCP Codec to decode the ADCP data self.adcp_codec = AdcpCodec() self.adcp_codec.ensemble_event += self.process_ensemble # Serial Port self.serial_port = None self.serial_thread = None self.serial_thread_alive = False self.app_state = { "is_serial_connected": False, # Is the serial port connected "serial_status": [], # Status of the serial connection "selected_serial_port": "", # Comm port selected "selected_baud": "115200", # Baud rate selected "is_serial_error": False, # Any serial errors. "serial_error_status": [], # List of error messages "baud_list": self.get_baud_rates(), # List of all available Baud rates "serial_port_list": self.get_serial_ports(), # List of all available Serial Ports "serial_raw_ascii": "", # Raw ascii from the serial port "max_ascii_buff": 10000, # Maximum number of characters to keep in serial ASCII buffer "adcp_break": {}, # Results of a BREAK statement "adcp_ens_num": 0, # Latest Ensemble number "selected_files": [], # Selected files to playback, "selected_project_db": None, # Selected Project, "prj_name": "", # Current project name "prj_path": "", # Current project folder path } self.transect_state = { "transect_index": 1, # Current index for the transect "transect_dt_start": None, # Start datetime of the transect "transect_duration": None, # Time duration of the transect "voltage": 0.0, # System Voltage } # Current Transect self.db_file = None # DB file to store the ensembles and transects self.raw_file = None # Raw binary file to the store the ensemble data self.is_record_raw_data = self.rti_config.config['RIVER'][ 'auto_save_raw'] # Automatically save on startup self.curr_ens_index = 0 # Index the project DB for the latest ensemble self.transect_index = 0 # Transect Index self.transect = None # Current transect #self.is_volt_plot_init = False #self.voltage_queue = deque(maxlen=100) #self.ens_dt_queue = deque(maxlen=100) # Incoming serial data self.serial_raw_bytes = None
class AppManager: def __init__(self, socketio): self.plot = self.create_plot() self.socketio = socketio # RTI Configuration self.rti_config = RtiConfig(file_path="config.ini") self.rti_config.init_river_project_config() # Plot manager to keep track of plots self.plot_mgr = PlotManager(app_mgr=self, socketio=socketio) # River Project Manager to keep track of River projects self.river_prj_mrg = RiverProjectManager(self.rti_config) # ADCP Codec to decode the ADCP data self.adcp_codec = AdcpCodec() self.adcp_codec.ensemble_event += self.process_ensemble # Serial Port self.serial_port = None self.serial_thread = None self.serial_thread_alive = False self.app_state = { "is_serial_connected": False, # Is the serial port connected "serial_status": [], # Status of the serial connection "selected_serial_port": "", # Comm port selected "selected_baud": "115200", # Baud rate selected "is_serial_error": False, # Any serial errors. "serial_error_status": [], # List of error messages "baud_list": self.get_baud_rates(), # List of all available Baud rates "serial_port_list": self.get_serial_ports(), # List of all available Serial Ports "serial_raw_ascii": "", # Raw ascii from the serial port "max_ascii_buff": 10000, # Maximum number of characters to keep in serial ASCII buffer "adcp_break": {}, # Results of a BREAK statement "adcp_ens_num": 0, # Latest Ensemble number "selected_files": [], # Selected files to playback, "selected_project_db": None, # Selected Project, "prj_name": "", # Current project name "prj_path": "", # Current project folder path } self.transect_state = { "transect_index": 1, # Current index for the transect "transect_dt_start": None, # Start datetime of the transect "transect_duration": None, # Time duration of the transect "voltage": 0.0, # System Voltage } # Current Transect self.db_file = None # DB file to store the ensembles and transects self.raw_file = None # Raw binary file to the store the ensemble data self.is_record_raw_data = self.rti_config.config['RIVER'][ 'auto_save_raw'] # Automatically save on startup self.curr_ens_index = 0 # Index the project DB for the latest ensemble self.transect_index = 0 # Transect Index self.transect = None # Current transect #self.is_volt_plot_init = False #self.voltage_queue = deque(maxlen=100) #self.ens_dt_queue = deque(maxlen=100) # Incoming serial data self.serial_raw_bytes = None def get_plot(self): return self.plot def create_plot(self): N = 40 x = np.linspace(0, 1, N) y = np.random.randn(N) df = pd.DataFrame({'x': x, 'y': y}) # creating a sample dataframe data = [ go.Bar( x=df['x'], # assign x as the dataframe column 'x' y=df['y']) ] graphJSON = json.dumps(data, cls=plotly.utils.PlotlyJSONEncoder) return graphJSON def clear_plots(self): """ Clear all the plots. """ self.plot_mgr.clear_plots() def create_river_project(self): """ Create a river project file if it does not exist. This will keep all the ensemble and transect information. Also create a raw data file to store all the incoming serial data. """ if not self.db_file: curr_datetime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") #prj_file = self.river_prj_mrg.create_project(curr_datetime) #self.app_state["prj_name"] = curr_datetime #self.app_state["prj_path"] = prj_file.file_path db_file_path = os.path.join( self.rti_config.config['RIVER']['output_dir'], curr_datetime + ".db") self.db_file = RtiSqliteProjects(db_file_path) self.db_file.create_tables() self.raw_file = RtiBinaryWriter( folder_path=self.rti_config.config['RIVER']['output_dir']) def create_transect(self): # Create a transect if not self.transect: self.transect = Transect(self.transect_next_index) # Ensure the project is created self.create_river_project(self) # Start recording a transect file if self.raw_file: self.raw_file.close() file_transect_header = "Transect_" + str(self.transect_index) self.raw_file = RtiBinaryWriter( folder_path=self.rti_config.config['RIVER']['output_dir'], header=file_transect_header) def update_site_info(self, site_info): """ Update the site information for the transect. """ # Create the transect if it is not created self.create_transect() # Set the values self.transect.site_name = site_info["site_name"] self.transect.station_number = site_info["station_number"] self.transect.location = site_info["location"] self.transect.party = site_info["party"] self.transect.boat = site_info["boat"] self.transect.measurement_num = site_info["measurement_num"] self.transect.comments = site_info["comments"] # Write the data to the transect self.db_file.update_transect(self.transect) def start_transect(self): """ Start the transect. Take all the information from the transect settings and write it to the db file. """ # Create the project db file if it does not exist self.create_river_project() # Create a transect if not self.transect: self.transect = Transect(self.transect_next_index) # Set the start datetime self.transect.start_datetime = datetime.datetime.now() # Set the first ensemble for the transect self.transect.start_ens_index = self.curr_ens_index def stop_transect(self): """ Stop the transect. Set the last ensemble in the transect. Make the discharge calculation. Record the transect information to the DB file. """ # Create the project db file if it does not exist self.create_river_project() # Set the stop datetime self.transect.stop_datetime = datetime.datetime.now() # Set the last ensemble for the transect self.transect.last_ens_index = self.curr_ens_index # Add the transect to the db file self.db_file.add_transect(self.transect) # Increment the transect index for the next transect self.transect_index = self.transect_next_index + 1 def socketio_background_thread(self): """ Background worker. This will maintain the status of the backend and the GUI. Send the status of the Datalogger download process. This will continously check the status of the process. """ count = 0 while True: # Wait time self.socketio.sleep(5) count += 1 # Send a debug status to websocket self.socketio.emit('status_report', { 'data': 'Server generated event', 'count': count }, namespace='/rti') def get_serial_ports(self): return serial_port.get_serial_ports() def get_baud_rates(self): return serial_port.get_baud_rates() def connect_serial(self, comm_port: str, baud: int): """ Connect the serial port. Start the serial port read thread. """ # Set App State self.app_state["selected_serial_port"] = comm_port self.app_state["selected_baud"] = str(baud) if not self.serial_port: try: self.serial_port = AdcpSerialPort(comm_port, baud) except ValueError as ve: logging.error("Error opening serial port. " + str(ve)) self.app_state["is_serial_connected"] = False self.app_state["is_serial_error"] = True self.app_state["serial_error_status"].append( "Error opening serial port. " + str(ve)) return self.app_state except serial.SerialException as se: logging.error("Error opening serial port. " + str(se)) self.app_state["is_serial_connected"] = False self.app_state["is_serial_error"] = True self.app_state["serial_error_status"].append( "Error opening serial port. " + str(se)) return self.app_state except Exception as e: logging.error("Error opening serial port. " + str(e)) self.app_state["is_serial_connected"] = False self.app_state["is_serial_error"] = True self.app_state["serial_error_status"].append( "Error opening serial port. " + str(e)) return self.app_state # Start the read thread self.serial_thread_alive = True self.serial_thread = threading.Thread( name="Serial Terminal Thread", target=self.serial_thread_worker) self.serial_thread.start() # Set the app state self.app_state["is_serial_connected"] = True self.app_state["is_serial_error"] = False self.app_state["serial_status"].clear() self.app_state["serial_status"].append("Connected") # Send a BREAK to get ADCP Information self.send_serial_break() # Clear the plots to get new data self.clear_plots() return self.app_state def disconnect_serial(self): """ Disconnect the serial port. """ self.serial_thread_alive = False if self.serial_port: self.serial_port.disconnect() self.serial_port = None # Set the app state self.app_state["is_serial_connected"] = False self.app_state["is_serial_error"] = False self.app_state["serial_status"].clear() self.app_state["serial_error_status"].clear() self.app_state["serial_status"].append("Disconnected") return self.app_state def send_serial_break(self): if self.serial_port: # Clear the buffer of the serial data # We can then check for the results self.app_state["serial_raw_ascii"] = "" self.serial_port.send_break() # Wait a second for result time.sleep(1.2) # Get the results of the BREAK break_results = self.app_state["serial_raw_ascii"] logging.debug(break_results) # Decode the BREAK result self.app_state["adcp_break"] = self.adcp_codec.decode_BREAK( break_results) logging.debug(self.app_state["adcp_break"]) return self.app_state["adcp_break"] def send_serial_cmd(self, cmd): if self.serial_port: self.serial_port.send_cmd(cmd=cmd) def playback_files(self): """ Display a dialog box to select the files. :return: List of all the files selected. """ self.app_state["selected_files"] = self.select_files_playback() # Plot all the plots if len(self.app_state["selected_files"]) >= 1: # Get the directory of the first file selected file_dir = os.path.dirname(self.app_state["selected_files"][0]) file_name_no_ext = pathlib.Path( self.app_state["selected_files"][0]).stem file_ext = pathlib.Path(self.app_state["selected_files"][0]).suffix # Check if it is a raw file or DB file if file_ext == '.bin' or file_ext == '.ens' or file_ext == '.rtb' or file_ext == '.BIN' or file_ext == '.ENS' or file_ext == '.RTB': # Create a project DB file project_path = os.path.join( file_dir, file_name_no_ext + RtiSqliteProjects.FILE_EXT) playback_project = RtiSqliteProjects(project_path) # Load the files to the project # Use a thread to load the data read_file_thread = threading.Thread( target=self.load_raw_files, args=[project_path], name="Read Raw File Thread") # Start the thread then wait for it to finish before moving on read_file_thread.start() read_file_thread.join() #playback_project.load_files(self.app_state['selected_files']) # Reset what the selected project is self.app_state['selected_project_db'] = project_path # Check if the file is a RDB file elif file_ext == '.rdb' or file_ext == '.RDB' or file_ext == RtiSqliteProjects.FILE_EXT: self.app_state["selected_project_db"] = self.app_state[ "selected_files"][0] # Load plot the DB data if self.app_state["selected_project_db"]: self.plot_mgr.playback_sqlite( self.app_state["selected_project_db"]) return self.app_state["selected_project_db"] def load_raw_files(self, project_path): """ Load the raw data to the project. :param project_path: Path to the RDB file. :return: """ playback_project = RtiSqliteProjects(project_path) # Load the files to the project playback_project.load_files(self.app_state['selected_files']) def select_files_playback(self): """ Display a dialog box to select the files. :return: List of all the files selected. """ # Dialog to ask for a file to select root = mtTkinter.Tk() root.overrideredirect( True) # Used to Bring window to front and focused root.geometry('0x0+0+0') # Used to Bring window to front and focused root.focus_force() # Used to Bring window to front and focused filetypes = [("RDB files", "*.rdb"), ("ENS Files", "*.ens"), ("BIN Files", "*.bin"), ('All Files', '*.*')] file_paths = filedialog.askopenfilenames( parent=root, title="Select File to Playback", filetypes=filetypes) root.withdraw() print(str(file_paths)) return file_paths def process_ensemble(self, sender, ens): """" Process the next incoming ensemble. """ heading = 0.0 pitch = 0.0 roll = 0.0 voltage = 0.0 water_temp = 0.0 transect_duration = "" ens_time = "" adcp_ens_num = 0 if ens.IsEnsembleData: #print(str(ens.EnsembleData.EnsembleNumber)) self.app_state["adcp_ens_num"] = ens.EnsembleData.EnsembleNumber adcp_ens_num = ens.EnsembleData.EnsembleNumber if not self.transect_state["transect_dt_start"]: self.transect_state[ "transect_dt_start"] = ens.EnsembleData.datetime() # Calculate the time between when transect started and now self.transect_state["transect_duration"] = ( ens.EnsembleData.datetime() - self.transect_state["transect_dt_start"]).total_seconds() #duration = (ens.EnsembleData.datetime() - self.app_state["transect_dt_start"]).total_seconds() transect_duration = str( timedelta(seconds=self.transect_state["transect_duration"])) ens_time = str(ens.EnsembleData.datetime().strftime("%H:%M:%S")) if ens.IsAncillaryData: heading = round(ens.AncillaryData.Heading, 1) pitch = round(ens.AncillaryData.Pitch, 1) roll = round(ens.AncillaryData.Roll, 1) water_temp = round(ens.AncillaryData.WaterTemp, 1) if ens.IsSystemSetup: voltage = round(ens.SystemSetup.Voltage, 1) # Pass the ASCII serial data to the websocket self.socketio.emit('adcp_ens', { 'adcp_ens_num': adcp_ens_num, 'ens_time': ens_time, 'transect_duration': transect_duration, 'voltage': voltage, 'heading': heading, 'pitch': pitch, 'roll': roll, 'water_temp': water_temp, }, namespace='/rti') # Update the plot manager self.plot_mgr.add_ens(ens) # Record the ensembles self.record_ens(ens) def serial_thread_worker(self): """ Thread worker to handle reading the serial port. :param mgr: This Object to get access to the variables. :return: """ while self.serial_thread_alive: try: if self.serial_port.raw_serial.in_waiting: try: # Read the data from the serial port self.serial_raw_bytes = self.serial_port.read( self.serial_port.raw_serial.in_waiting) # Record the data self.record_raw_data(self.serial_raw_bytes) # Convert to ASCII # Ignore any non-ASCII characters raw_serial_ascii = self.serial_raw_bytes.decode( 'ascii', 'ignore') # Replace ACK and NAK raw_serial_ascii = raw_serial_ascii.replace( "\6", "[ACK]") #raw_serial_ascii = raw_serial_ascii.replace("\15", "[NAK]") # Convert the data to ASCII self.app_state["serial_raw_ascii"] += raw_serial_ascii # Pass the ASCII serial data to the websocket self.socketio.emit( 'serial_comm', {'data': self.app_state["serial_raw_ascii"]}, namespace='/rti') # Maintain a fixed buffer size ascii_buff_size = len( self.app_state["serial_raw_ascii"]) if ascii_buff_size > 0: if ascii_buff_size > self.app_state[ "max_ascii_buff"]: # Remove the n number of characters to keep it within the buffer size remove_buff_size = ascii_buff_size - self.app_state[ "max_ascii_buff"] self.app_state[ "serial_raw_ascii"] = self.app_state[ "serial_raw_ascii"][remove_buff_size:] # Pass data to codec to decode ADCP data self.adcp_codec.add(self.serial_raw_bytes) except Exception as ex: # Do nothing # This is to prevent from seeing binary data on screen logging.info("Error Reading serial data" + str(ex)) # Put a small sleep to allow more data to go into the buffer time.sleep(0.01) except serial.SerialException as se: logging.error("Error using the serial port.\n" + str(se)) self.disconnect_serial() except Exception as ex: logging.error("Error processing the data.\n" + str(ex)) self.disconnect_serial() def record_raw_data(self, serial_bytes): """ Write the raw data from the serial port to a binary file. This will write any data. :param serial_bytes: Serial bytes array """ if self.is_record_raw_data: # Ensure the file is created self.create_river_project() # If the file is created, write the data if self.raw_file: self.raw_file.write(serial_bytes) def record_ens(self, ens: Ensemble): """ Record the ensemble to the db file. Record the raw data to a raw binary file. :param ens: Ensemble data. """ # Create the project db file if it does not exist self.create_river_project() # Write the ensemble to the db file if self.db_file: # Set the latest index of the ensemble in the db file # Set the burst number to the transect number # Set the is_batch_write to false. We are not writing in bulk self.curr_ens_index = self.db_file.add_ensemble( ens, burst_num=self.transect_index, is_batch_write=False)
def __init__(self): self.rti_config = RtiConfig() self.rti_config.init_average_waves_config() self.rti_config.init_terminal_config() self.rti_config.init_waves_config() self.rti_config.init_plot_server_config() self.is_screen_data = True self.already_got_data = False self.ens_count = 0 prj_idx = 1 # Bokeh Plot #self.plot_manager = RtiBokehPlotManager(self.rti_config) #self.plot_manager.start() #self.bokeh_server = RtiBokehServer(self.rti_config, self.plot_manager) # Matplotlib plot #self.display_ens = DisplayEnsembles() # Setup the StreamLit Plots self.heatmap = StreamlitHeatmap() self.mag_dir_line = StreamlitMagDirLine() #self.volt_line = StreamlitPowerLine() #self.ancillary_line = StreamlitAncillaryLine() # On reprocessing in Streamlit, it will through an error if this # is rerun because the browser content is not in the main thread rti_check = RtiCheckFile() # Event handler for ensembles rti_check.ensemble_event += self.ens_handler # Select a file to process #uploaded_file = st.file_uploader("Choose a file", type=['ens', 'bin']) #if uploaded_file is not None: # do stuff #print(uploaded_file) #file_paths = uploaded_file file_paths = rti_check.select_files() # Verify a file was selected if len(file_paths) > 0: #file_paths = [Path("//Beansack/rico/RTI/Data/cs/nav.com.cn/xiamen202007-/01400000000000000000000000000254_xiamen202007_2.ENS")] logging.debug(file_paths) # Get the folder path from the first file path folder_path = PurePath(file_paths[0]).parent prj_name = Path(file_paths[0]).stem db_name = str(prj_name) + ".db" # Create a project file to store the results db_path = str(folder_path / db_name) logging.debug(db_path) if not Path(db_path).exists(): self.project = RtiSqliteProjects(file_path=db_path) self.project.create_tables() prj_idx = self.project.add_prj_sql(str(prj_name), db_path) # Begin the batch writing to the database self.project.begin_batch(str(prj_name)) # Process the selected file rti_check.process(file_paths, show_live_error=False) # Get the summary and add it to the sqlite project file_summary = rti_check.get_summary() self.project.add_summary(json.dumps(file_summary), prj_idx) # End any remaining batch self.project.end_batch() else: # Create a connection to the sqlite project file self.project = RtiSqliteProjects(file_path=db_path) prj_idx = self.project.check_project_exist(str(prj_name)) # Display the summary prj_json_summary = self.project.get_summary(prj_idx) if len(prj_json_summary) >= 1: json_str = prj_json_summary[0][0] json_summ = json.loads(json_str) #print(json.dumps(json_summ, indent=1)) print(json.dumps(json_summ["errors"], indent=1)) print(json.dumps(json_summ["summary"], indent=1)) st.title("Summary") st.json(json_summ) # Plot heatmap #self.heatmap.get_plot("mag") #self.heatmap.get_plot("dir") # Plot mag and direction line plot #self.mag_dir_line.get_bin_selector() #self.mag_dir_line.get_plot("mag") #self.mag_dir_line.get_plot("dir") #self.ancillary_line.get_plot() StreamlitAncillaryLine.get_sqlite_plot(db_path) # Plot the Status StreamlitStatusLine.get_sqlite_plot(db_path) # Plot the Voltage StreamlitPowerLine.get_sqlite_plot(db_path) # Plot the Bottom Track Range StreamlitBottomTrackRangeLine.get_sqlite_plot(db_path) # Plot the Bottom Track Beam Velocity with filtering turned on StreamlitBottomTrackBeamVelocityLine.get_sqlite_plot(db_path, filter=True, filter_max=5.0) # Plot the Bottom Track Beam Velocity StreamlitBottomTrackBeamVelocityLine.get_sqlite_plot(db_path, filter=False) # Plot the Bottom Track Vessel Speed with filtering turned on StreamlitBottomTrackVesselSpeedLine.get_sqlite_plot(db_path, filter=True, filter_max=5.0) # Plot the Bottom Track Vessel Speed StreamlitBottomTrackVesselSpeedLine.get_sqlite_plot(db_path, filter=False) # Plot the Bottom Track Vessel Direction with filtering turned on StreamlitBottomTrackVesselDirectionLine.get_sqlite_plot(db_path, filter=True) # Plot the Bottom Track Vessel Direction StreamlitBottomTrackVesselDirectionLine.get_sqlite_plot(db_path, filter=False) # Plot the Water Magnitude StreamlitMagHeatmap.get_sqlite_plot(db_path)
import os import h5py from rti_python.River.RiverProject import RiverProject from rti_python.River.RiverProjectMeta import RiverProjectMeta from rti_python.Utilities.config import RtiConfig rti_config = RtiConfig() rti_config.init_river_project_config() def test_constructor(): project = RiverProject(rti_config, "Project111", os.path.join(rti_config.config['RIVER']['output_dir'], "Project111.hdf5")) assert "Project111" == project.name assert os.path.join(rti_config.config['RIVER']['output_dir'], "Project111.hdf5") == project.file_path def test_set_values(): project = RiverProject(rti_config, "Project112", os.path.join(rti_config.config['RIVER']['output_dir'], "Project112.hdf5")) project.adcp_serial_num = "0001" project.firmware_ver = "0.0.1.2" project.draft_transducer = 11.11 project.magnetic_declination = 1.234 project.software_ver = "1.2.3.4" project.ident_of_crew = "Jon and Bob" project.station_name = "River1" project.station_id = "111R1" project.water_temp = 1.34 project.percentage_discharged_measured = 23.4 project.channel_width = 1234 project.cross_section_area = 34
def set_options(self, is_boat_speed: bool, is_boat_dir: bool, is_heading: bool, is_pitch: bool, is_roll: bool, is_temp: bool, is_gnss_qual: bool, is_gnss_hdop: bool, is_num_sats: bool, is_water_speed: bool, is_water_dir: bool, is_vtg_speed: bool, max_ens: int): """ Set the options for the Time Series. :param is_boat_speed: Boat Speed on/off :param is_boat_dir: Boat Direciton on/off :param is_heading: Heading on/off :param is_pitch: Pitch on/off :param is_roll: roll on/off :param is_temp: temperature on/off :param is_gnss_qual: GNSS Quality on/off :param is_gnss_hdop: GNSS HDOP on/off :param is_num_sats: Number of Sats on/off :param is_water_speed: Water Speed on/off :param is_water_dir: Water Direction on/off :param is_vtg_speed: GPS Speed on/off :param max_ens: Maximum ensembles to display :return: """ # Lock the object self.thread_lock.acquire() # Write settings to the config self.rti_config.config['TIMESERIES'][ 'IS_BOAT_SPEED'] = RtiConfig.bool_to_str(is_boat_speed) self.rti_config.config['TIMESERIES'][ 'IS_BOAT_DIR'] = RtiConfig.bool_to_str(is_boat_dir) self.rti_config.config['TIMESERIES'][ 'IS_HEADING'] = RtiConfig.bool_to_str(is_heading) self.rti_config.config['TIMESERIES'][ 'IS_PITCH'] = RtiConfig.bool_to_str(is_pitch) self.rti_config.config['TIMESERIES'][ 'IS_ROLL'] = RtiConfig.bool_to_str(is_roll) self.rti_config.config['TIMESERIES'][ 'IS_TEMPERATURE'] = RtiConfig.bool_to_str(is_temp) self.rti_config.config['TIMESERIES'][ 'IS_GNSS_QUAL'] = RtiConfig.bool_to_str(is_gnss_qual) self.rti_config.config['TIMESERIES'][ 'IS_GNSS_HDOP'] = RtiConfig.bool_to_str(is_gnss_hdop) self.rti_config.config['TIMESERIES'][ 'IS_NUM_SATS'] = RtiConfig.bool_to_str(is_num_sats) self.rti_config.config['TIMESERIES'][ 'IS_WATER_SPEED'] = RtiConfig.bool_to_str(is_water_speed) self.rti_config.config['TIMESERIES'][ 'IS_WATER_DIR'] = RtiConfig.bool_to_str(is_water_dir) self.rti_config.config['TIMESERIES'][ 'IS_VTG_SPEED'] = RtiConfig.bool_to_str(is_vtg_speed) self.rti_config.config['TIMESERIES']['MAX_ENS'] = str(max_ens) self.rti_config.write() # Set the options self.is_boat_speed = is_boat_speed self.is_boat_dir = is_boat_dir self.is_heading = is_heading self.is_pitch = is_pitch self.is_roll = is_roll self.is_temperature = is_temp self.is_gnss_qual = is_gnss_qual self.is_gnss_hdop = is_gnss_hdop self.is_num_sats = is_num_sats self.is_water_speed = is_water_speed self.is_water_dir = is_water_dir self.is_vtg_speed = is_vtg_speed # Check if Max Ensembles changed # If it changed, then create new deque with new maxlen if self.max_ens != max_ens and not math.isnan(max_ens): self.max_ens = max_ens # Create a temp of the values boat_speed = list(self.boat_speed) boat_dir = list(self.boat_dir) heading = list(self.heading) pitch = list(self.pitch) roll = list(self.roll) temperature = list(self.temperature) gnss_qual = list(self.gnss_qual) gnss_hdop = list(self.gnss_hdop) num_sats = list(self.num_sats) water_speed = list(self.water_speed) water_dir = list(self.water_dir) vtg_speed = list(self.vtg_speed) # Recreate the deque with the new maxlen and original data self.boat_speed = deque(boat_speed, maxlen=self.max_ens) self.boat_dir = deque(boat_dir, maxlen=self.max_ens) self.heading = deque(heading, maxlen=self.max_ens) self.pitch = deque(pitch, maxlen=self.max_ens) self.roll = deque(roll, maxlen=self.max_ens) self.temperature = deque(temperature, maxlen=self.max_ens) self.gnss_qual = deque(gnss_qual, maxlen=self.max_ens) self.gnss_hdop = deque(gnss_hdop, maxlen=self.max_ens) self.num_sats = deque(num_sats, maxlen=self.max_ens) self.water_speed = deque(water_speed, maxlen=max_ens) self.water_dir = deque(water_dir, maxlen=max_ens) self.vtg_speed = deque(vtg_speed, maxlen=max_ens) # Release the lock self.thread_lock.release()
class DataManager: def __init__(self): # RTI Config file self.rti_config = RtiConfig() self.rti_config.init_terminal_config() # Terminal Options self.rti_config.init_timeseries_plot_config() # Time Series Options self.adcp_codec = AdcpCodec() self.adcp_codec.ensemble_event += self.handle_ensemble_data self.tabular_vm = TabularDataVM() self.amp_vm = AmplitudeVM() self.contour_vm = ContourVM() self.shiptrack_vm = ShipTrackVM() self.timeseries_vm = TimeSeriesVM(rti_config=self.rti_config) self.adcp_terminal = AdcpTerminalVM(self.rti_config) self.adcp_terminal.on_serial_data += self.handle_adcp_serial_data # ZeroRPC Manager and Thread self.zero_rpc = ZeroRpcManager(rti_config=self.rti_config, data_mgr=self, tabular_vm=self.tabular_vm, amp_vm=self.amp_vm, contour_vm=self.contour_vm, shiptrack_vm=self.shiptrack_vm, timeseries_vm=self.timeseries_vm, adcp_terminal=self.adcp_terminal) self.zero_rpc_thread = Thread(name="ZeroRPC", target=self.zero_rpc.run_server, args=(4241, )) # Ensemble processing thread self.ens_thread_alive = True self.ens_queue = deque(maxlen=1000) self.ens_thread_event = Event() self.ens_thread = Thread(name="DataManager", target=self.ens_thread_run) # Used to remove vessel speed self.prev_bt_east = Ensemble.BadVelocity self.prev_bt_north = Ensemble.BadVelocity self.prev_bt_vert = Ensemble.BadVelocity def start(self, zerorpc_port: int): """ Start the ensemble thread. This thread handles all the incoming ensembles. :param zerorpc_port: zerorpc port. :return: """ # Start the ens thread self.ens_thread.start() # Start the zerorpc thread self.zero_rpc_thread.start() def shutdown(self): """ Shutdown the object. :return: """ # Shutdown the Ensemble thread self.ens_thread_alive = False self.ens_thread_event.set() def handle_adcp_serial_data(self, sender, data): """ Receive raw serial data from the serial port. Pass the data to the codec to be processed. :param sender: Not Used. :param data: Raw ensemble binary data. :return: """ logging.info("DataManager: Serial Data Received") self.adcp_codec.add(data) def handle_ensemble_data(self, sender, ens): """ Receiver data from the codec and process it by passing it to the data manager incoming ensemble. :param sender:Not Used :param ens: Ensemble from codec :return: """ self.incoming_ens(ens) def incoming_ens(self, ens: Ensemble): """ Handle all incoming data to be displayed. Put the data in a queue then wakeup the thread. :param ens: Ensemble to be displayed. :return: """ # Add the data to the queue self.ens_queue.append(ens) # Wakeup the thread self.ens_thread_event.set() def ens_thread_run(self): """" Run a thread to handle the incoming ensemble data. Pass the data to the Waveforce codec and average water. """ while self.ens_thread_alive: # Wait until the thread is awoken self.ens_thread_event.wait() # Check if data is in the queue while len(self.ens_queue) > 0: # Get the data from the queue ens = self.ens_queue.popleft() # QA QC the data EnsembleQC.scan_ensemble(ens) # Screen the data # Pass data if ens: if ens.IsEnsembleData: logging.info("AdcpDataManager: Process Ensemble: " + str(ens.EnsembleData.EnsembleNumber)) # Screen Data # Pass Data to Tabular data self.tabular_vm.set_ens(ens) # Pass data to Amplitude plot VM self.amp_vm.set_ens(ens) # Pass data to Contour plot VM self.contour_vm.set_ens(ens) # Pass data to Ship Track plot VM self.shiptrack_vm.set_ens(ens) # Pass data to Time Series plot VM self.timeseries_vm.set_ens(ens) # Reset the event self.ens_thread_event.clear()
def __init__(self, config=None): QtWidgets.QMainWindow.__init__(self) # Setup the logging #RtiLogging.RtiLogger(log_level=logging.DEBUG) self.rti_config = RtiConfig() self.rti_config.init_average_waves_config() self.rti_config.init_terminal_config() self.rti_config.init_waves_config() self.rti_config.init_plot_server_config() # Setup logging log_file_path = self.rti_config.config['Waves'][ 'output_dir'] + "/rti_waves.log" logging.basicConfig( level=logging.INFO, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%m-%d %H:%M', filename=log_file_path, filemode='a+') self.bokeh_app = None # Initialize Monitor self.Monitor = MonitorVM(self, self.rti_config) docked_monitor = QtWidgets.QDockWidget("Monitor", self) docked_monitor.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) docked_monitor.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable) docked_monitor.setWidget(self.Monitor) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, docked_monitor) # Initialize the Setup self.Setup = SetupVM(self, self.rti_config) self.docked_setup = QtWidgets.QDockWidget("Setup", self) self.docked_setup.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) self.docked_setup.setFeatures( QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetClosable) self.docked_setup.resize(500, 400) self.docked_setup.setWidget(self.Setup) self.docked_setup.setVisible(False) #self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.docked_setup) # Initialize Terminal self.Terminal = TerminalVM(self, self.rti_config) docked_terminal = QtWidgets.QDockWidget("Terminal", self) docked_terminal.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) docked_terminal.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable) docked_terminal.setWidget(self.Terminal) self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, docked_terminal) #self.tabifyDockWidget(self.docked_setup, docked_terminal) # Initialize the Average Water Column self.AvgWater = AverageWaterVM(self, self.rti_config) self.docked_avg_water = QtWidgets.QDockWidget("AvgWater", self) self.docked_avg_water.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) self.docked_avg_water.setFeatures( QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetClosable) self.docked_avg_water.resize(800, 600) self.docked_avg_water.setWidget(self.AvgWater) self.docked_avg_water.setVisible(False) # Start Event Loop needed for server #asyncio.set_event_loop(asyncio.new_event_loop()) # Setting num_procs here means we can't touch the IOLoop before now, we must # let Server handle that. If you need to explicitly handle IOLoops then you # will need to use the lower level BaseServer class. #self.server = Server({'/': self.AvgWater.setup_bokeh_server}) #apps = {'/': self.AvgWater.setup_bokeh_server} #handler = FunctionHandler(self.AvgWater.setup_bokeh_server) #app = Application(handler) apps = {'/': self.get_bokeh_app()} bokeh_port = int(self.rti_config.config['PLOT']['PORT']) bokeh_ip = self.rti_config.config['PLOT']['IP'] websocket_allow = bokeh_ip + ":" + str(bokeh_port) self.server = Server(apps, port=bokeh_port, address=bokeh_ip, allow_websocket_origin=[websocket_allow]) self.server.start() # Only display the webpage if enabled if self.rti_config.config.getboolean('PLOT', 'LIVE'): self.server.show('/') #thread = Thread(target=self.start_bokeh_server) #thread.start() # Add the displays to the manager to monitor all the data self.AutoWavesManager = autowaves_manger.AutoWavesManager( self, self.rti_config, self.Terminal, self.Setup, self.Monitor, self.AvgWater) # Initialize the window self.main_window_init() # Outside the notebook ioloop needs to be started loop = IOLoop.current() #loop.start() t = Thread(target=loop.start, daemon=True) t.start()
class MainWindow(QtWidgets.QMainWindow): """ Main window for the application """ def __init__(self, config=None): QtWidgets.QMainWindow.__init__(self) # Setup the logging #RtiLogging.RtiLogger(log_level=logging.DEBUG) self.rti_config = RtiConfig() self.rti_config.init_average_waves_config() self.rti_config.init_terminal_config() self.rti_config.init_waves_config() self.rti_config.init_plot_server_config() # Setup logging log_file_path = self.rti_config.config['Waves'][ 'output_dir'] + "/rti_waves.log" logging.basicConfig( level=logging.INFO, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%m-%d %H:%M', filename=log_file_path, filemode='a+') self.bokeh_app = None # Initialize Monitor self.Monitor = MonitorVM(self, self.rti_config) docked_monitor = QtWidgets.QDockWidget("Monitor", self) docked_monitor.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) docked_monitor.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable) docked_monitor.setWidget(self.Monitor) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, docked_monitor) # Initialize the Setup self.Setup = SetupVM(self, self.rti_config) self.docked_setup = QtWidgets.QDockWidget("Setup", self) self.docked_setup.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) self.docked_setup.setFeatures( QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetClosable) self.docked_setup.resize(500, 400) self.docked_setup.setWidget(self.Setup) self.docked_setup.setVisible(False) #self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.docked_setup) # Initialize Terminal self.Terminal = TerminalVM(self, self.rti_config) docked_terminal = QtWidgets.QDockWidget("Terminal", self) docked_terminal.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) docked_terminal.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable) docked_terminal.setWidget(self.Terminal) self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, docked_terminal) #self.tabifyDockWidget(self.docked_setup, docked_terminal) # Initialize the Average Water Column self.AvgWater = AverageWaterVM(self, self.rti_config) self.docked_avg_water = QtWidgets.QDockWidget("AvgWater", self) self.docked_avg_water.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) self.docked_avg_water.setFeatures( QtWidgets.QDockWidget.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetClosable) self.docked_avg_water.resize(800, 600) self.docked_avg_water.setWidget(self.AvgWater) self.docked_avg_water.setVisible(False) # Start Event Loop needed for server #asyncio.set_event_loop(asyncio.new_event_loop()) # Setting num_procs here means we can't touch the IOLoop before now, we must # let Server handle that. If you need to explicitly handle IOLoops then you # will need to use the lower level BaseServer class. #self.server = Server({'/': self.AvgWater.setup_bokeh_server}) #apps = {'/': self.AvgWater.setup_bokeh_server} #handler = FunctionHandler(self.AvgWater.setup_bokeh_server) #app = Application(handler) apps = {'/': self.get_bokeh_app()} bokeh_port = int(self.rti_config.config['PLOT']['PORT']) bokeh_ip = self.rti_config.config['PLOT']['IP'] websocket_allow = bokeh_ip + ":" + str(bokeh_port) self.server = Server(apps, port=bokeh_port, address=bokeh_ip, allow_websocket_origin=[websocket_allow]) self.server.start() # Only display the webpage if enabled if self.rti_config.config.getboolean('PLOT', 'LIVE'): self.server.show('/') #thread = Thread(target=self.start_bokeh_server) #thread.start() # Add the displays to the manager to monitor all the data self.AutoWavesManager = autowaves_manger.AutoWavesManager( self, self.rti_config, self.Terminal, self.Setup, self.Monitor, self.AvgWater) # Initialize the window self.main_window_init() # Outside the notebook ioloop needs to be started loop = IOLoop.current() #loop.start() t = Thread(target=loop.start, daemon=True) t.start() def get_bokeh_app(self): """ Generate a single instance of the bokeh app. :return: Bokeh app created. """ if self.bokeh_app is None: handler = FunctionHandler(self.AvgWater.setup_bokeh_server) self.bokeh_app = Application(handler) return self.bokeh_app def main_window_init(self): # Set the title of the window self.setWindowTitle( "Rowe Technologies Inc. - AutoWaves Monitor v1.9.3") self.setWindowIcon(QtGui.QIcon(":rti.ico")) self.resize(930, 550) mainMenu = self.menuBar() fileMenu = mainMenu.addMenu('File') #avgMenu = mainMenu.addMenu('Average Water') setupMenu = mainMenu.addMenu('Setup') playbackButton = QAction(QIcon('exit24.png'), 'Playback', self) playbackButton.setShortcut('Ctrl+P') playbackButton.setStatusTip('Playback files') playbackButton.triggered.connect(self.playback) fileMenu.addAction(playbackButton) exitButton = QAction(QIcon('exit24.png'), 'Exit', self) exitButton.setShortcut('Ctrl+Q') exitButton.setStatusTip('Exit application') exitButton.triggered.connect(self.close) fileMenu.addAction(exitButton) setupButton = QAction(QIcon('exit24.png'), 'Waves Setup', self) setupButton.setShortcut('Ctrl+S') setupButton.setStatusTip('Setup The Waves Settings') setupButton.triggered.connect(self.display_setup_view) setupMenu.addAction(setupButton) #avgWaterButton = QAction(QIcon('exit24.png'), 'Average Water', self) #avgWaterButton.setShortcut('Ctrl+A') #avgWaterButton.setStatusTip('Average the Water Column') #avgWaterButton.triggered.connect(self.display_avg_water_view) #avgMenu.addAction(avgWaterButton) waveHeightPlotButton = QAction(QIcon('exit24.png'), 'Water Height Plot', self) waveHeightPlotButton.setShortcut('Ctrl+H') waveHeightPlotButton.setStatusTip('Average Water Height Plot') waveHeightPlotButton.triggered.connect( self.display_wave_height_plot_view) #avgMenu.addAction(waveHeightPlotButton) eastVelPlotButton = QAction(QIcon('exit24.png'), 'East Velocity Plot', self) eastVelPlotButton.setShortcut('Ctrl+E') eastVelPlotButton.setStatusTip('Earth Velocity [East] Plot ') eastVelPlotButton.triggered.connect(self.display_east_vel_plot_view) #avgMenu.addAction(eastVelPlotButton) northVelPlotButton = QAction(QIcon('exit24.png'), 'North Velocity Plot', self) northVelPlotButton.setShortcut('Ctrl+N') northVelPlotButton.setStatusTip('Earth Velocity [North] Plot ') northVelPlotButton.triggered.connect(self.display_north_vel_plot_view) #avgMenu.addAction(northVelPlotButton) magVelPlotButton = QAction(QIcon('exit24.png'), 'Velocity Magnitude Plot', self) magVelPlotButton.setShortcut('Ctrl+N') magVelPlotButton.setStatusTip('Velocity Magnitude Plot ') magVelPlotButton.triggered.connect(self.display_mag_plot_view) #avgMenu.addAction(magVelPlotButton) dirVelPlotButton = QAction(QIcon('exit24.png'), 'Water Direction Plot', self) dirVelPlotButton.setShortcut('Ctrl+N') dirVelPlotButton.setStatusTip('Water Direction Plot ') dirVelPlotButton.triggered.connect(self.display_dir_plot_view) #avgMenu.addAction(dirVelPlotButton) # Show the main window self.show() def start_bokeh_server(self): #self.server.io_loop.add_callback(self.server.show, "/") #self.server.io_loop.start() # Outside the notebook ioloop needs to be started loop = IOLoop.current() loop.start() def openFileNamesDialog(self): """ Open a file names dialog to select multiple files. :return: """ options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog files, _ = QFileDialog.getOpenFileNames( self, "Open Ensemble Files", "", "All Files (*);;Ensemble Files (*.ens)", options=options) return files @pyqtSlot() def display_setup_view(self): self.docked_setup.setFloating(True) self.docked_setup.show() @pyqtSlot() def display_avg_water_view(self): self.docked_avg_water.setFloating(True) self.docked_avg_water.show() @pyqtSlot() def display_wave_height_plot_view(self): self.AvgWater.docked_wave_height.setFloating(True) self.AvgWater.docked_wave_height.show() @pyqtSlot() def display_east_vel_plot_view(self): self.AvgWater.docked_earth_vel_east.setFloating(True) self.AvgWater.docked_earth_vel_east.show() @pyqtSlot() def display_north_vel_plot_view(self): self.AvgWater.docked_earth_vel_north.setFloating(True) self.AvgWater.docked_earth_vel_north.show() @pyqtSlot() def display_mag_plot_view(self): self.AvgWater.docked_mag.setFloating(True) self.AvgWater.docked_mag.show() @pyqtSlot() def display_dir_plot_view(self): self.AvgWater.docked_dir.setFloating(True) self.AvgWater.docked_dir.show() @pyqtSlot() def playback(self): """ Playback files. Select the files. Then pass them to the manager to read and playback the files. :return: """ # Select Files files = self.openFileNamesDialog() logging.debug("Playing back files: " + str(files)) # Pass files to manager if files: self.AutoWavesManager.playback_file(files) def closeEvent(self, event): """ Generate 'question' dialog on clicking 'X' button in title bar. Reimplement the closeEvent() event handler to include a 'Question' dialog with options on how to proceed - Close, Cancel buttons """ reply = QtWidgets.QMessageBox.question( self, "Message", "Are you sure you want to quit?", QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel) if reply == QtWidgets.QMessageBox.Close: logging.debug("Shutting down") self.Setup.shutdown() logging.debug("Setup VM shutdown") self.Terminal.shutdown() logging.debug("Terminal VL shutdown") self.AvgWater.shutdown() logging.debug("Average Water shutdown") self.AutoWavesManager.shutdown() logging.debug("Auto Waves Manager shutdown") event.accept() else: event.ignore()