class DisplayView(QObject): """ Acts as MVC view, controls the UI """ close_signal = Signal() def __init__(self, version=None, parent=None): """ DisplayView constructor :param str version: version to be shown on window title """ super(DisplayView, self).__init__(parent) self.__login_dialog = None self.__display_window = None self.__version = version def init_login_view(self, user_names): """ Initializes the login view """ self.__login_dialog = LoginDialog(user_names, self.__version) self.__login_dialog.close_signal.connect(self.__close_action) self.__login_dialog.show() def register_login_callback(self, callback): """ Register login button callback :param callback: Slot function to be called on button click callback signature: callback(user_name:str, password:str) """ if self.__login_dialog: self.__login_dialog.login.clicked.connect( lambda: callback(str(self.__login_dialog.user_name.text()), str(self.__login_dialog.password.text())) ) def login_view_show_error(self, message=None): """ Show error message on login screen :param str message: Optional string, clears the status if None else sets it """ if self.__login_dialog: if message: self.__login_dialog.login_status.setText(message) else: self.__login_dialog.login_status.clear() def close_login_view(self): """ Hides the login view """ if self.__login_dialog: self.__login_dialog.hide() self.__login_dialog.deleteLater() def update_status_bar(self, message): """ Update the display view status bar with the specified message :param str message: message string """ if self.__display_window: self.__display_window.statusbar.showMessage(message) def set_connection_status(self, connection_status, color): """ Update the connection status label with the specified message :param str connection_status: current connection status (Network Down/ Internet Down/ OK) :param str color: color of the connection status(red/green) """ if self.__display_window: self.__display_window.connection_status.setText(connection_status) self.__display_window.connection_status.setStyleSheet("color: {}".format(color)) elif self.__login_dialog: # Ignore connection status = OK if connection_status != UIStrings.OK: self.__login_dialog.login_connection_status.setText(connection_status) def init_display_view(self, setting): """ Initializes display window Currently showing only thresholds from the settings :param ControllerSetting setting: initial settings """ self.__display_window = DisplayMainWindow(self.__version) self.__display_window.close_signal.connect(self.__close_action) self.update_settings(setting) # add graph plot self.__display_window.temperature_figure = matplotlib.figure.Figure(facecolor="white", edgecolor="black") self.__display_window.temperature_canvas = figure_canvas(self.__display_window.temperature_figure) self.__display_window.graph_layout.addWidget(self.__display_window.temperature_canvas) self.__display_window.humidity_figure = matplotlib.figure.Figure(facecolor="white", edgecolor="black") self.__display_window.humidity_canvas = figure_canvas(self.__display_window.humidity_figure) self.__display_window.humidity_graph_layout.addWidget(self.__display_window.humidity_canvas) self.__init_graph() # Show display window self.__display_window.show() def update_settings(self, setting): """ Update the thresholds on display page :param ControllerSetting setting: controller setting object """ if setting: self.__display_window.temperature_threshold.setText("{:.2f}".format(setting.temperature_threshold)) self.__display_window.humidity_threshold.setText("{:.2f}".format(setting.humidity_threshold)) def plot_graph(self, temperature_data, humidity_data): """ Plot time vs temperature graph and time vs humidity graph :param dict temperature_data: key-time, value-temperature :param dict humidity_data: key- time, value- temperature """ self.__display_window.temperature_value.setText("{:.2f}".format(temperature_data[temperature_data.keys()[-1]])) self.__display_window.humidity_value.setText("{:.2f}".format(humidity_data[humidity_data.keys()[-1]])) self.__init_graph() self.__update_graph(temperature_data, humidity_data) def __init_graph(self): """ Initializes graph parameters """ # initialize temperature graph if self.__display_window.temperature_axes: self.__display_window.temperature_axes.clear() if self.__display_window.temperature_figure: self.__display_window.temperature_figure.clf() self.__display_window.temperature_axes = self.__display_window.temperature_figure.add_subplot(1, 1, 1) self.__display_window.temperature_axes.grid(True) self.__display_window.temperature_axes.set_ylim(MIN_TEMPERATURE, MAX_TEMPERATURE) self.__display_window.temperature_axes.set_ylabel("Temperature (" + DEGREE_CELSIUS + ")", fontsize=FONT_SIZE) self.__display_window.temperature_axes.tick_params(axis="both", which="major", labelsize=FONT_SIZE) self.__display_window.temperature_canvas.draw() # initialize humidity graph if self.__display_window.humidity_axes: self.__display_window.humidity_axes.clear() if self.__display_window.humidity_figure: self.__display_window.humidity_figure.clf() self.__display_window.humidity_axes = self.__display_window.humidity_figure.add_subplot(1, 1, 1) self.__display_window.humidity_axes.grid(True) self.__display_window.humidity_axes.set_ylim(MIN_HUMIDITY, MAX_HUMIDITY) self.__display_window.humidity_axes.set_ylabel("Humidity (%RH)", fontsize=FONT_SIZE) self.__display_window.humidity_axes.tick_params(axis="both", which="major", labelsize=FONT_SIZE) self.__display_window.humidity_canvas.draw() def __update_graph(self, temperature_data, humidity_data): """ Update graph with latest measurements :param dict temperature_data: key-time, value-temperature """ # Clipping out of range temperature values to min and max values for key, _ in temperature_data.items(): if temperature_data[key] > MAX_TEMPERATURE: temperature_data[key] = MAX_TEMPERATURE elif temperature_data[key] < MIN_TEMPERATURE: temperature_data[key] = MIN_TEMPERATURE # Clipping out of range humidity values to min and max values for key, _ in humidity_data.items(): if humidity_data[key] > MAX_HUMIDITY: humidity_data[key] = MAX_HUMIDITY elif humidity_data[key] < MIN_HUMIDITY: humidity_data[key] = MIN_HUMIDITY # update temperature graph x_axis_data = temperature_data.keys() y_axis_data = temperature_data.values() self.__display_window.temperature_axes.plot(x_axis_data, y_axis_data, lw=3.0, label="Temperature", color="blue") if self.__display_window.temperature_threshold.text(): temperature_threshold = self.__display_window.temperature_threshold.text() y_axis_data = [temperature_threshold] * len(temperature_data.keys()) self.__display_window.temperature_axes.plot( x_axis_data, y_axis_data, color="red", linestyle="dashed", label="Temperature Threshold", lw=3.0 ) self.__display_window.temperature_axes.legend( bbox_to_anchor=(0, 1), numpoints=1, loc="lower left", fontsize=LEGEND_FONT_SIZE, borderaxespad=0 ) self.__display_window.temperature_figure.autofmt_xdate() self.__display_window.temperature_axes.xaxis.set_major_formatter(DateFormatter(TIME_FMT)) self.__display_window.temperature_axes.xaxis.set_major_locator(LinearLocator()) self.__display_window.temperature_canvas.draw() # update humidity graph self.__display_window.humidity_axes.plot( humidity_data.keys(), humidity_data.values(), lw=3.0, label="Humidity", color="green" ) if self.__display_window.humidity_threshold.text(): humidity_threshold = self.__display_window.humidity_threshold.text() y_axis_data = [humidity_threshold] * len(humidity_data.keys()) self.__display_window.humidity_axes.plot( humidity_data.keys(), y_axis_data, color="red", linestyle="dashed", label="Humidity Threshold", lw=3.0 ) self.__display_window.humidity_axes.legend( bbox_to_anchor=(0, 1), numpoints=1, loc="lower left", fontsize=LEGEND_FONT_SIZE, borderaxespad=0 ) self.__display_window.humidity_figure.autofmt_xdate() self.__display_window.humidity_axes.xaxis.set_major_formatter(DateFormatter(TIME_FMT)) self.__display_window.humidity_axes.xaxis.set_major_locator(LinearLocator()) self.__display_window.humidity_canvas.draw() def __update_device_status_label(self, widget, device_on): """ Update device status label on screen :param QLabel widget: pyside QLabel widget for text display :param bool device_on: True if device is "ONLINE" """ if device_on: widget.setText(UIStrings.DEVICE_ONLINE) widget.setStyleSheet("color: green") else: widget.setText(UIStrings.DEVICE_OFFLINE) widget.setStyleSheet("color: red") def update_device_status(self, device_type, device_on): """ Update device status :param DeviceEnum device_type: device type e.g sensor or controller :param bool device_on: True if device is "ONLINE" """ if self.__display_window: if device_type == DeviceEnum.controller: self.__update_device_status_label(self.__display_window.controller_status, device_on) elif device_type == DeviceEnum.sensor: self.__update_device_status_label(self.__display_window.sensor_status, device_on) elif device_type == DeviceEnum.actuator: self.__update_device_status_label(self.__display_window.actuator_status, device_on) else: LOGGER.error("{} device doesn't exist".format(device_type)) def update_relay_status(self, event): """ Update relay information on display like status, mode etc :param RelayStatusEvent event: RelayStatusEvent object """ self.__display_window.heater_status.setText(UIStrings.RELAY_ON if event.relay_1_on else UIStrings.RELAY_OFF) self.__display_window.fan_status.setText(UIStrings.RELAY_ON if event.relay_2_on else UIStrings.RELAY_OFF) self.__display_window.heater_mode.setText( UIStrings.RELAY_AUTO if event.relay_1_mode == "AUTO" else UIStrings.RELAY_MANUAL ) self.__display_window.fan_mode.setText( UIStrings.RELAY_AUTO if event.relay_2_mode == "AUTO" else UIStrings.RELAY_MANUAL ) def update_latency(self, latency): """ Update latency information on display screen :param float latency: latency value, if None set latency as "NA" """ if self.__display_window: if latency: if latency > MAX_LATENCY_VALUE: # Avoid showing this value as it is likely to be incorrect latency value self.__display_window.latency.setText("> {} sec".format(MAX_LATENCY_VALUE)) else: self.__display_window.latency.setText("{:.2f} sec".format(latency)) else: self.__display_window.latency.setText("NA") def __close_action(self): """ Slot for display or login view close action """ self.close_signal.emit()
class AdminView(QObject): """ Acts as MVC view, controls the UI """ close_signal = Signal() __combo_box_above_index = 0 __combo_box_below_index = 1 def __init__(self, version=None, parent=None): """ AdminView constructor :param str version: version to be shown on window title """ super(AdminView, self).__init__(parent) self.__login_dialog = None self.__admin_window = None self.__save_setting_callback = None self.__version = version def init_login_view(self, user_names): """ Initializes the login view :param list user_names: list of user name strings, will be used for showing auto-completion """ self.__login_dialog = LoginDialog(user_names, self.__version) self.__login_dialog.close_signal.connect(self.__close_action) self.__login_dialog.show() def register_login_callback(self, callback): """ Register login button callback :param callback: Slot function to be called on button click callback signature: callback(user_name:str, password:str) """ if self.__login_dialog: self.__login_dialog.login.clicked.connect( lambda: callback(str(self.__login_dialog.user_name.text()), str(self.__login_dialog.password.text()))) def __save_setting_clicked(self): """ Local function called when save setting is clicked Creates ControllerSetting object and calls controller callback """ setting = { "temperature_threshold": self.__admin_window.temperature_threshold.value(), "humidity_threshold": self.__admin_window.humidity_threshold.value(), "temperature_read_interval": self.__admin_window.temperature_read_interval.value(), "humidity_read_interval": self.__admin_window.humidity_read_interval.value(), "temperature_read_delta": self.__admin_window.temperature_read_delta.value(), "humidity_read_delta": self.__admin_window.humidity_read_delta.value(), "sensor_heartbeat": self.__admin_window.sensor_heartbeat.value(), "actuator_heartbeat": self.__admin_window.actuator_heartbeat.value(), "controller_heartbeat": self.__admin_window.controller_heartbeat.value() } if self.__admin_window.temperature_orientation.currentIndex() == \ AdminView.__combo_box_above_index: setting[ "temperature_orientation"] = ControllerSetting.threshold_orientation_above else: setting[ "temperature_orientation"] = ControllerSetting.threshold_orientation_below if self.__admin_window.humidity_orientation.currentIndex() == \ AdminView.__combo_box_above_index: setting[ "humidity_orientation"] = ControllerSetting.threshold_orientation_above else: setting[ "humidity_orientation"] = ControllerSetting.threshold_orientation_below self.__save_setting_callback(ControllerSetting(setting=setting)) def register_save_setting_callback(self, callback): """ Register save button callback :param callback: Slot function to be called on button click callback signature: callback(setting) """ if self.__admin_window: self.__save_setting_callback = callback self.__admin_window.save_settings_btn.clicked.connect( self.__save_setting_clicked) def register_control_callback(self, callback): """ Register callback for the manual control buttons :param callback: Slot function to be called on button click callback signature: callback(command) """ if self.__admin_window: self.__admin_window.heater_on_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_1_on)) self.__admin_window.heater_off_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_1_off)) self.__admin_window.heater_automatic_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_1_auto)) self.__admin_window.fan_on_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_2_on)) self.__admin_window.fan_off_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_2_off)) self.__admin_window.fan_automatic_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_2_auto)) def login_view_show_error(self, message=None): """ Show error message on login screen :param str message: Optional string, clears the status if None else sets it """ if self.__login_dialog: if message: self.__login_dialog.login_status.setText(message) else: self.__login_dialog.login_status.clear() def close_login_view(self): """ Hides the login view """ if self.__login_dialog: self.__login_dialog.hide() self.__login_dialog.deleteLater() def init_admin_view(self, setting): """ Initializes the admin view :param ControllerSetting setting: initial setting """ self.__admin_window = AdminMainWindow(self.__version) self.update_settings(setting) self.__admin_window.close_signal.connect(self.__close_action) self.__admin_window.show() def update_settings(self, setting): """ Update the thresholds on admin page spin box :param ControllerSetting setting: controller setting object """ if setting: self.__admin_window.temperature_threshold.setValue( setting.temperature_threshold) self.__admin_window.humidity_threshold.setValue( setting.humidity_threshold) self.__admin_window.temperature_read_interval.\ setValue(setting.temperature_read_interval) self.__admin_window.humidity_read_interval.setValue( setting.humidity_read_interval) self.__admin_window.temperature_read_delta.setValue( setting.temperature_read_delta) self.__admin_window.humidity_read_delta.setValue( setting.humidity_read_delta) self.__admin_window.sensor_heartbeat.setValue( setting.sensor_heartbeat) self.__admin_window.actuator_heartbeat.setValue( setting.actuator_heartbeat) self.__admin_window.controller_heartbeat.setValue( setting.controller_heartbeat) if setting.temperature_orientation == ControllerSetting.threshold_orientation_above: self.__admin_window.temperature_orientation.setCurrentIndex( AdminView.__combo_box_above_index) else: self.__admin_window.temperature_orientation.setCurrentIndex( AdminView.__combo_box_below_index) if setting.humidity_orientation == ControllerSetting.threshold_orientation_above: self.__admin_window.humidity_orientation.setCurrentIndex( AdminView.__combo_box_above_index) else: self.__admin_window.humidity_orientation.setCurrentIndex( AdminView.__combo_box_below_index) def __set_button_underline(self, button, enable): """ If enable is true, sets button text underline on; otherwise sets underline off. :param QPushButton button: button widget :param bool enable: whether to underline it """ font = button.font() font.setUnderline(enable) button.setFont(font) def update_manual_buttons_status(self, relay_type, relay_status, relay_mode): """ Underlines the appropriate button based on status and mode :param RelayDevicesEnum relay_type: which relay status to set, heater or fan :param bool relay_status: current status on or off :param str relay_mode: current mode "AUTO" or "MANUAL" """ if self.__admin_window: button = {} if relay_type == RelayDevicesEnum.heater: button = { "auto": self.__admin_window.heater_automatic_btn, "on": self.__admin_window.heater_on_btn, "off": self.__admin_window.heater_off_btn } elif relay_type == RelayDevicesEnum.fan: button = { "auto": self.__admin_window.fan_automatic_btn, "on": self.__admin_window.fan_on_btn, "off": self.__admin_window.fan_off_btn } else: LOGGER.error("Invalid relay type") if button: # clear the previous underlines and set again based on status, mode self.__set_button_underline(button["auto"], False) self.__set_button_underline(button["on"], False) self.__set_button_underline(button["off"], False) if relay_mode == "AUTO": self.__set_button_underline(button["auto"], True) elif relay_status: self.__set_button_underline(button["on"], True) else: self.__set_button_underline(button["off"], True) def update_status_bar(self, message): """ Update the admin view status bar with the specified message :param str message: message string """ if self.__admin_window: self.__admin_window.statusbar.showMessage(message) def set_connection_status(self, connection_status, color): """ Update the connection status label with the specified message :param str connection_status: current connection status (Network Down/ Internet Down/ OK) :param str color: color of the connection status(red/green) """ if self.__admin_window: self.__admin_window.connection_status.setText(connection_status) self.__admin_window.connection_status.setStyleSheet( 'color: {}'.format(color)) elif self.__login_dialog: # Ignore connection status = OK if connection_status != UIStrings.OK: self.__login_dialog.login_connection_status.setText( connection_status) @Slot() def __close_action(self): """ Slot for admin or login view close action """ self.close_signal.emit()
class AdminView(QObject): """ Acts as MVC view, controls the UI """ close_signal = Signal() __combo_box_above_index = 0 __combo_box_below_index = 1 def __init__(self, version=None, parent=None): """ AdminView constructor :param str version: version to be shown on window title """ super(AdminView, self).__init__(parent) self.__login_dialog = None self.__admin_window = None self.__save_setting_callback = None self.__version = version def init_login_view(self, user_names): """ Initializes the login view :param list user_names: list of user name strings, will be used for showing auto-completion """ self.__login_dialog = LoginDialog(user_names, self.__version) self.__login_dialog.close_signal.connect(self.__close_action) self.__login_dialog.show() def register_login_callback(self, callback): """ Register login button callback :param callback: Slot function to be called on button click callback signature: callback(user_name:str, password:str) """ if self.__login_dialog: self.__login_dialog.login.clicked.connect( lambda: callback(str(self.__login_dialog.user_name.text()), str(self.__login_dialog.password.text()))) def __save_setting_clicked(self): """ Local function called when save setting is clicked Creates ControllerSetting object and calls controller callback """ setting = {"temperature_threshold": self.__admin_window.temperature_threshold.value(), "humidity_threshold": self.__admin_window.humidity_threshold.value(), "temperature_read_interval": self.__admin_window.temperature_read_interval.value(), "humidity_read_interval": self.__admin_window.humidity_read_interval.value(), "temperature_read_delta": self.__admin_window.temperature_read_delta.value(), "humidity_read_delta": self.__admin_window.humidity_read_delta.value(), "sensor_heartbeat": self.__admin_window.sensor_heartbeat.value(), "actuator_heartbeat": self.__admin_window.actuator_heartbeat.value(), "controller_heartbeat": self.__admin_window.controller_heartbeat.value()} if self.__admin_window.temperature_orientation.currentIndex() == \ AdminView.__combo_box_above_index: setting["temperature_orientation"] = ControllerSetting.threshold_orientation_above else: setting["temperature_orientation"] = ControllerSetting.threshold_orientation_below if self.__admin_window.humidity_orientation.currentIndex() == \ AdminView.__combo_box_above_index: setting["humidity_orientation"] = ControllerSetting.threshold_orientation_above else: setting["humidity_orientation"] = ControllerSetting.threshold_orientation_below self.__save_setting_callback(ControllerSetting(setting=setting)) def register_save_setting_callback(self, callback): """ Register save button callback :param callback: Slot function to be called on button click callback signature: callback(setting) """ if self.__admin_window: self.__save_setting_callback = callback self.__admin_window.save_settings_btn.clicked.connect(self.__save_setting_clicked) def register_control_callback(self, callback): """ Register callback for the manual control buttons :param callback: Slot function to be called on button click callback signature: callback(command) """ if self.__admin_window: self.__admin_window.heater_on_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_1_on)) self.__admin_window.heater_off_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_1_off)) self.__admin_window.heater_automatic_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_1_auto)) self.__admin_window.fan_on_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_2_on)) self.__admin_window.fan_off_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_2_off)) self.__admin_window.fan_automatic_btn.clicked.\ connect(lambda: callback(ControllerCommandEnum.relay_2_auto)) def login_view_show_error(self, message=None): """ Show error message on login screen :param str message: Optional string, clears the status if None else sets it """ if self.__login_dialog: if message: self.__login_dialog.login_status.setText(message) else: self.__login_dialog.login_status.clear() def close_login_view(self): """ Hides the login view """ if self.__login_dialog: self.__login_dialog.hide() self.__login_dialog.deleteLater() def init_admin_view(self, setting): """ Initializes the admin view :param ControllerSetting setting: initial setting """ self.__admin_window = AdminMainWindow(self.__version) self.update_settings(setting) self.__admin_window.close_signal.connect(self.__close_action) self.__admin_window.show() def update_settings(self, setting): """ Update the thresholds on admin page spin box :param ControllerSetting setting: controller setting object """ if setting: self.__admin_window.temperature_threshold.setValue(setting.temperature_threshold) self.__admin_window.humidity_threshold.setValue(setting.humidity_threshold) self.__admin_window.temperature_read_interval.\ setValue(setting.temperature_read_interval) self.__admin_window.humidity_read_interval.setValue(setting.humidity_read_interval) self.__admin_window.temperature_read_delta.setValue(setting.temperature_read_delta) self.__admin_window.humidity_read_delta.setValue(setting.humidity_read_delta) self.__admin_window.sensor_heartbeat.setValue(setting.sensor_heartbeat) self.__admin_window.actuator_heartbeat.setValue(setting.actuator_heartbeat) self.__admin_window.controller_heartbeat.setValue(setting.controller_heartbeat) if setting.temperature_orientation == ControllerSetting.threshold_orientation_above: self.__admin_window.temperature_orientation.setCurrentIndex( AdminView.__combo_box_above_index) else: self.__admin_window.temperature_orientation.setCurrentIndex( AdminView.__combo_box_below_index) if setting.humidity_orientation == ControllerSetting.threshold_orientation_above: self.__admin_window.humidity_orientation.setCurrentIndex( AdminView.__combo_box_above_index) else: self.__admin_window.humidity_orientation.setCurrentIndex( AdminView.__combo_box_below_index) def __set_button_underline(self, button, enable): """ If enable is true, sets button text underline on; otherwise sets underline off. :param QPushButton button: button widget :param bool enable: whether to underline it """ font = button.font() font.setUnderline(enable) button.setFont(font) def update_manual_buttons_status(self, relay_type, relay_status, relay_mode): """ Underlines the appropriate button based on status and mode :param RelayDevicesEnum relay_type: which relay status to set, heater or fan :param bool relay_status: current status on or off :param str relay_mode: current mode "AUTO" or "MANUAL" """ if self.__admin_window: button = {} if relay_type == RelayDevicesEnum.heater: button = {"auto": self.__admin_window.heater_automatic_btn, "on": self.__admin_window.heater_on_btn, "off": self.__admin_window.heater_off_btn} elif relay_type == RelayDevicesEnum.fan: button = {"auto": self.__admin_window.fan_automatic_btn, "on": self.__admin_window.fan_on_btn, "off": self.__admin_window.fan_off_btn} else: LOGGER.error("Invalid relay type") if button: # clear the previous underlines and set again based on status, mode self.__set_button_underline(button["auto"], False) self.__set_button_underline(button["on"], False) self.__set_button_underline(button["off"], False) if relay_mode == "AUTO": self.__set_button_underline(button["auto"], True) elif relay_status: self.__set_button_underline(button["on"], True) else: self.__set_button_underline(button["off"], True) def update_status_bar(self, message): """ Update the admin view status bar with the specified message :param str message: message string """ if self.__admin_window: self.__admin_window.statusbar.showMessage(message) def set_connection_status(self, connection_status, color): """ Update the connection status label with the specified message :param str connection_status: current connection status (Network Down/ Internet Down/ OK) :param str color: color of the connection status(red/green) """ if self.__admin_window: self.__admin_window.connection_status.setText(connection_status) self.__admin_window.connection_status.setStyleSheet('color: {}'.format(color)) elif self.__login_dialog: # Ignore connection status = OK if connection_status != UIStrings.OK: self.__login_dialog.login_connection_status.setText(connection_status) @Slot() def __close_action(self): """ Slot for admin or login view close action """ self.close_signal.emit()
class DisplayView(QObject): """ Acts as MVC view, controls the UI """ close_signal = Signal() def __init__(self, version=None, parent=None): """ DisplayView constructor :param str version: version to be shown on window title """ super(DisplayView, self).__init__(parent) self.__login_dialog = None self.__display_window = None self.__version = version def init_login_view(self, user_names): """ Initializes the login view """ self.__login_dialog = LoginDialog(user_names, self.__version) self.__login_dialog.close_signal.connect(self.__close_action) self.__login_dialog.show() def register_login_callback(self, callback): """ Register login button callback :param callback: Slot function to be called on button click callback signature: callback(user_name:str, password:str) """ if self.__login_dialog: self.__login_dialog.login.clicked.connect( lambda: callback(str(self.__login_dialog.user_name.text()), str(self.__login_dialog.password.text()))) def login_view_show_error(self, message=None): """ Show error message on login screen :param str message: Optional string, clears the status if None else sets it """ if self.__login_dialog: if message: self.__login_dialog.login_status.setText(message) else: self.__login_dialog.login_status.clear() def close_login_view(self): """ Hides the login view """ if self.__login_dialog: self.__login_dialog.hide() self.__login_dialog.deleteLater() def update_status_bar(self, message): """ Update the display view status bar with the specified message :param str message: message string """ if self.__display_window: self.__display_window.statusbar.showMessage(message) def set_connection_status(self, connection_status, color): """ Update the connection status label with the specified message :param str connection_status: current connection status (Network Down/ Internet Down/ OK) :param str color: color of the connection status(red/green) """ if self.__display_window: self.__display_window.connection_status.setText(connection_status) self.__display_window.connection_status.setStyleSheet( "color: {}".format(color)) elif self.__login_dialog: # Ignore connection status = OK if connection_status != UIStrings.OK: self.__login_dialog.login_connection_status.setText( connection_status) def init_display_view(self, setting): """ Initializes display window Currently showing only thresholds from the settings :param ControllerSetting setting: initial settings """ self.__display_window = DisplayMainWindow(self.__version) self.__display_window.close_signal.connect(self.__close_action) self.update_settings(setting) # add graph plot self.__display_window.temperature_figure = matplotlib.figure.Figure( facecolor="white", edgecolor="black") self.__display_window.temperature_canvas = figure_canvas( self.__display_window.temperature_figure) self.__display_window.graph_layout.addWidget( self.__display_window.temperature_canvas) self.__display_window.humidity_figure = matplotlib.figure.Figure( facecolor="white", edgecolor="black") self.__display_window.humidity_canvas = figure_canvas( self.__display_window.humidity_figure) self.__display_window.humidity_graph_layout.addWidget( self.__display_window.humidity_canvas) self.__init_graph() # Show display window self.__display_window.show() def update_settings(self, setting): """ Update the thresholds on display page :param ControllerSetting setting: controller setting object """ if setting: self.__display_window.temperature_threshold.\ setText("{:.2f}".format(setting.temperature_threshold)) self.__display_window.humidity_threshold.\ setText("{:.2f}".format(setting.humidity_threshold)) def plot_graph(self, temperature_data, humidity_data): """ Plot time vs temperature graph and time vs humidity graph :param dict temperature_data: key-time, value-temperature :param dict humidity_data: key- time, value- temperature """ self.__display_window.temperature_value.\ setText("{:.2f}".format(temperature_data[temperature_data.keys()[-1]])) self.__display_window.humidity_value.\ setText("{:.2f}".format(humidity_data[humidity_data.keys()[-1]])) self.__init_graph() self.__update_graph(temperature_data, humidity_data) def __init_graph(self): """ Initializes graph parameters """ # initialize temperature graph if self.__display_window.temperature_axes: self.__display_window.temperature_axes.clear() if self.__display_window.temperature_figure: self.__display_window.temperature_figure.clf() self.__display_window.temperature_axes = self.__display_window.temperature_figure.\ add_subplot(1, 1, 1) self.__display_window.temperature_axes.grid(True) self.__display_window.temperature_axes.set_ylim( MIN_TEMPERATURE, MAX_TEMPERATURE) self.__display_window.temperature_axes.set_ylabel("Temperature (" + DEGREE_CELSIUS + ")", fontsize=FONT_SIZE) self.__display_window.temperature_axes.tick_params(axis="both", which="major", labelsize=FONT_SIZE) self.__display_window.temperature_canvas.draw() # initialize humidity graph if self.__display_window.humidity_axes: self.__display_window.humidity_axes.clear() if self.__display_window.humidity_figure: self.__display_window.humidity_figure.clf() self.__display_window.humidity_axes = self.__display_window.humidity_figure.\ add_subplot(1, 1, 1) self.__display_window.humidity_axes.grid(True) self.__display_window.humidity_axes.set_ylim(MIN_HUMIDITY, MAX_HUMIDITY) self.__display_window.humidity_axes.set_ylabel("Humidity (%RH)", fontsize=FONT_SIZE) self.__display_window.humidity_axes.tick_params(axis="both", which="major", labelsize=FONT_SIZE) self.__display_window.humidity_canvas.draw() def __update_graph(self, temperature_data, humidity_data): """ Update graph with latest measurements :param dict temperature_data: key-time, value-temperature """ # Clipping out of range temperature values to min and max values for key, _ in temperature_data.items(): if temperature_data[key] > MAX_TEMPERATURE: temperature_data[key] = MAX_TEMPERATURE elif temperature_data[key] < MIN_TEMPERATURE: temperature_data[key] = MIN_TEMPERATURE # Clipping out of range humidity values to min and max values for key, _ in humidity_data.items(): if humidity_data[key] > MAX_HUMIDITY: humidity_data[key] = MAX_HUMIDITY elif humidity_data[key] < MIN_HUMIDITY: humidity_data[key] = MIN_HUMIDITY # update temperature graph x_axis_data = temperature_data.keys() y_axis_data = temperature_data.values() self.__display_window.temperature_axes.plot(x_axis_data, y_axis_data, lw=3.0, label="Temperature", color="blue") if self.__display_window.temperature_threshold.text(): temperature_threshold = self.__display_window.temperature_threshold.text( ) y_axis_data = [temperature_threshold] * len( temperature_data.keys()) self.__display_window.temperature_axes.\ plot(x_axis_data, y_axis_data, color="red", linestyle="dashed", label="Temperature Threshold", lw=3.0) self.__display_window.temperature_axes.legend( bbox_to_anchor=(0, 1), numpoints=1, loc="lower left", fontsize=LEGEND_FONT_SIZE, borderaxespad=0) self.__display_window.temperature_figure.autofmt_xdate() self.__display_window.temperature_axes.xaxis.set_major_formatter( DateFormatter(TIME_FMT)) self.__display_window.temperature_axes.xaxis.set_major_locator( LinearLocator()) self.__display_window.temperature_canvas.draw() # update humidity graph self.__display_window.humidity_axes.plot(humidity_data.keys(), humidity_data.values(), lw=3.0, label="Humidity", color="green") if self.__display_window.humidity_threshold.text(): humidity_threshold = self.__display_window.humidity_threshold.text( ) y_axis_data = [humidity_threshold] * len(humidity_data.keys()) self.__display_window.humidity_axes.plot( humidity_data.keys(), y_axis_data, color="red", linestyle="dashed", label="Humidity Threshold", lw=3.0) self.__display_window.humidity_axes.legend(bbox_to_anchor=(0, 1), numpoints=1, loc="lower left", fontsize=LEGEND_FONT_SIZE, borderaxespad=0) self.__display_window.humidity_figure.autofmt_xdate() self.__display_window.humidity_axes.xaxis.set_major_formatter( DateFormatter(TIME_FMT)) self.__display_window.humidity_axes.xaxis.set_major_locator( LinearLocator()) self.__display_window.humidity_canvas.draw() def __update_device_status_label(self, widget, device_on): """ Update device status label on screen :param QLabel widget: pyside QLabel widget for text display :param bool device_on: True if device is "ONLINE" """ if device_on: widget.setText(UIStrings.DEVICE_ONLINE) widget.setStyleSheet('color: green') else: widget.setText(UIStrings.DEVICE_OFFLINE) widget.setStyleSheet('color: red') def update_device_status(self, device_type, device_on): """ Update device status :param DeviceEnum device_type: device type e.g sensor or controller :param bool device_on: True if device is "ONLINE" """ if self.__display_window: if device_type == DeviceEnum.controller: self.__update_device_status_label( self.__display_window.controller_status, device_on) elif device_type == DeviceEnum.sensor: self.__update_device_status_label( self.__display_window.sensor_status, device_on) elif device_type == DeviceEnum.actuator: self.__update_device_status_label( self.__display_window.actuator_status, device_on) else: LOGGER.error("{} device doesn't exist".format(device_type)) def update_relay_status(self, event): """ Update relay information on display like status, mode etc :param RelayStatusEvent event: RelayStatusEvent object """ self.__display_window.heater_status.setText( UIStrings.RELAY_ON if event.relay_1_on else UIStrings.RELAY_OFF) self.__display_window.fan_status.setText( UIStrings.RELAY_ON if event.relay_2_on else UIStrings.RELAY_OFF) self.__display_window.heater_mode.setText( UIStrings.RELAY_AUTO if event.relay_1_mode == "AUTO" else UIStrings.RELAY_MANUAL) self.__display_window.fan_mode.setText( UIStrings.RELAY_AUTO if event.relay_2_mode == "AUTO" else UIStrings.RELAY_MANUAL) def update_latency(self, latency): """ Update latency information on display screen :param float latency: latency value, if None set latency as "NA" """ if self.__display_window: if latency: if latency > MAX_LATENCY_VALUE: # Avoid showing this value as it is likely to be incorrect latency value self.__display_window.latency.setText( "> {} sec".format(MAX_LATENCY_VALUE)) else: self.__display_window.latency.setText( "{:.2f} sec".format(latency)) else: self.__display_window.latency.setText("NA") def __close_action(self): """ Slot for display or login view close action """ self.close_signal.emit()