def __init__(self, qtui, settings): super().__init__(qtui, settings) self._default_min_max_text = self._ui.forecast_d1_day_temps_value.text( ) # set default day night icons - sunny clear day_icon = get_asset_file("weather_icons", "day-800") # night-clear night_icon = get_asset_file("weather_icons", "night-800") self._ui.forecast_background.setPixmap( QtGui.QPixmap( str(config.assets_path / "gui_bgrs" / settings.get(FORECAST_BG)))) common.draw_svg(self._ui.forecast_d1_day_icon, day_icon) common.draw_svg(self._ui.forecast_d2_day_icon, day_icon) common.draw_svg(self._ui.forecast_d3_day_icon, day_icon) common.draw_svg(self._ui.forecast_d1_night_icon, night_icon) common.draw_svg(self._ui.forecast_d2_night_icon, night_icon) common.draw_svg(self._ui.forecast_d3_night_icon, night_icon) self._init_dummy_values() # connect daily detail view self._ui.forecast_d1_icon.clicked.connect(lambda: self.show_detail(1)) self._ui.forecast_d2_icon.clicked.connect(lambda: self.show_detail(2)) self._ui.forecast_d3_icon.clicked.connect(lambda: self.show_detail(3)) # call once at begin self.init_with_cyclic_update()
def _get_condition_icon(ident, is_day) -> Path: if is_day: ident = "day-" + str(ident) else: ident = "night-" + str(ident) file_path = get_asset_file("weather_icons", ident) # normalize to lower case if not file_path: file_path = get_asset_file("gui_base", "dummy.png") return file_path
def _init_dummy_values(self): dummy_icon = str(get_asset_file("gui_base", "dummy-pic")) self._ui.forecast_d1_icon.setPixmap(QtGui.QPixmap(dummy_icon)) self._ui.forecast_d2_icon.setPixmap(QtGui.QPixmap(dummy_icon)) self._ui.forecast_d3_icon.setPixmap(QtGui.QPixmap(dummy_icon)) # set day temps to NA self._ui.forecast_d1_day_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None)) self._ui.forecast_d2_day_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None)) self._ui.forecast_d3_day_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None)) # set night temps to NA self._ui.forecast_d1_night_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None)) self._ui.forecast_d2_night_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None)) self._ui.forecast_d3_night_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None))
def parse_event_file(events_file_path: Path) -> List[Event]: """ Parse the json config file, validate and convert to object structure """ events: List[Event] = [] Logger().info(f"EventHandler: Loading file '{events_file_path}'...") if not events_file_path.is_file(): Logger().warning( f"EventHandler: Config file '{events_file_path}' does not exist.") return [] with open(events_file_path, encoding="utf-8") as config_file: try: events_config = json.load(config_file) with open(get_asset_file("base", "events_schema")) as schema_file: json_schema = json.load(schema_file) jsonschema.validate(instance=events_config, schema=json_schema) except BaseException as error: Logger().error(f"EventHandler: Config file:\n{str(error)}") return [] for event_data in events_config.get("events", []): event = Event(event_data.get("name", "")) event.triggers = event_data.get("triggers", "") event.actions = event_data.get("actions", "") event.recurrence = event_data.get("recurrence", "") event.date = event_data.get("date", "") event.last_triggered = event_data.get("last_triggered", "") events.append(event) return events
def _change_background(self, file_id: str): """ Slot for change background signal """ background = get_asset_file("gui_base", file_id) if background and background.is_file(): while not self.ready: time.sleep(1) if not self._ui: return self._ui.interior_background.setPixmap( QtGui.QPixmap(str(background)))
def get_temperature_icon(temp_value: Optional[float]) -> Path: """ Return the path of the image resource for the appropriate temperature input. t < 0: empty t < 10: low t < 22: half t < 30 high t > 30: full """ assets_subfolder = "weather_icons" # set dummy as default icon_path = get_asset_file(assets_subfolder, "thermometer_empty") # return dummy for invalid value if not temp_value: return icon_path # set up ranges for the 5 icons if temp_value <= 0: icon_path = get_asset_file(assets_subfolder, "thermometer_empty") elif temp_value < 10: icon_path = get_asset_file( assets_subfolder, "thermometer_almost_empty") elif temp_value < 22: icon_path = get_asset_file(assets_subfolder, "thermometer_half") elif temp_value < 30: icon_path = get_asset_file( assets_subfolder, "thermometer_almost_full") else: icon_path = get_asset_file(assets_subfolder, "thermometer_full") return icon_path
def get_font(font_name) -> QtGui.QFont: # set up font font_file = get_asset_file("font", "franzo") font_id = QtGui.QFontDatabase.addApplicationFont(str(font_file)) if not config.qt_app: return QtGui.QFont() font = config.qt_app.font() if font_id != -1: font_db = QtGui.QFontDatabase() font_styles = font_db.styles(font_name) font_families = QtGui.QFontDatabase.applicationFontFamilies(font_id) if font_families: font = font_db.font(font_families[0], font_styles[0], 13) else: logger.warning("Can't apply selected font file.") return font
def show_info_screen(self): """ Shows the user help overlay (opaque).""" if not self._ui: return self._ui.overlay_background.setPixmap( QtGui.QPixmap(str(get_asset_file("gui_base", "info_overlay")))) self._ui.overlay_background.raise_() self._ui.overlay_background.show() self._ui.ol_sensors_label.raise_() self._ui.ol_sensors_label.show() self._ui.ol_wh_today_label.raise_() self._ui.ol_wh_today_label.show() self._ui.ol_wh_forecast_label.raise_() self._ui.ol_wh_forecast_label.show() self._ui.ol_title_label.raise_() self._ui.ol_title_label.show()
def get_tts_string(self, key: str, lang="en") -> str: if lang in LANGS_MAP: lang = LANGS_MAP.get(lang) dict_file = get_asset_file("base", "tts_dict") # read filetoc.json with open(str(dict_file), encoding='utf-8') as f: tts_dict = json.load(f) # get filetype and filelist lang_dict = tts_dict.get(lang) if not lang_dict: self._logger.error(f"TTS: Cannot find language string for {lang}") return "" value = lang_dict.get(key) if not value: Logger().error("Cannot find resource id %s in catalog", key) return value
def qt_app_setup(settings) -> QtWidgets.QApplication: """ Set up all Qt application specific attributes, which can't be changed later on Returns qt_app object. """ # apply Qt attributes (only at init possible) QtWidgets.QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) QtWidgets.QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) # set up global Qt Application instance qt_app = QtWidgets.QApplication([]) # set icon icon_path = get_asset_file("gui_base", "icon") qt_app.setWindowIcon(QtGui.QIcon(str(icon_path))) # install translator common.set_ui_language(qt_app, settings) return qt_app
def __init__(self, main_ui, settings): super().__init__(main_ui, settings) self._online_info_date_time = None # date when last online update occured # save default text to restore formatting self._default_temp_text = self._ui.exterior_temp_value.text() day_icon = get_asset_file("weather_icons", "day-800") common.draw_svg(self._ui.exterior_forecast_icon, day_icon, scale=3) self._ui.exterior_forecast_icon.clicked.connect(self.show_detail) # set day temps to NA self._default_min_max_text = self._ui.exterior_forecast_temps_value.text( ) self._ui.exterior_forecast_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, None, None)) # call once at begin self.init_with_cyclic_update()
def __init__(self, background=True): """ background: set the image as the background. The layout changes if background ist set. Without: the spinner plays in the middle. A screenshot is taken when the splashcreen initializes and set as background, so changing elements in the gui are not seen by the user. With: background image is scaled and spinner plays in the bottom middle. """ self._is_background_set = background self._label = None self._spinner = None if background: pixmap = QtGui.QPixmap(str(get_asset_file("gui_base", "loading"))) pixmap = pixmap.scaled(800, 480, transformMode=Qt.SmoothTransformation) else: if not config.qt_app: return # can not really happen... screen = config.qt_app.primaryScreen() pixmap = screen.grabWindow(voidptr(0)) QtWidgets.QSplashScreen.__init__(self, pixmap) if config.DEBUG_LEVEL > 0: # unlock gui when debugging self.setWindowFlags(Qt.FramelessWindowHint)
def write_events_file(events_file_path: Path, events: List[Event]): """ Write out the json dict from model """ events_data = [] for event in events: event_data = { "name": event.name, "recurrence": event.recurrence, "date": event.date, "triggers": event.triggers, "actions": event.actions, "last_triggered": event.last_triggered } events_data.append(event_data) # get last version with open(get_asset_file("base", "events_schema")) as schema_file: json_schema = json.load(schema_file) version = json_schema.get("properties").get("version").get("enum")[-1] events_config = {"version": version, "events": events_data} try: with open(events_file_path, "w") as config_file: json.dump(events_config, config_file, indent=4) except Exception: Logger().error("EventHandler: Cannot open events.json file")
def _cyclic_update(self): self._logger.debug("ExteriorGui: update") if not self._comps.weather_info: return forecast = self._comps.weather_info.get_5_day_forecast() current_weather = self._comps.weather_info.get_current_weather() if not current_weather or not forecast: # handling for no connection - throw away value after an hour if not self._online_info_date_time: # never got any value online_temp_value = None else: # look, if last value is older than 5 minutes current_date_time = datetime.datetime.now() time_delta = current_date_time - self._online_info_date_time if time_delta.seconds > 300: # 5 minutes online_temp_value = None else: return # forecast still valid, don't change displayed value else: self._online_info_date_time = current_weather.fetch_time online_temp_value = current_weather.temp # for later use online_weather_desc = current_weather.description self._logger.debug("ExteriorGui: Current weather condition is %s", online_weather_desc) # set weather description specific background image online_weather_category = current_weather.main.lower() cloudiness = current_weather.clouds if cloudiness > 65 and online_weather_category == "clouds": online_weather_category = "heavy_clouds" if current_weather.is_daytime(): bg_name = "bg_day_" + online_weather_category else: bg_name = "bg_night_" + online_weather_category self._ui.exterior_background.set_image( str(get_asset_file("weather_bgrs", bg_name))) # set todays forecast if not forecast[0]: return common.draw_svg(self._ui.exterior_forecast_icon, forecast[0].icon, scale=3) if not current_weather.is_daytime(): temp_min = forecast[0].temp_night_min temp_max = forecast[0].temp_night_max if temp_min == -float("inf") or temp_max == float("inf"): temp_min = forecast[0].temp_min temp_max = forecast[0].temp_max else: temp_min = forecast[0].temp_min temp_max = forecast[0].temp_max if temp_min == -float("inf") or temp_max == float("inf"): temp_min = forecast[0].temp_night_min temp_max = forecast[0].temp_night_max self._ui.exterior_forecast_temps_value.setText( common.format_temp_text_minmax(self._default_min_max_text, temp_min, temp_max)) # if sensor is not available switch to online data temp_value = online_temp_value if self._comps.remote_temp_sensor and self._comps.remote_temp_sensor.is_active: temp_value = self._comps.remote_temp_sensor.get_temperature() # format and set values to temperature display temp_val_text = common.format_float_temp_text(self._default_temp_text, temp_value) self._ui.exterior_temp_value.setText(temp_val_text) # set temperature icon common.draw_svg(self._ui.exterior_temp_icon, common.get_temperature_icon(temp_value))
def __init__(self, main_ui: "WeatherMainUi", comp_ctrl: ComponentController, settings: Settings): super().__init__() self._main_ui = main_ui # reference to main ui instance self._comp_ctrl = comp_ctrl self._comps = comp_ctrl.components self._settings = settings self._previous_scaling = self._settings.get(FONT_SCALING) self._runtime_system = RuntimeSystem() # create qt base objects self.setWindowFlags( Qt.WindowType(Qt.CustomizeWindowHint | Qt.FramelessWindowHint)) ui_type = uic.loadUiType(config.base_path / "ui" / "qt" / "options.ui") self._ui = ui_type[0]() # 0th element is always the UI class self._ui.setupUi(self) self.setGeometry(main_ui.geometry()) # start fader - variable must be held otherwise gc will claim it fader_widget = FaderWidget(main_ui, self) # pylint: disable=unused-variable # set version label self._ui.version_label.setText(WAQD_VERSION) # set up fix background image self._ui.background_label.setPixmap( QtGui.QPixmap(str(get_asset_file("gui_base", "background-full")))) # display current options self.display_options() # connect buttons on main tab self._ui.ok_button.clicked.connect(self.apply_options) self._ui.cancel_button.clicked.connect(self.close_ui) self._ui.shutdown_button.clicked.connect(self.call_shutdown) self._ui.restart_button.clicked.connect(self.call_restart) self._ui.connect_wlan_button.clicked.connect(self.connect_wlan) self._ui.lang_cbox.currentTextChanged.connect( self._update_language_cbox) self._ui.forecast_background_cbox.currentTextChanged.connect( self._update_preview_forecast) self._ui.interior_background_cbox.currentTextChanged.connect( self._update_preview_interior) self._ui.font_cbox.currentFontChanged.connect(self._update_font) # connect elements on energy saver tab self._ui.night_mode_begin_slider.valueChanged.connect( self._update_night_mode_slider) self._ui.night_mode_begin_slider.sliderMoved.connect( self._update_night_mode_slider) self._ui.night_mode_end_slider.valueChanged.connect( self._update_night_mode_slider) self._ui.night_mode_end_slider.sliderMoved.connect( self._update_night_mode_slider) self._ui.brightness_slider.valueChanged.connect( self._update_brightness_slider) self._ui.brightness_slider.sliderMoved.connect( self._update_brightness_slider) self._ui.motion_sensor_enable_toggle.toggled.connect( self._update_motion_sensor_enabled) # set starting tab to first tab self._ui.options_tabs.setCurrentIndex(0) # set to normal brightness self._comps.display.set_brightness(self._settings.get_int(BRIGHTNESS)) common.scale_gui_elements( self, self._settings.get_float(FONT_SCALING) * self.EXTRA_SCALING) # initialize splash screen for the closing of the UI and make a screenshot self._splash_screen = SplashScreen(background=False) # minimal wait to show the button feedback time.sleep(0.3) self.show()