From 38266816852c21224b0c593cfd1e58791331ec7c Mon Sep 17 00:00:00 2001 From: alessandro90 Date: Wed, 1 May 2019 19:50:03 +0200 Subject: [PATCH] Fixes bug in forecast thread (close app while downloading). Also makes some style adjustments --- artemis.py | 612 ++++++++++++++++++++--------------- audio_player.py | 37 ++- clickable_progress_bar.py | 3 +- double_text_button.py | 12 +- download_window.py | 12 +- fixed_aspect_ratio_label.py | 5 +- fixed_aspect_ratio_widget.py | 3 +- requirements_win.txt | 6 + space_weather_data.py | 52 +-- switchable_label.py | 3 +- themes.py | 275 +++++++++++----- threads.py | 70 ++-- utilities.py | 18 +- 13 files changed, 670 insertions(+), 438 deletions(-) diff --git a/artemis.py b/artemis.py index f03ecf9..56ea329 100644 --- a/artemis.py +++ b/artemis.py @@ -6,6 +6,7 @@ import sys from time import sleep from pandas import read_csv + from PyQt5.QtWidgets import (QMainWindow, QApplication, qApp, @@ -54,6 +55,7 @@ class Artemis(QMainWindow, Ui_MainWindow): super().__init__() self.setupUi(self) self.set_initial_size() + self.closing = False self.download_window = DownloadWindow() self.download_window.complete.connect(self.show_downloaded_signals) self.actionExit.triggered.connect(qApp.quit) @@ -63,61 +65,75 @@ class Artemis(QMainWindow, Ui_MainWindow): self.current_signal_name = '' self.signal_names = [] self.total_signals = 0 - self.switchable_r_labels = SwitchableLabelsIterable(self.r0_now_lbl, - self.r1_now_lbl, - self.r2_now_lbl, - self.r3_now_lbl, - self.r4_now_lbl, - self.r5_now_lbl,) + self.switchable_r_labels = SwitchableLabelsIterable( + self.r0_now_lbl, + self.r1_now_lbl, + self.r2_now_lbl, + self.r3_now_lbl, + self.r4_now_lbl, + self.r5_now_lbl + ) - self.switchable_s_labels = SwitchableLabelsIterable(self.s0_now_lbl, - self.s1_now_lbl, - self.s2_now_lbl, - self.s3_now_lbl, - self.s4_now_lbl, - self.s5_now_lbl,) + self.switchable_s_labels = SwitchableLabelsIterable( + self.s0_now_lbl, + self.s1_now_lbl, + self.s2_now_lbl, + self.s3_now_lbl, + self.s4_now_lbl, + self.s5_now_lbl + ) - self.switchable_g_now_labels = SwitchableLabelsIterable(self.g0_now_lbl, - self.g1_now_lbl, - self.g2_now_lbl, - self.g3_now_lbl, - self.g4_now_lbl, - self.g5_now_lbl) + self.switchable_g_now_labels = SwitchableLabelsIterable( + self.g0_now_lbl, + self.g1_now_lbl, + self.g2_now_lbl, + self.g3_now_lbl, + self.g4_now_lbl, + self.g5_now_lbl + ) - self.switchable_g_today_labels = SwitchableLabelsIterable(self.g0_today_lbl, - self.g1_today_lbl, - self.g2_today_lbl, - self.g3_today_lbl, - self.g4_today_lbl, - self.g5_today_lbl) + self.switchable_g_today_labels = SwitchableLabelsIterable( + self.g0_today_lbl, + self.g1_today_lbl, + self.g2_today_lbl, + self.g3_today_lbl, + self.g4_today_lbl, + self.g5_today_lbl + ) - self.k_storm_labels = SwitchableLabelsIterable(self.k_ex_sev_storm_lbl, - self.k_very_sev_storm_lbl, - self.k_sev_storm_lbl, - self.k_maj_storm_lbl, - self.k_min_storm_lbl, - self.k_active_lbl, - self.k_unsettled_lbl, - self.k_quiet_lbl, - self.k_very_quiet_lbl, - self.k_inactive_lbl) + self.k_storm_labels = SwitchableLabelsIterable( + self.k_ex_sev_storm_lbl, + self.k_very_sev_storm_lbl, + self.k_sev_storm_lbl, + self.k_maj_storm_lbl, + self.k_min_storm_lbl, + self.k_active_lbl, + self.k_unsettled_lbl, + self.k_quiet_lbl, + self.k_very_quiet_lbl, + self.k_inactive_lbl + ) - self.a_storm_labels = SwitchableLabelsIterable(self.a_sev_storm_lbl, - self.a_maj_storm_lbl, - self.a_min_storm_lbl, - self.a_active_lbl, - self.a_unsettled_lbl, - self.a_quiet_lbl) + self.a_storm_labels = SwitchableLabelsIterable( + self.a_sev_storm_lbl, + self.a_maj_storm_lbl, + self.a_min_storm_lbl, + self.a_active_lbl, + self.a_unsettled_lbl, + self.a_quiet_lbl + ) - self.forecast_labels = (self.forecast_lbl_0, - self.forecast_lbl_1, - self.forecast_lbl_2, - self.forecast_lbl_3, - self.forecast_lbl_4, - self.forecast_lbl_5, - self.forecast_lbl_6, - self.forecast_lbl_7, - self.forecast_lbl_8) + self.forecast_labels = ( + self.forecast_lbl_0, + self.forecast_lbl_1, + self.forecast_lbl_2, + self.forecast_lbl_3, + self.forecast_lbl_4, + self.forecast_lbl_5, + self.forecast_lbl_6, + self.forecast_lbl_7, + self.forecast_lbl_8 + ) for lab in self.forecast_labels: lab.set_default_stylesheet() @@ -141,37 +157,37 @@ class Artemis(QMainWindow, Ui_MainWindow): ) connect_events_to_func( - events_to_connect = [self.lower_freq_spinbox.valueChanged, - self.upper_freq_spinbox.valueChanged, - self.lower_freq_filter_unit.currentTextChanged, - self.upper_freq_filter_unit.currentTextChanged, - self.activate_low_freq_filter_btn.toggled], - fun_to_connect = self.set_min_value_upper_limit, - fun_args = [self.lower_freq_filter_unit, - self.lower_freq_spinbox, - self.upper_freq_filter_unit, - self.upper_freq_spinbox] + events_to_connect=[self.lower_freq_spinbox.valueChanged, + self.upper_freq_spinbox.valueChanged, + self.lower_freq_filter_unit.currentTextChanged, + self.upper_freq_filter_unit.currentTextChanged, + self.activate_low_freq_filter_btn.toggled], + fun_to_connect=self.set_min_value_upper_limit, + fun_args=[self.lower_freq_filter_unit, + self.lower_freq_spinbox, + self.upper_freq_filter_unit, + self.upper_freq_spinbox] ) connect_events_to_func( - events_to_connect = [self.lower_freq_spinbox.valueChanged, - self.upper_freq_spinbox.valueChanged, - self.lower_freq_filter_unit.currentTextChanged, - self.upper_freq_filter_unit.currentTextChanged, - self.activate_low_freq_filter_btn.clicked, - self.activate_up_freq_filter_btn.clicked, - self.lower_freq_confidence.valueChanged, - self.upper_freq_confidence.valueChanged], - fun_to_connect = self.set_band_filter_label, - fun_args = [self.activate_low_freq_filter_btn, - self.lower_freq_spinbox, - self.lower_freq_filter_unit, - self.lower_freq_confidence, - self.activate_up_freq_filter_btn, - self.upper_freq_spinbox, - self.upper_freq_filter_unit, - self.upper_freq_confidence, - self.freq_range_lbl] + events_to_connect=[self.lower_freq_spinbox.valueChanged, + self.upper_freq_spinbox.valueChanged, + self.lower_freq_filter_unit.currentTextChanged, + self.upper_freq_filter_unit.currentTextChanged, + self.activate_low_freq_filter_btn.clicked, + self.activate_up_freq_filter_btn.clicked, + self.lower_freq_confidence.valueChanged, + self.upper_freq_confidence.valueChanged], + fun_to_connect=self.set_band_filter_label, + fun_args=[self.activate_low_freq_filter_btn, + self.lower_freq_spinbox, + self.lower_freq_filter_unit, + self.lower_freq_confidence, + self.activate_up_freq_filter_btn, + self.upper_freq_spinbox, + self.upper_freq_filter_unit, + self.upper_freq_confidence, + self.freq_range_lbl] ) self.activate_low_freq_filter_btn.toggled.connect( @@ -192,62 +208,64 @@ class Artemis(QMainWindow, Ui_MainWindow): self.apply_remove_freq_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) self.apply_remove_freq_filter_btn.set_slave_filters( - [ + simple_ones=[ *self.frequency_filters_btns, self.include_undef_freqs, self.activate_low_freq_filter_btn, - self.activate_up_freq_filter_btn, + self.activate_up_freq_filter_btn ], - self.activate_low_freq_filter_btn, - [ + radio_1=self.activate_low_freq_filter_btn, + ruled_by_radio_1=[ self.lower_freq_spinbox, self.lower_freq_filter_unit, - self.lower_freq_confidence, + self.lower_freq_confidence ], - self.activate_up_freq_filter_btn, - [ + radio_2=self.activate_up_freq_filter_btn, + ruled_by_radio_2=[ self.upper_freq_spinbox, self.upper_freq_filter_unit, - self.upper_freq_confidence, - ], + self.upper_freq_confidence + ] ) self.apply_remove_freq_filter_btn.clicked.connect(self.display_signals) - self.reset_frequency_filters_btn.clicked.connect(partial(self.reset_fb_filters, Ftype.FREQ)) + self.reset_frequency_filters_btn.clicked.connect( + partial(self.reset_fb_filters, Ftype.FREQ) + ) # Manage bandwidth filters. connect_events_to_func( - events_to_connect = [self.lower_band_spinbox.valueChanged, - self.upper_band_spinbox.valueChanged, - self.lower_band_filter_unit.currentTextChanged, - self.upper_band_filter_unit.currentTextChanged, - self.activate_low_band_filter_btn.toggled], + events_to_connect=[self.lower_band_spinbox.valueChanged, + self.upper_band_spinbox.valueChanged, + self.lower_band_filter_unit.currentTextChanged, + self.upper_band_filter_unit.currentTextChanged, + self.activate_low_band_filter_btn.toggled], fun_to_connect = self.set_min_value_upper_limit, - fun_args = [self.lower_band_filter_unit, - self.lower_band_spinbox, - self.upper_band_filter_unit, - self.upper_band_spinbox] + fun_args=[self.lower_band_filter_unit, + self.lower_band_spinbox, + self.upper_band_filter_unit, + self.upper_band_spinbox] ) connect_events_to_func( - events_to_connect = [self.lower_band_spinbox.valueChanged, - self.upper_band_spinbox.valueChanged, - self.lower_band_filter_unit.currentTextChanged, - self.upper_band_filter_unit.currentTextChanged, - self.activate_low_band_filter_btn.clicked, - self.activate_up_band_filter_btn.clicked, - self.lower_band_confidence.valueChanged, - self.upper_band_confidence.valueChanged], - fun_to_connect = self.set_band_filter_label, - fun_args = [self.activate_low_band_filter_btn, - self.lower_band_spinbox, - self.lower_band_filter_unit, - self.lower_band_confidence, - self.activate_up_band_filter_btn, - self.upper_band_spinbox, - self.upper_band_filter_unit, - self.upper_band_confidence, - self.band_range_lbl] + events_to_connect=[self.lower_band_spinbox.valueChanged, + self.upper_band_spinbox.valueChanged, + self.lower_band_filter_unit.currentTextChanged, + self.upper_band_filter_unit.currentTextChanged, + self.activate_low_band_filter_btn.clicked, + self.activate_up_band_filter_btn.clicked, + self.lower_band_confidence.valueChanged, + self.upper_band_confidence.valueChanged], + fun_to_connect=self.set_band_filter_label, + fun_args=[self.activate_low_band_filter_btn, + self.lower_band_spinbox, + self.lower_band_filter_unit, + self.lower_band_confidence, + self.activate_up_band_filter_btn, + self.upper_band_spinbox, + self.upper_band_filter_unit, + self.upper_band_confidence, + self.band_range_lbl] ) self.activate_low_band_filter_btn.toggled.connect( @@ -266,54 +284,63 @@ class Artemis(QMainWindow, Ui_MainWindow): self.upper_band_confidence) ) - self.apply_remove_band_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) + self.apply_remove_band_filter_btn.set_texts(Constants.APPLY, + Constants.REMOVE) self.apply_remove_band_filter_btn.set_slave_filters( - [ + simple_ones=[ self.include_undef_bands, self.activate_low_band_filter_btn, - self.activate_up_band_filter_btn, + self.activate_up_band_filter_btn ], - self.activate_low_band_filter_btn, - [ + radio_1=self.activate_low_band_filter_btn, + ruled_by_radio_1=[ self.lower_band_spinbox, self.lower_band_filter_unit, - self.lower_band_confidence, + self.lower_band_confidence ], - self.activate_up_band_filter_btn, - [ + radio_2=self.activate_up_band_filter_btn, + ruled_by_radio_2=[ self.upper_band_spinbox, self.upper_band_filter_unit, - self.upper_band_confidence, - ], + self.upper_band_confidence + ] ) self.apply_remove_band_filter_btn.clicked.connect(self.display_signals) - self.reset_band_filters_btn.clicked.connect(partial(self.reset_fb_filters, Ftype.BAND)) + self.reset_band_filters_btn.clicked.connect( + partial(self.reset_fb_filters, Ftype.BAND) + ) # Manage category filters # Order matters! - self.cat_filter_btns = [self.military_btn, - self.radar_btn, - self.active_btn, - self.inactive_btn, - self.ham_btn, - self.commercial_btn, - self.aviation_btn, - self.marine_btn, - self.analogue_btn, - self.digital_btn, - self.trunked_btn, - self.utility_btn, - self.sat_btn, - self.navigation_btn, - self.interfering_btn, - self.number_stations_btn, - self.time_signal_btn,] + self.cat_filter_btns = [ + self.military_btn, + self.radar_btn, + self.active_btn, + self.inactive_btn, + self.ham_btn, + self.commercial_btn, + self.aviation_btn, + self.marine_btn, + self.analogue_btn, + self.digital_btn, + self.trunked_btn, + self.utility_btn, + self.sat_btn, + self.navigation_btn, + self.interfering_btn, + self.number_stations_btn, + self.time_signal_btn + ] self.apply_remove_cat_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) - self.apply_remove_cat_filter_btn.set_slave_filters([*self.cat_filter_btns, - self.cat_at_least_one, - self.cat_all]) + self.apply_remove_cat_filter_btn.set_slave_filters( + simple_ones=[ + *self.cat_filter_btns, + self.cat_at_least_one, + self.cat_all + ] + ) self.apply_remove_cat_filter_btn.clicked.connect(self.display_signals) self.reset_cat_filters_btn.clicked.connect(self.reset_cat_filters) @@ -323,31 +350,35 @@ class Artemis(QMainWindow, Ui_MainWindow): UrlColors = namedtuple("UrlColors", ["inactive", "active", "clicked"]) self.url_button.colors = UrlColors("#9f9f9f", "#4c75ff", "#942ccc") - self.category_labels = [self.cat_mil, - self.cat_rad, - self.cat_active, - self.cat_inactive, - self.cat_ham, - self.cat_comm, - self.cat_avi, - self.cat_mar, - self.cat_ana, - self.cat_dig, - self.cat_trunked, - self.cat_utility, - self.cat_sat, - self.cat_navi, - self.cat_interf, - self.cat_num_stat, - self.cat_time_sig,] + self.category_labels = [ + self.cat_mil, + self.cat_rad, + self.cat_active, + self.cat_inactive, + self.cat_ham, + self.cat_comm, + self.cat_avi, + self.cat_mar, + self.cat_ana, + self.cat_dig, + self.cat_trunked, + self.cat_utility, + self.cat_sat, + self.cat_navi, + self.cat_interf, + self.cat_num_stat, + self.cat_time_sig + ] - self.property_labels = [self.freq_lab, - self.band_lab, - self.mode_lab, - self.modul_lab, - self.loc_lab, - self.acf_lab, - self.description_text,] + self.property_labels = [ + self.freq_lab, + self.band_lab, + self.mode_lab, + self.modul_lab, + self.loc_lab, + self.acf_lab, + self.description_text + ] self.url_button.clicked.connect(self.go_to_web_page_signal) @@ -357,8 +388,12 @@ class Artemis(QMainWindow, Ui_MainWindow): self.mode_tree_widget.itemSelectionChanged.connect(self.manage_mode_selections) self.reset_mode_filters_btn.clicked.connect(self.reset_mode_filters) self.apply_remove_mode_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) - self.apply_remove_mode_filter_btn.set_slave_filters([self.mode_tree_widget, - self.include_unknown_modes_btn]) + self.apply_remove_mode_filter_btn.set_slave_filters( + simple_ones=[ + self.mode_tree_widget, + self.include_unknown_modes_btn + ] + ) self.apply_remove_mode_filter_btn.clicked.connect(self.display_signals) # Set modulation filter screen. @@ -366,8 +401,12 @@ class Artemis(QMainWindow, Ui_MainWindow): self.modulation_list.addItems(Constants.MODULATIONS) self.search_bar_modulation.textEdited.connect(self.show_matching_modulations) self.apply_remove_modulation_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) - self.apply_remove_modulation_filter_btn.set_slave_filters([self.search_bar_modulation, - self.modulation_list]) + self.apply_remove_modulation_filter_btn.set_slave_filters( + simple_ones=[ + self.search_bar_modulation, + self.modulation_list + ] + ) self.apply_remove_modulation_filter_btn.clicked.connect(self.display_signals) self.reset_modulation_filters_btn.clicked.connect(self.reset_modulation_filters) self.modulation_list.itemClicked.connect(self.remove_if_unselected_modulation) @@ -377,23 +416,34 @@ class Artemis(QMainWindow, Ui_MainWindow): self.locations_list.addItems(Constants.LOCATIONS) self.search_bar_location.textEdited.connect(self.show_matching_locations) self.apply_remove_location_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) - self.apply_remove_location_filter_btn.set_slave_filters([self.search_bar_location, - self.locations_list]) + self.apply_remove_location_filter_btn.set_slave_filters( + simple_ones=[ + self.search_bar_location, + self.locations_list + ] + ) self.apply_remove_location_filter_btn.clicked.connect(self.display_signals) self.reset_location_filters_btn.clicked.connect(self.reset_location_filters) self.locations_list.itemClicked.connect(self.remove_if_unselected_location) # Set ACF filter screen. self.apply_remove_acf_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) - self.apply_remove_acf_filter_btn.set_slave_filters([self.include_undef_acf, self.acf_spinbox, self.acf_confidence]) + self.apply_remove_acf_filter_btn.set_slave_filters( + simple_ones=[ + self.include_undef_acf, + self.acf_spinbox, + self.acf_confidence + ] + ) self.apply_remove_acf_filter_btn.clicked.connect(self.display_signals) self.reset_acf_filters_btn.clicked.connect(self.reset_acf_filters) self.acf_info_btn.clicked.connect(lambda : webbrowser.open(Constants.ACF_DOCS)) connect_events_to_func( - events_to_connect = [self.acf_spinbox.valueChanged, self.acf_confidence.valueChanged], - fun_to_connect = self.set_acf_interval_label, - fun_args = None + events_to_connect=[self.acf_spinbox.valueChanged, + self.acf_confidence.valueChanged], + fun_to_connect=self.set_acf_interval_label, + fun_args=None ) # GFD @@ -406,14 +456,18 @@ class Artemis(QMainWindow, Ui_MainWindow): # Left list widget and search bar. self.search_bar.textChanged.connect(self.display_signals) self.result_list.currentItemChanged.connect(self.display_specs) - self.result_list.itemDoubleClicked.connect(lambda: self.main_tab.setCurrentWidget(self.signal_properties_tab)) - self.audio_widget = AudioPlayer(self.play, - self.pause, - self.stop, - self.volume, - self.audio_progress, - self.active_color, - self.inactive_color) + self.result_list.itemDoubleClicked.connect( + lambda: self.main_tab.setCurrentWidget(self.signal_properties_tab) + ) + self.audio_widget = AudioPlayer( + self.play, + self.pause, + self.stop, + self.volume, + self.audio_progress, + self.active_color, + self.inactive_color + ) BandLabel = namedtuple("BandLabel", ["left", "center", "right"]) self.band_labels = [ @@ -431,7 +485,9 @@ class Artemis(QMainWindow, Ui_MainWindow): ] # Space weather - self.info_now_btn.clicked.connect(lambda : webbrowser.open(Constants.FORECAST_INFO)) + self.info_now_btn.clicked.connect( + lambda : webbrowser.open(Constants.FORECAST_INFO) + ) self.update_now_bar.clicked.connect(self.start_update_space_weather) self.update_now_bar.set_idle() self.space_weather_data = SpaceWeatherData() @@ -592,16 +648,18 @@ class Artemis(QMainWindow, Ui_MainWindow): label.pixmap = pixmap label.make_transparent() label.apply_pixmap() - else: - pop_up(self, title = Messages.BAD_DOWNLOAD, - text = Messages.BAD_DOWNLOAD_MSG).show() + elif not self.closing: + pop_up(self, title=Messages.BAD_DOWNLOAD, + text=Messages.BAD_DOWNLOAD_MSG).show() self.space_weather_data.remove_data() @pyqtSlot() def go_to_gfd(self, by): query = "/?q=" if by is GfdType.FREQ: - value_in_mhz = self.freq_gfd.value() * Constants.CONVERSION_FACTORS[self.unit_freq_gfd.currentText()] / Constants.CONVERSION_FACTORS["MHz"] + value_in_mhz = self.freq_gfd.value() \ + * Constants.CONVERSION_FACTORS[self.unit_freq_gfd.currentText()] \ + / Constants.CONVERSION_FACTORS["MHz"] query += str(value_in_mhz) elif by is GfdType.LOC: query += self.gfd_line_edit.text() @@ -655,8 +713,8 @@ class Artemis(QMainWindow, Ui_MainWindow): item.child(i).setSelected(True) def set_initial_size(self): - """Function to handle high resolution screens. The function sets bigger sizes - for all the relevant fixed-size widgets.""" + """Function to handle high resolution screens. The function sets bigger + sizes for all the relevant fixed-size widgets.""" d = QDesktopWidget().availableGeometry() w = d.width() h = d.height() @@ -720,17 +778,17 @@ class Artemis(QMainWindow, Ui_MainWindow): try: is_checksum_ok = checksum_ok(db, ChecksumWhat.DB) except Exception: - pop_up(self, title = Messages.NO_CONNECTION, - text = Messages.NO_CONNECTION_MSG).show() + pop_up(self, title=Messages.NO_CONNECTION, + text=Messages.NO_CONNECTION_MSG).show() else: if not is_checksum_ok: self.download_db() else: - answer = pop_up(self, title = Messages.DB_UP_TO_DATE, - text = Messages.DB_UP_TO_DATE_MSG, - informative_text = Messages.DOWNLOAD_ANYWAY_QUESTION, - is_question = True, - default_btn = QMessageBox.No).exec() + answer = pop_up(self, title=Messages.DB_UP_TO_DATE, + text=Messages.DB_UP_TO_DATE_MSG, + informative_text=Messages.DOWNLOAD_ANYWAY_QUESTION, + is_question=True, + default_btn=QMessageBox.No).exec() if answer == QMessageBox.Yes: self.download_db() @@ -743,27 +801,27 @@ class Artemis(QMainWindow, Ui_MainWindow): with open(db_path, "rb") as file_db: db = file_db.read() except Exception: - answer = pop_up(self, title = Messages.NO_DB, - text = Messages.NO_DB_AVAIL, - informative_text = Messages.DOWNLOAD_NOW_QUESTION, - is_question = True).exec() + answer = pop_up(self, title=Messages.NO_DB, + text=Messages.NO_DB_AVAIL, + informative_text=Messages.DOWNLOAD_NOW_QUESTION, + is_question=True).exec() if answer == QMessageBox.Yes: self.download_db() else: try: is_checksum_ok = checksum_ok(db, ChecksumWhat.DB) except Exception: - pop_up(self, title = Messages.NO_CONNECTION, - text = Messages.NO_CONNECTION_MSG).show() + pop_up(self, title=Messages.NO_CONNECTION, + text=Messages.NO_CONNECTION_MSG).show() else: if is_checksum_ok: - pop_up(self, title = Messages.DB_UP_TO_DATE, - text = Messages.DB_UP_TO_DATE_MSG).show() + pop_up(self, title=Messages.DB_UP_TO_DATE, + text=Messages.DB_UP_TO_DATE_MSG).show() else: - answer = pop_up(self, title = Messages.DB_NEW_VER, - text = Messages.DB_NEW_VER_MSG, - informative_text = Messages.DOWNLOAD_NOW_QUESTION, - is_question = True).exec() + answer = pop_up(self, title=Messages.DB_NEW_VER, + text=Messages.DB_NEW_VER_MSG, + informative_text=Messages.DOWNLOAD_NOW_QUESTION, + is_question=True).exec() if answer == QMessageBox.Yes: self.download_db() @@ -777,23 +835,23 @@ class Artemis(QMainWindow, Ui_MainWindow): names = Database.NAMES try: self.db = read_csv(os.path.join(Constants.DATA_FOLDER, Database.NAME), - sep = Database.DELIMITER, - header = None, - index_col = 0, - dtype = {name : str for name in Database.STRINGS}, - names = names,) + sep=Database.DELIMITER, + header=None, + index_col=0, + dtype={name: str for name in Database.STRINGS}, + names=names) except FileNotFoundError: self.search_bar.setDisabled(True) - answer = pop_up(self, title = Messages.NO_DB, - text = Messages.NO_DB_AVAIL, - informative_text = Messages.DOWNLOAD_NOW_QUESTION, - is_question = True).exec() + answer = pop_up(self, title=Messages.NO_DB, + text=Messages.NO_DB_AVAIL, + informative_text=Messages.DOWNLOAD_NOW_QUESTION, + is_question=True).exec() if answer == QMessageBox.Yes: self.download_db() else: self.signal_names = self.db.index self.total_signals = len(self.signal_names) - self.db.fillna(Constants.UNKNOWN, inplace = True) + self.db.fillna(Constants.UNKNOWN, inplace=True) self.db[Signal.WIKI_CLICKED] = False self.update_status_tip(self.total_signals) self.result_list.clear() @@ -825,11 +883,13 @@ class Artemis(QMainWindow, Ui_MainWindow): upper_combo_box.disconnect() upper_combo_box.setCurrentText(new_unit) upper_combo_box.currentTextChanged.connect( - partial(self.set_min_value_upper_limit, - lower_combo_box, - lower_spin_box, - upper_combo_box, - upper_spin_box) + partial( + self.set_min_value_upper_limit, + lower_combo_box, + lower_spin_box, + upper_combo_box, + upper_spin_box + ) ) @pyqtSlot() @@ -854,7 +914,8 @@ class Artemis(QMainWindow, Ui_MainWindow): min_value = lower_spinbox.value() if lower_confidence.value() != 0: min_value -= lower_spinbox.value() * lower_confidence.value() / 100 - to_display += str(round(min_value, Constants.MAX_DIGITS)) + ' ' + lower_unit.currentText() + to_display += str(round(min_value, Constants.MAX_DIGITS)) \ + + ' ' + lower_unit.currentText() else: to_display += 'DC' to_display += Constants.RANGE_SEPARATOR @@ -864,7 +925,8 @@ class Artemis(QMainWindow, Ui_MainWindow): color = self.active_color if upper_confidence.value() != 0: max_value += upper_spinbox.value() * upper_confidence.value() / 100 - to_display += str(round(max_value, Constants.MAX_DIGITS)) + ' ' + upper_unit.currentText() + to_display += str(round(max_value, Constants.MAX_DIGITS)) + ' ' \ + + upper_unit.currentText() else: to_display += 'INF' if activate_low and activate_high: @@ -884,8 +946,9 @@ class Artemis(QMainWindow, Ui_MainWindow): def set_acf_interval_label(self): tolerance = self.acf_spinbox.value() * self.acf_confidence.value() / 100 if tolerance > 0: - to_display = f"Selected range:\n\n{round(self.acf_spinbox.value() - tolerance, Constants.MAX_DIGITS)}" +\ - Constants.RANGE_SEPARATOR + f"{round(self.acf_spinbox.value() + tolerance, Constants.MAX_DIGITS)} ms" + val = round(self.acf_spinbox.value() - tolerance, Constants.MAX_DIGITS) + to_display = f"Selected range:\n\n{val}" + Constants.RANGE_SEPARATOR \ + + f"{round(self.acf_spinbox.value() + tolerance, Constants.MAX_DIGITS)} ms" else: to_display = f"Selected value:\n\n{self.acf_spinbox.value()} ms" self.acf_range_lbl.setText(to_display) @@ -923,12 +986,15 @@ class Artemis(QMainWindow, Ui_MainWindow): self.statusbar.setStyleSheet(f'color: {self.active_color}') else: self.statusbar.setStyleSheet(f'color: {self.inactive_color}') - self.statusbar.showMessage(f"{available_signals} out of {self.total_signals} signals displayed.") + self.statusbar.showMessage( + f"{available_signals} out of {self.total_signals} signals displayed." + ) @pyqtSlot() def reset_fb_filters(self, ftype): if ftype != Ftype.FREQ and ftype != Ftype.BAND: raise ValueError("Wrong ftype in function 'reset_fb_filters'") + apply_remove_btn = getattr(self, 'apply_remove_' + ftype + '_filter_btn') include_undef_btn = getattr(self, 'include_undef_' + ftype + 's') activate_low = getattr(self, 'activate_low_' + ftype + '_filter_btn') @@ -939,6 +1005,7 @@ class Artemis(QMainWindow, Ui_MainWindow): upper_spinbox = getattr(self, 'upper_' + ftype + '_spinbox') lower_confidence = getattr(self, 'lower_' + ftype + '_confidence') upper_confidence = getattr(self, 'lower_' + ftype + '_confidence') + default_val = 1 if ftype == Ftype.FREQ else 5000 if ftype == Ftype.FREQ: for f in self.frequency_filters_btns: @@ -1014,8 +1081,10 @@ class Artemis(QMainWindow, Ui_MainWindow): else: return False - signal_freqs = (int(self.db.at[signal_name, Signal.INF_FREQ]), - int(self.db.at[signal_name, Signal.SUP_FREQ])) + signal_freqs = ( + int(self.db.at[signal_name, Signal.INF_FREQ]), + int(self.db.at[signal_name, Signal.SUP_FREQ]) + ) band_filter_ok = False any_checked = False @@ -1028,13 +1097,13 @@ class Artemis(QMainWindow, Ui_MainWindow): upper_limit_ok = True if self.activate_low_freq_filter_btn.isChecked(): if not signal_freqs[1] >= filters_limit(self.lower_freq_spinbox, - self.lower_freq_filter_unit, - self.lower_freq_confidence, -1): + self.lower_freq_filter_unit, + self.lower_freq_confidence, -1): lower_limit_ok = False if self.activate_up_freq_filter_btn.isChecked(): if not signal_freqs[0] < filters_limit(self.upper_freq_spinbox, - self.upper_freq_filter_unit, - self.upper_freq_confidence): + self.upper_freq_filter_unit, + self.upper_freq_confidence): upper_limit_ok = False if any_checked: return band_filter_ok and lower_limit_ok and upper_limit_ok @@ -1051,20 +1120,22 @@ class Artemis(QMainWindow, Ui_MainWindow): else: return False - signal_bands = (int(self.db.at[signal_name, Signal.INF_BAND]), - int(self.db.at[signal_name, Signal.SUP_BAND])) + signal_bands = ( + int(self.db.at[signal_name, Signal.INF_BAND]), + int(self.db.at[signal_name, Signal.SUP_BAND]) + ) lower_limit_ok = True upper_limit_ok = True if self.activate_low_band_filter_btn.isChecked(): if not signal_bands[1] >= filters_limit(self.lower_band_spinbox, - self.lower_band_filter_unit, - self.lower_band_confidence, -1): + self.lower_band_filter_unit, + self.lower_band_confidence, -1): lower_limit_ok = False if self.activate_up_band_filter_btn.isChecked(): if not signal_bands[0] < filters_limit(self.upper_band_spinbox, - self.upper_band_filter_unit, - self.upper_band_confidence): + self.upper_band_filter_unit, + self.upper_band_confidence): upper_limit_ok = False return lower_limit_ok and upper_limit_ok @@ -1151,21 +1222,31 @@ class Artemis(QMainWindow, Ui_MainWindow): current_signal = self.db.loc[self.current_signal_name] self.url_button.setEnabled(True) if not current_signal.at[Signal.WIKI_CLICKED]: - self.url_button.setStyleSheet(f"color: {self.url_button.colors.active};") + self.url_button.setStyleSheet( + f"color: {self.url_button.colors.active};" + ) else: - self.url_button.setStyleSheet(f"color: {self.url_button.colors.clicked};") + self.url_button.setStyleSheet( + f"color: {self.url_button.colors.clicked};" + ) category_code = current_signal.at[Signal.CATEGORY_CODE] undef_freq = is_undef_freq(current_signal) undef_band = is_undef_band(current_signal) if not undef_freq: - self.freq_lab.setText(format_numbers(current_signal.at[Signal.INF_FREQ], - current_signal.at[Signal.SUP_FREQ]) + self.freq_lab.setText( + format_numbers( + current_signal.at[Signal.INF_FREQ], + current_signal.at[Signal.SUP_FREQ] + ) ) else: self.freq_lab.setText("Undefined") if not undef_band: - self.band_lab.setText(format_numbers(current_signal.at[Signal.INF_BAND], - current_signal.at[Signal.SUP_BAND]) + self.band_lab.setText( + format_numbers( + current_signal.at[Signal.INF_BAND], + current_signal.at[Signal.SUP_BAND] + ) ) else: self.band_lab.setText("Undefined") @@ -1184,7 +1265,9 @@ class Artemis(QMainWindow, Ui_MainWindow): self.audio_widget.set_audio_player(self.current_signal_name) else: self.url_button.setEnabled(False) - self.url_button.setStyleSheet(f"color: {self.url_button.colors.inactive};") + self.url_button.setStyleSheet( + f"color: {self.url_button.colors.inactive};" + ) self.current_signal_name = '' self.name_lab.setText("No Signal") self.name_lab.setAlignment(Qt.AlignHCenter) @@ -1196,15 +1279,23 @@ class Artemis(QMainWindow, Ui_MainWindow): self.audio_widget.set_audio_player() def display_spectrogram(self): - default_pic = os.path.join(self.default_images_folder, Constants.NOT_SELECTED) + default_pic = os.path.join( + self.default_images_folder, + Constants.NOT_SELECTED + ) item = self.result_list.currentItem() if item: spectrogram_name = item.text() - path_spectr = os.path.join(Constants.DATA_FOLDER, - Constants.SPECTRA_FOLDER, - spectrogram_name + Constants.SPECTRA_EXT) + path_spectr = os.path.join( + Constants.DATA_FOLDER, + Constants.SPECTRA_FOLDER, + spectrogram_name + Constants.SPECTRA_EXT + ) if not QFileInfo(path_spectr).exists(): - path_spectr = os.path.join(self.default_images_folder, Constants.NOT_AVAILABLE) + path_spectr = os.path.join( + self.default_images_folder, + Constants.NOT_AVAILABLE + ) else: path_spectr = default_pic self.spectrogram.setPixmap(QPixmap(path_spectr)) @@ -1248,13 +1339,18 @@ class Artemis(QMainWindow, Ui_MainWindow): @pyqtSlot() def go_to_web_page_signal(self): if self.current_signal_name: - self.url_button.setStyleSheet(f"color: {self.url_button.colors.clicked}") + self.url_button.setStyleSheet( + f"color: {self.url_button.colors.clicked}" + ) webbrowser.open(self.db.at[self.current_signal_name, Signal.URL]) self.db.at[self.current_signal_name, Signal.WIKI_CLICKED] = True def closeEvent(self, event): + self.closing = True if self.download_window.isVisible(): self.download_window.close() + if self.space_weather_data.is_updating: + self.space_weather_data.shutdown_thread() super().closeEvent(event) diff --git a/audio_player.py b/audio_player.py index fbaec41..5ccc6c6 100644 --- a/audio_player.py +++ b/audio_player.py @@ -1,5 +1,4 @@ import os -import sys from pydub import AudioSegment from pygame import mixer from PyQt5.QtCore import QTimer, pyqtSlot, QObject @@ -9,11 +8,9 @@ import qtawesome as qta class AudioPlayer(QObject): # Maybe useless inheriting from QObject - """ - This is the audio player widget. The only public methods are the __init__ - method, set_audio_player, which loads the current file and refresh_btns_colors. Everything else - is managed internally. - """ + """This is the audio player widget. The only public methods are the __init__ + method, set_audio_player, which loads the current file and refresh_btns_colors. + Everything else is managed internally.""" __time_step = 500 # Milliseconds. @@ -40,19 +37,21 @@ class AudioPlayer(QObject): # Maybe useless inheriting from QObject def refresh_btns_colors(self, active_color, inactive_color): self.__play.setIcon(qta.icon('fa5.play-circle', - color = active_color, - color_disabled = inactive_color)) + color=active_color, + color_disabled=inactive_color)) self.__pause.setIcon(qta.icon('fa5.pause-circle', - color = active_color, - color_disabled = inactive_color)) + color=active_color, + color_disabled=inactive_color)) self.__stop.setIcon(qta.icon('fa5.stop-circle', - color = active_color, - color_disabled = inactive_color)) + color=active_color, + color_disabled=inactive_color)) @pyqtSlot() def __set_volume(self): if mixer.get_init(): - mixer.music.set_volume(self.__volume.value() / self.__volume.maximum()) + mixer.music.set_volume( + self.__volume.value() / self.__volume.maximum() + ) def __reset_audio_widget(self): if mixer.get_init(): @@ -82,7 +81,11 @@ class AudioPlayer(QObject): # Maybe useless inheriting from QObject def set_audio_player(self, fname = ""): self.__first_call = True self.__reset_audio_widget() - full_name = os.path.join(Constants.DATA_FOLDER, Constants.AUDIO_FOLDER, fname + '.ogg') + full_name = os.path.join( + Constants.DATA_FOLDER, + Constants.AUDIO_FOLDER, + fname + '.ogg' + ) if os.path.exists(full_name): self.__play.setEnabled(True) self.__audio_file = full_name @@ -92,8 +95,10 @@ class AudioPlayer(QObject): # Maybe useless inheriting from QObject if not self.__paused: if self.__first_call: self.__first_call = False - mixer.init(frequency = AudioSegment.from_ogg(self.__audio_file).frame_rate, - buffer = 2048) + mixer.init(frequency=AudioSegment.from_ogg( + self.__audio_file + ).frame_rate, + buffer=2048) mixer.music.load(self.__audio_file) self.__set_volume() self.__set_max_progress_bar() diff --git a/clickable_progress_bar.py b/clickable_progress_bar.py index 0cbf2fc..815a8e3 100644 --- a/clickable_progress_bar.py +++ b/clickable_progress_bar.py @@ -2,11 +2,12 @@ from PyQt5.QtWidgets import QProgressBar from PyQt5.QtCore import Qt, pyqtSignal from constants import Constants + class ClickableProgressBar(QProgressBar): clicked = pyqtSignal() - def __init__(self, parent = None): + def __init__(self, parent=None): self.__text = '' super().__init__(parent) diff --git a/double_text_button.py b/double_text_button.py index 45eb6a1..de2ed2e 100644 --- a/double_text_button.py +++ b/double_text_button.py @@ -2,7 +2,7 @@ from PyQt5.QtWidgets import QPushButton from PyQt5.QtCore import pyqtSlot class DoubleTextButton(QPushButton): - def __init__(self, parent = None): + def __init__(self, parent=None): super().__init__(parent) self.clicked.connect(self.__manage_click) @@ -10,11 +10,11 @@ class DoubleTextButton(QPushButton): self.__text_a = text_a self.__text_b = text_b - def set_slave_filters(self, simple_ones = None, - radio_1 = None, - ruled_by_radio_1 = None, - radio_2 = None, - ruled_by_radio_2 = None): + def set_slave_filters(self, simple_ones=None, + radio_1=None, + ruled_by_radio_1=None, + radio_2=None, + ruled_by_radio_2=None): self.__simple_ones = simple_ones self.__ruled_by_radio_1 = ruled_by_radio_1 self.__radio_1 = radio_1 diff --git a/download_window.py b/download_window.py index 9b8327a..1465cc1 100644 --- a/download_window.py +++ b/download_window.py @@ -23,13 +23,13 @@ class DownloadWindow(QWidget, Ui_Download_window): # Qt.WindowStaysOnTopHint ) - self.no_internet_msg = pop_up(self, title = Messages.NO_CONNECTION, - text = Messages.NO_CONNECTION_MSG, - connection = self.close) + self.no_internet_msg = pop_up(self, title=Messages.NO_CONNECTION, + text=Messages.NO_CONNECTION_MSG, + connection=self.close) - self.bad_db_download_msg = pop_up(self, title = Messages.BAD_DOWNLOAD, - text = Messages.BAD_DOWNLOAD_MSG, - connection = self.close) + self.bad_db_download_msg = pop_up(self, title=Messages.BAD_DOWNLOAD, + text=Messages.BAD_DOWNLOAD_MSG, + connection=self.close) self.download_thread = DownloadThread() self.download_thread.finished.connect(self.wait_close) diff --git a/fixed_aspect_ratio_label.py b/fixed_aspect_ratio_label.py index 27c00e8..3b61f9b 100644 --- a/fixed_aspect_ratio_label.py +++ b/fixed_aspect_ratio_label.py @@ -1,6 +1,7 @@ from PyQt5.QtWidgets import QLabel from PyQt5.QtCore import Qt + class FixedAspectRatioLabel(QLabel): def __init__(self, parent = None): super().__init__(parent) @@ -20,7 +21,9 @@ class FixedAspectRatioLabel(QLabel): if self.pixmap: self.setPixmap( self.pixmap.scaled( - self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) + self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation + ) + ) def rescale(self, size): self.resize(size) diff --git a/fixed_aspect_ratio_widget.py b/fixed_aspect_ratio_widget.py index 71e78f9..bcf381e 100644 --- a/fixed_aspect_ratio_widget.py +++ b/fixed_aspect_ratio_widget.py @@ -1,9 +1,10 @@ from PyQt5.QtWidgets import QWidget from PyQt5.QtCore import QSize + class FixedAspectRatioWidget(QWidget): space = 10 - def __init__(self, parent = None): + def __init__(self, parent=None): super().__init__(parent) self.labels = [] diff --git a/requirements_win.txt b/requirements_win.txt index 9234497..2b1e420 100644 --- a/requirements_win.txt +++ b/requirements_win.txt @@ -1,13 +1,18 @@ +aiohttp==3.5.4 altgraph==0.16.1 asn1crypto==0.24.0 +async-timeout==3.0.1 +attrs==19.1.0 certifi==2019.3.9 cffi==1.11.5 +chardet==3.0.4 cryptography==2.3.1 Cython==0.29.6 future==0.16.0 idna==2.7 intel-openmp==2019.0 macholib==1.11 +multidict==4.5.2 numpy==1.15.2 pandas==0.23.4 pefile==2018.8.8 @@ -28,3 +33,4 @@ six==1.11.0 urllib3==1.24 win-inet-pton==1.0.1 wincertstore==0.2 +yarl==1.3.0 diff --git a/space_weather_data.py b/space_weather_data.py index 3f6191d..4f3b764 100644 --- a/space_weather_data.py +++ b/space_weather_data.py @@ -2,6 +2,7 @@ from PyQt5.QtGui import QPixmap from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from threads import UpadteSpaceWeatherThread, ThreadStatus + class SpaceWeatherData(QObject): update_complete = pyqtSignal(bool) @@ -12,15 +13,17 @@ class SpaceWeatherData(QObject): self.ak_index = '' self.sgas = '' self.geo_storm = '' - self.images = [QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap()] + self.images = [ + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap() + ] self.__update_thread = UpadteSpaceWeatherThread(self) self.__update_thread.finished.connect(self.__parse_and_emit_signal) @@ -33,7 +36,7 @@ class SpaceWeatherData(QObject): self.__update_thread.start() def __parse_data(self): - double_split = lambda string : [i.split() for i in string.splitlines()] + double_split = lambda string: [i.split() for i in string.splitlines()] self.xray = double_split(self.xray) self.prot_el = double_split(self.prot_el) self.ak_index = double_split(self.ak_index) @@ -46,21 +49,26 @@ class SpaceWeatherData(QObject): self.ak_index = '' self.sgas = '' self.geo_storm = '' - self.images = [QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap(), - QPixmap()] + self.images = [ + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap(), + QPixmap() + ] @pyqtSlot() def __parse_and_emit_signal(self): - if self.__update_thread.status is not ThreadStatus.OK: - status_ok = False - else: + status_ok = False + if self.__update_thread.status is ThreadStatus.OK: status_ok = True self.__parse_data() self.update_complete.emit(status_ok) + + def shutdown_thread(self): + self.__update_thread.terminate() + self.__update_thread.wait() diff --git a/switchable_label.py b/switchable_label.py index c4f6f12..6e6e074 100644 --- a/switchable_label.py +++ b/switchable_label.py @@ -1,7 +1,8 @@ from PyQt5.QtWidgets import QLabel + class SwitchableLabel(QLabel): - def __init__(self, parent = None): + def __init__(self, parent=None): super().__init__(parent) self.switch_on_colors = () self.switch_off_colors = () diff --git a/themes.py b/themes.py index e290f7e..38798dc 100644 --- a/themes.py +++ b/themes.py @@ -9,6 +9,7 @@ from constants import Constants from switchable_label import SwitchableLabelsIterable from utilities import pop_up + class ThemeConstants(object): FOLDER = "themes" EXTENSION = ".qss" @@ -34,54 +35,77 @@ class Theme(object): self.__theme_path = "" self.__current_theme = "" - self.__parent.default_images_folder = os.path.join(ThemeConstants.FOLDER, - ThemeConstants.DEFAULT, - ThemeConstants.ICONS_FOLDER) + self.__parent.default_images_folder = os.path.join( + ThemeConstants.FOLDER, + ThemeConstants.DEFAULT, + ThemeConstants.ICONS_FOLDER + ) - self.__forecast_labels = SwitchableLabelsIterable(*list(chain(self.__parent.switchable_r_labels, - self.__parent.switchable_s_labels, - self.__parent.switchable_g_now_labels, - self.__parent.switchable_g_today_labels, - self.__parent.k_storm_labels, - self.__parent.a_storm_labels, - [self.__parent.expected_noise_lbl]))) + self.__forecast_labels = SwitchableLabelsIterable( + *list( + chain( + self.__parent.switchable_r_labels, + self.__parent.switchable_s_labels, + self.__parent.switchable_g_now_labels, + self.__parent.switchable_g_today_labels, + self.__parent.k_storm_labels, + self.__parent.a_storm_labels, + [self.__parent.expected_noise_lbl] + ) + ) + ) - self.__forecast_labels.set("switch_on_colors", ThemeConstants.DEFAULT_ON_COLORS) - self.__forecast_labels.set("switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS) + self.__forecast_labels.set( + "switch_on_colors", + ThemeConstants.DEFAULT_ON_COLORS + ) + self.__forecast_labels.set( + "switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS + ) self.__theme_names = {} self.__detect_themes() def __refresh_range_labels(self): self.__parent.set_acf_interval_label() - self.__parent.set_band_filter_label(self.__parent.activate_low_band_filter_btn, - self.__parent.lower_band_spinbox, - self.__parent.lower_band_filter_unit, - self.__parent.lower_band_confidence, - self.__parent.activate_up_band_filter_btn, - self.__parent.upper_band_spinbox, - self.__parent.upper_band_filter_unit, - self.__parent.upper_band_confidence, - self.__parent.band_range_lbl) + self.__parent.set_band_filter_label( + self.__parent.activate_low_band_filter_btn, + self.__parent.lower_band_spinbox, + self.__parent.lower_band_filter_unit, + self.__parent.lower_band_confidence, + self.__parent.activate_up_band_filter_btn, + self.__parent.upper_band_spinbox, + self.__parent.upper_band_filter_unit, + self.__parent.upper_band_confidence, + self.__parent.band_range_lbl + ) - self.__parent.set_band_filter_label(self.__parent.activate_low_freq_filter_btn, - self.__parent.lower_freq_spinbox, - self.__parent.lower_freq_filter_unit, - self.__parent.lower_freq_confidence, - self.__parent.activate_up_freq_filter_btn, - self.__parent.upper_freq_spinbox, - self.__parent.upper_freq_filter_unit, - self.__parent.upper_freq_confidence, - self.__parent.freq_range_lbl) + self.__parent.set_band_filter_label( + self.__parent.activate_low_freq_filter_btn, + self.__parent.lower_freq_spinbox, + self.__parent.lower_freq_filter_unit, + self.__parent.lower_freq_confidence, + self.__parent.activate_up_freq_filter_btn, + self.__parent.upper_freq_spinbox, + self.__parent.upper_freq_filter_unit, + self.__parent.upper_freq_confidence, + self.__parent.freq_range_lbl + ) @pyqtSlot() def __apply(self, theme_path): self.__theme_path = theme_path if self.__theme_path != self.__current_theme: self.__change() - self.__parent.display_specs(self.__parent.result_list.currentItem(), None) + self.__parent.display_specs( + item=self.__parent.result_list.currentItem(), + previous_item=None + ) self.__refresh_range_labels() - self.__parent.audio_widget.refresh_btns_colors(self.__parent.active_color, self.__parent.inactive_color) + self.__parent.audio_widget.refresh_btns_colors( + self.__parent.active_color, + self.__parent.inactive_color + ) self.__forecast_labels.refresh() def __pretty_name(self, bad_name): @@ -93,7 +117,7 @@ class Theme(object): def __detect_themes(self): themes = [] - ag = QActionGroup(self.__parent, exclusive = True) + ag = QActionGroup(self.__parent, exclusive=True) for theme_folder in os.listdir(ThemeConstants.FOLDER): relative_folder = os.path.join(ThemeConstants.FOLDER, theme_folder) if os.path.isdir(os.path.abspath(relative_folder)): @@ -101,27 +125,46 @@ class Theme(object): themes.append(relative_folder) for theme_path in themes: theme_name = '&' + self.__pretty_name(os.path.basename(theme_path)) - new_theme = ag.addAction(QAction(theme_name, self.__parent, checkable = True)) + new_theme = ag.addAction( + QAction( + theme_name, + self.__parent, checkable=True + ) + ) self.__parent.menu_themes.addAction(new_theme) self.__theme_names[theme_name.lstrip('&')] = new_theme new_theme.triggered.connect(partial(self.__apply, theme_path)) + def __is_valid_html_color(self, colors): + pattern = "#([a-zA-Z0-9]){6}" + match_ok = lambda col: bool(re.match(pattern, col)) + if isinstance(colors, list): + if len(colors) > 1: + return all(match_ok(c) for c in colors) + else: + return match_ok(colors[0]) + else: + return match_ok(colors) + def __change(self): + theme_name = os.path.basename(self.__theme_path).split('-')[1] + ThemeConstants.EXTENSION try: - with open(os.path.join( - self.__theme_path, - os.path.basename(self.__theme_path).split('-')[1] + ThemeConstants.EXTENSION), "r") as stylesheet: + with open( + os.path.join(self.__theme_path, theme_name), "r" + ) as stylesheet: style = stylesheet.read() self.__parent.setStyleSheet(style) self.__parent.download_window.setStyleSheet(style) except FileNotFoundError: - pop_up(self.__parent, title = ThemeConstants.THEME_NOT_FOUND, - text = ThemeConstants.MISSING_THEME).show() + pop_up(self.__parent, title=ThemeConstants.THEME_NOT_FOUND, + text=ThemeConstants.MISSING_THEME).show() else: icons_path = os.path.join(self.__theme_path, ThemeConstants.ICONS_FOLDER) - default_icons_path = os.path.join(ThemeConstants.FOLDER, - ThemeConstants.DEFAULT, - ThemeConstants.ICONS_FOLDER) + default_icons_path = os.path.join( + ThemeConstants.FOLDER, + ThemeConstants.DEFAULT, + ThemeConstants.ICONS_FOLDER + ) if os.path.exists(os.path.join(icons_path, Constants.NOT_SELECTED)) and \ os.path.exists(os.path.join(icons_path, Constants.NOT_AVAILABLE)): @@ -129,91 +172,144 @@ class Theme(object): else: self.__parent.default_images_folder = default_icons_path - path_to_search_label = os.path.join(icons_path, Constants.SEARCH_LABEL_IMG) - default_search_label = os.path.join(default_icons_path, Constants.SEARCH_LABEL_IMG) + path_to_search_label = os.path.join( + icons_path, + Constants.SEARCH_LABEL_IMG + ) + default_search_label = os.path.join( + default_icons_path, + Constants.SEARCH_LABEL_IMG + ) if os.path.exists(path_to_search_label): - self.__parent.search_label.setPixmap(QPixmap(path_to_search_label)) - self.__parent.modulation_search_label.setPixmap(QPixmap(path_to_search_label)) - self.__parent.location_search_label.setPixmap(QPixmap(path_to_search_label)) + self.__parent.search_label.setPixmap( + QPixmap(path_to_search_label) + ) + self.__parent.modulation_search_label.setPixmap( + QPixmap(path_to_search_label) + ) + self.__parent.location_search_label.setPixmap( + QPixmap(path_to_search_label) + ) else: - self.__parent.search_label.setPixmap(QPixmap(default_search_label)) - self.__parent.modulation_search_label.setPixmap(QPixmap(default_search_label)) - self.__parent.location_search_label.setPixmap(QPixmap(default_search_label)) + self.__parent.search_label.setPixmap( + QPixmap(default_search_label) + ) + self.__parent.modulation_search_label.setPixmap( + QPixmap(default_search_label) + ) + self.__parent.location_search_label.setPixmap( + QPixmap(default_search_label) + ) self.__parent.search_label.setScaledContents(True) self.__parent.modulation_search_label.setScaledContents(True) self.__parent.location_search_label.setScaledContents(True) - path_to_volume_label = os.path.join(icons_path, Constants.VOLUME_LABEL_IMG) - default_volume_label = os.path.join(default_icons_path, Constants.VOLUME_LABEL_IMG) + path_to_volume_label = os.path.join( + icons_path, + Constants.VOLUME_LABEL_IMG + ) + default_volume_label = os.path.join( + default_icons_path, + Constants.VOLUME_LABEL_IMG + ) if os.path.exists(path_to_volume_label): - self.__parent.volume_label.setPixmap(QPixmap(path_to_volume_label)) + self.__parent.volume_label.setPixmap( + QPixmap(path_to_volume_label) + ) else: - self.__parent.volume_label.setPixmap(QPixmap(default_volume_label)) + self.__parent.volume_label.setPixmap( + QPixmap(default_volume_label) + ) self.__parent.volume_label.setScaledContents(True) - path_to_colors = os.path.join(self.__theme_path, ThemeConstants.COLORS) - active_color_ok = False - inactive_color_ok = False - switch_on_color_ok = False + path_to_colors = os.path.join( + self.__theme_path, + ThemeConstants.COLORS + ) + + active_color_ok = False + inactive_color_ok = False + switch_on_color_ok = False switch_off_color_ok = False - text_color_ok = False + text_color_ok = False if os.path.exists(path_to_colors): - is_valid_html_color = lambda colors : all([bool(re.match("#([a-zA-Z0-9]){6}", color)) for color in colors]) with open(path_to_colors, "r") as colors_file: for line in colors_file: if ThemeConstants.COLOR_SEPARATOR in line: quality, color = line.split(ThemeConstants.COLOR_SEPARATOR) color = color.rstrip() + color_len = 1 if ',' in color: - color = [c.rstrip().lstrip() for c in color.split(',')] - else: - color = [color] - if len(color) > 2: - break - if is_valid_html_color(color): - if quality.lower() == Constants.ACTIVE: - self.__parent.active_color = color[0] - active_color_ok = True - if quality.lower() == Constants.INACTIVE: - self.__parent.inactive_color = color[0] - inactive_color_ok = True - if len(color) == 2: + color = [c.strip() for c in color.split(',')] + color_len = len(color) + if self.__is_valid_html_color(color): + if color_len == 1: + if quality.lower() == Constants.ACTIVE: + self.__parent.active_color = color + active_color_ok = True + if quality.lower() == Constants.INACTIVE: + self.__parent.inactive_color = color + inactive_color_ok = True + if quality.lower() == Constants.TEXT_COLOR: + text_color_ok = True + self.__forecast_labels.set( + "text_color", + color + ) + if color_len == 2: if quality.lower() == Constants.LABEL_ON_COLOR: switch_on_color_ok = True - self.__forecast_labels.set("switch_on_colors", color) + self.__forecast_labels.set( + "switch_on_colors", + color + ) if quality.lower() == Constants.LABEL_OFF_COLOR: switch_off_color_ok = True - self.__forecast_labels.set("switch_off_colors", color) - if quality.lower() == Constants.TEXT_COLOR: - text_color_ok = True - self.__forecast_labels.set("text_color", color[0]) + self.__forecast_labels.set( + "switch_off_colors", + color + ) if not (active_color_ok and inactive_color_ok): self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR self.__parent.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR if not (switch_on_color_ok and switch_off_color_ok): - self.__forecast_labels.set("switch_on_colors", ThemeConstants.DEFAULT_ON_COLORS) - self.__forecast_labels.set("switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS) + self.__forecast_labels.set( + "switch_on_colors", + ThemeConstants.DEFAULT_ON_COLORS + ) + self.__forecast_labels.set( + "switch_off_colors", + ThemeConstants.DEFAULT_OFF_COLORS + ) if not text_color_ok: - self.__forecast_labels.set("text_color", ThemeConstants.DEFAULT_TEXT_COLOR) + self.__forecast_labels.set( + "text_color", + ThemeConstants.DEFAULT_TEXT_COLOR + ) self.__current_theme = self.__theme_path try: - with open(os.path.join(ThemeConstants.FOLDER, - ThemeConstants.CURRENT), "w") as current_theme: + with open(os.path.join( + ThemeConstants.FOLDER, + ThemeConstants.CURRENT + ), "w") as current_theme: current_theme.write(self.__theme_path) except Exception: pass def initialize(self): - current_theme_file = os.path.join(ThemeConstants.FOLDER, ThemeConstants.CURRENT) + current_theme_file = os.path.join( + ThemeConstants.FOLDER, + ThemeConstants.CURRENT + ) if os.path.exists(current_theme_file): with open(current_theme_file, "r") as current_theme_path: theme_path = current_theme_path.read() @@ -221,5 +317,12 @@ class Theme(object): self.__theme_names[theme_name].setChecked(True) self.__apply(theme_path) else: - self.__theme_names[self.__pretty_name(ThemeConstants.DEFAULT)].setChecked(True) - self.__apply(os.path.join(ThemeConstants.FOLDER, ThemeConstants.DEFAULT)) + self.__theme_names[ + self.__pretty_name(ThemeConstants.DEFAULT) + ].setChecked(True) + self.__apply( + os.path.join( + ThemeConstants.FOLDER, + ThemeConstants.DEFAULT + ) + ) diff --git a/threads.py b/threads.py index 8dc40c7..f85e830 100644 --- a/threads.py +++ b/threads.py @@ -3,12 +3,12 @@ from enum import Enum, auto from io import BytesIO import os.path from shutil import rmtree -import urllib3 from zipfile import ZipFile +import aiohttp +import urllib3 from PyQt5.QtCore import QThread from constants import Constants, Database, ChecksumWhat from utilities import checksum_ok -import aiohttp class ThreadStatus(Enum): @@ -19,38 +19,41 @@ class ThreadStatus(Enum): UNDEFINED = auto() -class DownloadThread(QThread): - def __init__(self): - super().__init__() - self.__status = ThreadStatus.UNDEFINED - self.reason = 0 - - @property - def status(self): - return self.__status +class _BaseDownloadThread(QThread): + def __init__(self, parent=None): + super().__init__(parent) + self.status = ThreadStatus.UNDEFINED def __del__(self): self.terminate() self.wait() + super().__del__() + + +class DownloadThread(_BaseDownloadThread): + def __init__(self): + super().__init__() + self.reason = 0 def run(self): + self.status = ThreadStatus.UNDEFINED try: db = urllib3.PoolManager().request('GET', Database.LINK_LOC) except urllib3.exceptions.MaxRetryError: # No internet connection. - self.__status = ThreadStatus.NO_CONNECTION_ERR + self.status = ThreadStatus.NO_CONNECTION_ERR return if db.status != 200: self.reason = db.reason - self.__status = ThreadStatus.BAD_DOWNLOAD_ERR + self.status = ThreadStatus.BAD_DOWNLOAD_ERR return try: is_checksum_ok = checksum_ok(db.data, ChecksumWhat.FOLDER) except Exception: - self.__status = ThreadStatus.NO_CONNECTION_ERR + self.status = ThreadStatus.NO_CONNECTION_ERR return else: if not is_checksum_ok: - self.__status = ThreadStatus.BAD_DOWNLOAD_ERR + self.status = ThreadStatus.BAD_DOWNLOAD_ERR return if os.path.exists(Constants.DATA_FOLDER): rmtree(Constants.DATA_FOLDER) @@ -58,25 +61,19 @@ class DownloadThread(QThread): with ZipFile(BytesIO(db.data)) as zipped: zipped.extractall() except Exception: - self.__status = ThreadStatus.UNKNOWN_ERR + self.status = ThreadStatus.UNKNOWN_ERR else: - self.__status = ThreadStatus.OK + self.status = ThreadStatus.OK -class UpadteSpaceWeatherThread(QThread): +class UpadteSpaceWeatherThread(_BaseDownloadThread): + + __properties = ("xray", "prot_el", "ak_index", "sgas", "geo_storm") + def __init__(self, space_weather_data): super().__init__() - self.__status = ThreadStatus.UNDEFINED self.__space_weather_data = space_weather_data - @property - def status(self): - return self.__status - - def __del__(self): - self.terminate() - self.wait() - async def __download_resource(self, session, link): resp = await session.get(link) return await resp.read() @@ -92,18 +89,27 @@ class UpadteSpaceWeatherThread(QThread): async def __download_resources(self, *links): session = aiohttp.ClientSession() - properties = ("xray", "prot_el", "ak_index", "sgas", "geo_storm") try: - t = [asyncio.create_task(self.__download_property(session, p)) for p in properties] + t = [] + for p in self.__properties: + t.append( + asyncio.create_task(self.__download_property(session, p)) + ) + tot_images = range(len(Constants.FORECAST_IMGS)) - t1 = [asyncio.create_task(self.__download_image(session, im_number)) for im_number in tot_images] + t1 = [] + for im_number in tot_images: + t1.append( + asyncio.create_task(self.__download_image(session, im_number)) + ) await asyncio.gather(*t, *t1) except Exception: - self.__status = ThreadStatus.UNKNOWN_ERR + self.status = ThreadStatus.UNKNOWN_ERR else: - self.__status = ThreadStatus.OK + self.status = ThreadStatus.OK finally: await session.close() def run(self): + self.status = ThreadStatus.UNDEFINED asyncio.run(self.__download_resources()) diff --git a/utilities.py b/utilities.py index 8355532..3786362 100644 --- a/utilities.py +++ b/utilities.py @@ -21,10 +21,10 @@ def uncheck_and_emit(button): button.clicked.emit() def pop_up(cls, title, text, - informative_text = None, - connection = None, - is_question = False, - default_btn = QMessageBox.Yes): + informative_text=None, + connection=None, + is_question=False, + default_btn=QMessageBox.Yes): msg = QMessageBox(cls) msg.setWindowTitle(title) msg.setText(text) @@ -48,21 +48,23 @@ def checksum_ok(data, what): else: raise ValueError("Wrong entry name.") try: - reference = read_csv(Database.LINK_REF, - delimiter = Database.DELIMITER).iat[-1, n] + reference = read_csv( + Database.LINK_REF, + delimiter=Database.DELIMITER + ).iat[-1, n] except Exception: raise return code.hexdigest() == reference def connect_events_to_func(events_to_connect, fun_to_connect, fun_args): - if fun_args: + if fun_args is not None: for event in events_to_connect: event.connect(partial(fun_to_connect, *fun_args)) else: for event in events_to_connect: event.connect(fun_to_connect) -def filters_limit(spinbox, filter_unit, confidence, sign = 1): +def filters_limit(spinbox, filter_unit, confidence, sign=1): band_filter = spinbox.value() * Constants.CONVERSION_FACTORS[filter_unit.currentText()] return band_filter + sign * (confidence.value() * band_filter) // 100