diff --git a/.gitignore b/.gitignore index 660e8c3..364da4c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,9 @@ Data .ipynb_checkpoints *.ipynb wav_converter.py -*.txt +to_do.txt +csv_info.txt +pyinstaller_cmd.txt icons_imgs TestData themes/.current_theme diff --git a/audio_player.py b/audio_player.py index 2d4c2f5..01ec3df 100644 --- a/audio_player.py +++ b/audio_player.py @@ -4,7 +4,7 @@ from pydub import AudioSegment from pygame import mixer from PyQt5.QtCore import QTimer, QTimer, pyqtSlot, QObject -from utilities import Constants +import constants import qtawesome as qta @@ -82,7 +82,7 @@ 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 diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..2c46a5b --- /dev/null +++ b/constants.py @@ -0,0 +1,168 @@ +from collections import namedtuple +from enum import Enum, auto + +class Ftype(object): + FREQ = "freq" + BAND = "band" + +class ChecksumWhat(Enum): + FOLDER = auto() + DB = auto() + +class Theme(object): + FOLDER = "themes" + EXTENSION = ".qss" + ICONS_FOLDER = "icons" + DEFAULT = "1-system" + CURRENT = ".current_theme" + COLORS = "colors.txt" + COLOR_SEPARATOR = "=" + DEFAULT_ACTIVE_COLOR = "#39eaff" + DEFAULT_INACTIVE_COLOR = "#9f9f9f" + +class Messages(object): + NO_DB_AVAIL = "No database available.\nGo to Updates->Update database." + NO_DB = "No database" + THEME_NOT_FOUND = "Theme not found" + MISSING_THEME = "Missing theme in " + Theme.FOLDER + " folder." + NO_CONNECTION = "No internet connection" + NO_CONNECTION_MSG = "Unable to establish an internet connection." + BAD_DOWNLOAD = "Something went wrong" + BAD_DOWNLOAD_MSG = "Something went wrong with the downaload.\nCheck your internet connection and try again." + BAD_FILE = "Bad file detected" + BAD_FILE_MSG = "The downloaded file seems to be corrupted.\nThe old database has not been deleted and\nthe downloaded file has been discarded." + +class Signal(object): + NAME = "name" + INF_FREQ = "inf_freq" + SUP_FREQ = "sup_freq" + MODE = "mode" + INF_BAND = "inf_band" + SUP_BAND = "sup_band" + LOCATION = "location" + URL = "url" + DESCRIPTION = "description" + MODULATION = "modulation" + CATEGORY_CODE = "category_code" + ACF = "acf" + WIKI_CLICKED = "url_clicked" + +class Database(object): + LINK_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip" + LINK_REF = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log" + NAME = "db.csv" + NAMES = (Signal.NAME, + Signal.INF_FREQ, + Signal.SUP_FREQ, + Signal.MODE, + Signal.INF_BAND, + Signal.SUP_BAND, + Signal.LOCATION, + Signal.URL, + Signal.DESCRIPTION, + Signal.MODULATION, + Signal.CATEGORY_CODE, + Signal.ACF,) + DELIMITER = "*" + STRINGS = (Signal.INF_FREQ, + Signal.SUP_FREQ, + Signal.MODE, + Signal.INF_BAND, + Signal.SUP_BAND, + Signal.CATEGORY_CODE,) + +SEARCH_LABEL_IMG = "search_icon.png" +VOLUME_LABEL_IMG = "volume.png" +DATA_FOLDER = "Data" +SPECTRA_FOLDER = "Spectra" +SPECTRA_EXT = ".png" +AUDIO_FOLDER = "Audio" +ACTIVE = "active" +INACTIVE = "inactive" +NOT_AVAILABLE = "spectrumnotavailable.png" +NOT_SELECTED = "nosignalselected.png" +__Band = namedtuple("Band", ["lower", "upper"]) +__ELF = __Band(0, 30) # Formally it is (3, 30) Hz. +__SLF = __Band(30, 300) +__ULF = __Band(300, 3000) +__VLF = __Band(3000, 30000) +__LF = __Band(30 * 10**3, 300 * 10**3) +__MF = __Band(300 * 10 ** 3, 3000 * 10**3) +__HF = __Band(3 * 10**6, 30 * 10**6) +__VHF = __Band(30 * 10**6, 300 * 10**6) +__UHF = __Band(300 * 10**6, 3000 * 10**6) +__SHF = __Band(3 * 10**9, 30 * 10**9) +__EHF = __Band(30 * 10**9, 300 * 10**9) +BANDS = (__ELF, __SLF, __ULF, __VLF, __LF, __MF, __HF, __VHF, __UHF, __SHF, __EHF) +CONVERSION_FACTORS = {"Hz" : 1, + "kHz": 1000, + "MHz": 1000000, + "GHz": 1000000000} +MODES = {"FM": ("NFM", "WFM"), + "AM": (), + "CW": (), + "SK": ("FSK", "PSK", "MSK"), + "SB": ("LSB", "USB", "DSB"), + "Chirp Spread Spectrum": (), + "FHSS-TDM": (), + "RAW": (), + "SC-FDMA": (),} +APPLY = "Apply" +REMOVE = "Remove" +UNKNOWN = "N/A" +MODULATIONS = ("8VSB", + "AFSK", + "AM", + "BFSK", + "C4FM", + "CDMA", + "COFDM", + "CW", + "FFSK", + "FM", + "FMCW", + "FMOP", + "FSK", + "GFSK", + "GMSK", + "IFK", + "MFSK", + "MSK", + "OFDM", + "OOK", + "PAM", + "PPM", + "PSK", + "QAM", + "TDMA",) +LOCATIONS = (UNKNOWN, + "Australia", + "Canada", + "Central Europe", + "China", + "Cyprus", + "Eastern Europe", + "Europe", + "Europe, japan and Asia", + "Exmouth, Australia", + "Finland", + "France", + "Germany", + "Home Base Mobile , AL", + "Hungary", + "Iran", + "Israel", + "Japan", + "LaMour, North Dakota", + "Lualualei, Hawaii", + "North America", + "North Korea", + "Poland", + "Romania", + "Ruda, Sweden", + "UK", + "United Kingdom", + "United States", + "Varberg, Sweden", + "World Wide", + "Worldwide",) \ No newline at end of file diff --git a/download_db_window.ui b/download_db_window.ui index 6f3c1f8..0da9b37 100644 --- a/download_db_window.ui +++ b/download_db_window.ui @@ -7,7 +7,7 @@ 0 0 400 - 151 + 137 @@ -29,7 +29,7 @@ Downloading database -Please wait +Please wait... Qt::AlignCenter diff --git a/download_window.py b/download_window.py index 2291e34..cfce206 100644 --- a/download_window.py +++ b/download_window.py @@ -1,7 +1,9 @@ from PyQt5 import uic from PyQt5.QtCore import Qt, pyqtSlot -from PyQt5.QtWidgets import QWidget, QMessageBox +from PyQt5.QtWidgets import QWidget from threads import DownloadThread, ThreadStatus +from utilities import throwable_message +from constants import Messages Ui_Download_window, _ = uic.loadUiType("download_db_window.ui") @@ -17,24 +19,19 @@ class DownloadWindow(QWidget, Ui_Download_window): # Qt.WindowStaysOnTopHint ) self.everything_ok = True - self.no_internet_msg = QMessageBox(self) - self.no_internet_msg.setWindowTitle("No internet connection") - self.no_internet_msg.setText("Unable to establish an internet connection.") - # self.no_internet_msg.buttonClicked.connect(self.close) - self.no_internet_msg.finished.connect(self.close) - self.bad_db_download_msg = QMessageBox(self) - self.bad_db_download_msg.setWindowTitle("Something wrong") - self.bad_db_download_msg.setText("""Something went wrong with the downaload. - Check your internet connection and try again.""") - self.bad_db_download_msg.finished.connect(self.close) + self.no_internet_msg = throwable_message(self, title = Messages.NO_CONNECTION, + text = Messages.NO_CONNECTION_MSG, + connection = self.close) - self.bad_file_msg = QMessageBox(self) - self.bad_file_msg.setWindowTitle("Bad file detected") - self.bad_file_msg.setText("""The downloaded file seems to be corrupted. - The old database has not been deleted and - the downloaded file has been discarded.""") - self.bad_file_msg.finished.connect(self.close) + self.bad_db_download_msg = throwable_message(self, title = Messages.BAD_DOWNLOAD, + text = Messages.BAD_DOWNLOAD_MSG, + connection = self.close) + + # Never used (should exploit the checksum check for the single file) + self.bad_file_msg = throwable_message(self, title = Messages.BAD_FILE, + text = Messages.BAD_FILE_MSG, + connection = self.close) self.download_thread = DownloadThread() self.download_thread.finished.connect(self.wait_close) @@ -42,8 +39,6 @@ class DownloadWindow(QWidget, Ui_Download_window): self.cancel_btn.clicked.connect(self.terminate_process) def show_no_connection_warning(self): - self.bad_db_download_msg.setText(f"""Unable to correctly download the database. - Reason: {self.download_thread.reason}""") self.no_internet_msg.show() self.everything_ok = False @@ -51,6 +46,10 @@ class DownloadWindow(QWidget, Ui_Download_window): self.bad_db_download_msg.show() self.everything_ok = False + def show_bad_file_warning(self): + self.bad_file_msg.show() + self.everything_ok = False + @pyqtSlot() def terminate_process(self): if self.download_thread.isRunning(): @@ -65,7 +64,7 @@ class DownloadWindow(QWidget, Ui_Download_window): elif self.download_thread.status == ThreadStatus.NO_CONNECTION_ERR: self.show_no_connection_warning() elif self.download_thread.status == ThreadStatus.BAD_DOWNLOAD_ERR: - self.show_bad_download_warning + self.show_bad_download_warning() else: self.close() diff --git a/main.py b/main.py index 7a14016..378690e 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,6 @@ from pandas import read_csv from PyQt5.QtWidgets import (QMainWindow, QApplication, QAction, - QMessageBox, qApp, QDesktopWidget, QListWidgetItem, @@ -27,17 +26,23 @@ from audio_player import AudioPlayer from double_text_button import DoubleTextButton from download_window import DownloadWindow +import constants -from utilities import (Constants, - reset_apply_remove_btn, +from utilities import (reset_apply_remove_btn, throwable_message, - is_valid_html_color,) + is_valid_html_color, + connect_to, + filters_ok, + is_undef_freq, + is_undef_band, + change_unit, + format_numbers) qt_creator_file = "main_window.ui" Ui_MainWindow, _ = uic.loadUiType(qt_creator_file) -class MyApp(QMainWindow, Ui_MainWindow): +class MyApp(QMainWindow, Ui_MainWindow): def __init__(self): super().__init__() self.setupUi(self) @@ -49,8 +54,8 @@ class MyApp(QMainWindow, Ui_MainWindow): self.current_signal_name = '' self.signal_names = [] self.total_signals = 0 - self.active_color = Constants.ACTIVE_COLOR - self.inactive_color = Constants.INACTIVE_COLOR + self.active_color = constants.Theme.DEFAULT_ACTIVE_COLOR + self.inactive_color = constants.Theme.DEFAULT_INACTIVE_COLOR # Manage frequency filters. self.frequency_filters_btns = ( @@ -67,7 +72,7 @@ class MyApp(QMainWindow, Ui_MainWindow): self.ehf_filter_btn, ) - self.connect_to( + connect_to( objects_to_connect = [self.lower_freq_spinbox.valueChanged, self.upper_freq_spinbox.valueChanged, self.lower_freq_filter_unit.currentTextChanged, @@ -80,7 +85,7 @@ class MyApp(QMainWindow, Ui_MainWindow): self.upper_freq_spinbox] ) - self.connect_to( + connect_to( objects_to_connect = [self.lower_freq_spinbox.valueChanged, self.upper_freq_spinbox.valueChanged, self.lower_freq_filter_unit.currentTextChanged, @@ -117,7 +122,7 @@ class MyApp(QMainWindow, Ui_MainWindow): self.upper_freq_confidence) ) - self.apply_remove_freq_filter_btn.set_texts(Constants.APPLY, Constants.REMOVE) + self.apply_remove_freq_filter_btn.set_texts(constants.APPLY, constants.REMOVE) self.apply_remove_freq_filter_btn.set_slave_filters( [ *self.frequency_filters_btns, @@ -139,11 +144,11 @@ class MyApp(QMainWindow, Ui_MainWindow): ], ) self.apply_remove_freq_filter_btn.clicked.connect(self.display_signals) - self.reset_frequency_filters_btn.clicked.connect(partial(self.reset_fb_filters, 'freq')) + self.reset_frequency_filters_btn.clicked.connect(partial(self.reset_fb_filters, constants.Ftype.FREQ)) # Manage bandwidth filters. - self.connect_to( + connect_to( objects_to_connect = [self.lower_band_spinbox.valueChanged, self.upper_band_spinbox.valueChanged, self.lower_band_filter_unit.currentTextChanged, @@ -156,7 +161,7 @@ class MyApp(QMainWindow, Ui_MainWindow): self.upper_band_spinbox] ) - self.connect_to( + connect_to( objects_to_connect = [self.lower_band_spinbox.valueChanged, self.upper_band_spinbox.valueChanged, self.lower_band_filter_unit.currentTextChanged, @@ -193,7 +198,7 @@ class MyApp(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( [ self.include_undef_bands, @@ -214,7 +219,7 @@ class MyApp(QMainWindow, Ui_MainWindow): ], ) self.apply_remove_band_filter_btn.clicked.connect(self.display_signals) - self.reset_band_filters_btn.clicked.connect(partial(self.reset_fb_filters, 'band')) + self.reset_band_filters_btn.clicked.connect(partial(self.reset_fb_filters, constants.Ftype.BAND)) # Manage category filters @@ -237,7 +242,7 @@ class MyApp(QMainWindow, Ui_MainWindow): 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_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]) @@ -283,16 +288,16 @@ class MyApp(QMainWindow, Ui_MainWindow): self.set_mode_tree_widget() 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_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.clicked.connect(self.display_signals) # Set modulation filter screen. - self.modulation_list.addItems(Constants.MODULATIONS) + 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_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.clicked.connect(self.display_signals) @@ -301,9 +306,9 @@ class MyApp(QMainWindow, Ui_MainWindow): # Set location filter screen. - self.locations_list.addItems(Constants.LOCATIONS) + 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_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.clicked.connect(self.display_signals) @@ -311,11 +316,9 @@ class MyApp(QMainWindow, Ui_MainWindow): self.locations_list.itemClicked.connect(self.remove_if_unselected_location) # Find available themes. - self.default_images_folder = os.path.join(Constants.THEMES_FOLDER, - Constants.DEFAULT_THEME, - Constants.ICONS_FOLDER) - # self.find_themes() - # self.set_theme() + self.default_images_folder = os.path.join(constants.Theme.FOLDER, + constants.Theme.DEFAULT, + constants.Theme.ICONS_FOLDER) # ########################################################################################## @@ -357,10 +360,10 @@ class MyApp(QMainWindow, Ui_MainWindow): def find_themes(self): themes = [] - for theme_folder in os.listdir(Constants.THEMES_FOLDER): - relative_folder = os.path.join(Constants.THEMES_FOLDER, theme_folder) + for theme_folder in os.listdir(constants.Theme.FOLDER): + relative_folder = os.path.join(constants.Theme.FOLDER, theme_folder) if os.path.isdir(os.path.abspath(relative_folder)): - relative_folder = os.path.join(Constants.THEMES_FOLDER, theme_folder) + relative_folder = os.path.join(constants.Theme.FOLDER, theme_folder) themes.append(relative_folder) for theme in themes: theme_name = '&' + ' '.join( @@ -382,26 +385,26 @@ class MyApp(QMainWindow, Ui_MainWindow): try: with open(os.path.join( theme_path, - os.path.basename(theme_path).split('-')[1] + Constants.THEME_EXTENSION) + os.path.basename(theme_path).split('-')[1] + constants.Theme.EXTENSION) ) as stylesheet: style = stylesheet.read() self.setStyleSheet(style) self.download_window.setStyleSheet(style) except FileNotFoundError: - throwable_message(self, title = "Theme not found", - text = f"Missing theme in {Constants.THEMES_FOLDER} folder.").show() + throwable_message(self, title = constants.Messages.THEME_NOT_FOUND, + text = constants.Messages.MISSING_THEME).show() else: - icons_path = os.path.join(theme_path, Constants.ICONS_FOLDER) - default_icons_path = os.path.join(Constants.THEMES_FOLDER, Constants.DEFAULT_THEME, Constants.ICONS_FOLDER) + icons_path = os.path.join(theme_path, constants.Theme.ICONS_FOLDER) + default_icons_path = os.path.join(constants.Theme.FOLDER, constants.Theme.DEFAULT, constants.Theme.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)): + if os.path.exists(os.path.join(icons_path, constants.NOT_SELECTED)) and \ + os.path.exists(os.path.join(icons_path, constants.NOT_AVAILABLE)): self.default_images_folder = icons_path else: self.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.search_label.setPixmap(QPixmap(path_to_search_label)) @@ -416,8 +419,8 @@ class MyApp(QMainWindow, Ui_MainWindow): self.modulation_search_label.setScaledContents(True) self.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.volume_label.setPixmap(QPixmap(path_to_volume_label)) @@ -426,7 +429,7 @@ class MyApp(QMainWindow, Ui_MainWindow): self.volume_label.setScaledContents(True) - path_to_colors = os.path.join(theme_path, Constants.THEME_COLORS) + path_to_colors = os.path.join(theme_path, constants.Theme.COLORS) active_color_ok = False inactive_color_ok = False valid_format = False @@ -435,36 +438,36 @@ class MyApp(QMainWindow, Ui_MainWindow): valid_file = True with open(path_to_colors, "r") as colors_file: for line in colors_file: - if '=' in line: + if constants.Theme.COLOR_SEPARATOR in line: valid_format = True - quality, color = line.split("=") + quality, color = line.split(constants.Theme.COLOR_SEPARATOR) color = color.rstrip() - if quality == "active" and is_valid_html_color(color): + if quality.lower() == constants.ACTIVE and is_valid_html_color(color): self.active_color = color active_color_ok = True - if quality == "inactive" and is_valid_html_color(color): + if quality.lower() == constants.INACTIVE and is_valid_html_color(color): self.inactive_color = color inactive_color_ok = True if not all([valid_file, valid_format, active_color_ok, inactive_color_ok]): - self.active_color = Constants.ACTIVE_COLOR - self.inactive_color = Constants.INACTIVE_COLOR + self.active_color = constants.Theme.DEFAULT_ACTIVE_COLOR + self.inactive_color = constants.Theme.DEFAULT_INACTIVE_COLOR self.audio_widget.refresh_btns_colors(self.active_color, self.inactive_color) try: - with open(os.path.join(Constants.THEMES_FOLDER, - Constants.CURRENT_THEME), "w") as current_theme: + with open(os.path.join(constants.Theme.FOLDER, + constants.Theme.CURRENT), "w") as current_theme: current_theme.write(theme_path) except: pass def set_theme(self): - current_theme_file = os.path.join(Constants.THEMES_FOLDER, Constants.CURRENT_THEME) + current_theme_file = os.path.join(constants.Theme.FOLDER, constants.Theme.CURRENT) if os.path.exists(current_theme_file): with open(current_theme_file) as current_theme: theme = current_theme.read() - if theme != Constants.DEFAULT_THEME: + if theme != constants.Theme.DEFAULT: self.change_theme(theme) @pyqtSlot(QListWidgetItem) @@ -494,7 +497,7 @@ class MyApp(QMainWindow, Ui_MainWindow): item.setHidden(True) def set_mode_tree_widget(self): - for parent, children in Constants.MODES.items(): + for parent, children in constants.MODES.items(): iparent = QTreeWidgetItem([parent]) self.mode_tree_widget.addTopLevelItem(iparent) for child in children: @@ -504,11 +507,11 @@ class MyApp(QMainWindow, Ui_MainWindow): def manage_mode_selections(self): selected_items = self.mode_tree_widget.selectedItems() - parents = Constants.MODES.keys() + parents = constants.MODES.keys() for parent in parents: for item in selected_items: if parent == item.text(0): - for i in range(len(Constants.MODES[parent])): + for i in range(len(constants.MODES[parent])): item.child(i).setSelected(True) def set_initial_size(self): @@ -571,32 +574,25 @@ class MyApp(QMainWindow, Ui_MainWindow): self.display_signals() def load_db(self): - names = Constants.DB_NAMES + names = constants.Database.NAMES try: - self.db = read_csv(os.path.join(Constants.DATA_FOLDER, Constants.DB_NAME), - sep = '*', + self.db = read_csv(os.path.join(constants.DATA_FOLDER, constants.Database.NAME), + sep = constants.Database.DELIMITER, header = None, index_col = 0, - dtype = {name : str for name in Constants.DB_STRINGS}, + dtype = {name : str for name in constants.Database.STRINGS}, names = names,) except FileNotFoundError: self.search_bar.setDisabled(True) - box = QMessageBox(self) - box.setWindowTitle(Constants.Messages.NO_DB) - box.setText(Constants.Messages.NO_DB_AVAIL) - box.show() + throwable_message(self, title = constants.Messages.NO_DB, + text = constants.Messages.NO_DB_AVAIL).show() else: self.signal_names = self.db.index self.total_signals = len(self.signal_names) - self.db.fillna(Constants.UNKNOWN, inplace = True) - self.db[Constants.DB_WIKI_CLICKED] = False + self.db.fillna(constants.UNKNOWN, inplace = True) + self.db[constants.Signal.WIKI_CLICKED] = False self.update_status_tip(self.total_signals) - @staticmethod - def connect_to(objects_to_connect, fun_to_connect, fun_args): - for signal in objects_to_connect: - signal.connect(partial(fun_to_connect, *fun_args)) - @pyqtSlot() def set_min_value_upper_limit(self, lower_combo_box, lower_spin_box, @@ -605,14 +601,13 @@ class MyApp(QMainWindow, Ui_MainWindow): if lower_spin_box.isEnabled(): unit_conversion = {'Hz' : ['kHz', 'MHz', 'GHz'], 'kHz': ['MHz', 'GHz'], - 'MHz': ['GHz'] - } + 'MHz': ['GHz']} lower_units = lower_combo_box.currentText() upper_units = upper_combo_box.currentText() lower_value = lower_spin_box.value() upper_value = upper_spin_box.value() - inf_limit = (lower_value * Constants.CONVERSION_FACTORS[lower_units]) \ - // Constants.CONVERSION_FACTORS[upper_units] + inf_limit = (lower_value * constants.CONVERSION_FACTORS[lower_units]) \ + // constants.CONVERSION_FACTORS[upper_units] counter = 0 while inf_limit > upper_spin_box.maximum(): counter += 1 @@ -685,8 +680,6 @@ class MyApp(QMainWindow, Ui_MainWindow): @pyqtSlot() def display_signals(self): - # for i in range(self.result_list.count()): - # self.result_list.item(i).setHidden(True) text = self.search_bar.text() available_signals = 0 for index, signal in enumerate(self.signal_names): @@ -712,7 +705,7 @@ class MyApp(QMainWindow, Ui_MainWindow): @pyqtSlot() def reset_fb_filters(self, ftype): - if ftype != 'freq' and ftype != 'band': + if ftype != constants.Ftype.FREQ and ftype != constants.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') @@ -724,8 +717,8 @@ class MyApp(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 == 'freq' else 5000 - if ftype == 'freq': + default_val = 1 if ftype == constants.Ftype.FREQ else 5000 + if ftype == constants.Ftype.FREQ: for f in self.frequency_filters_btns: if f.isChecked(): f.setChecked(False) @@ -776,19 +769,19 @@ class MyApp(QMainWindow, Ui_MainWindow): def frequency_filters_ok(self, signal_name): if not self.apply_remove_freq_filter_btn.isChecked(): return True - undef_freq = self.is_undef_freq(self.db.loc[signal_name]) + undef_freq = is_undef_freq(self.db.loc[signal_name]) if undef_freq: if self.include_undef_freqs.isChecked(): return True else: return False - signal_freqs = (int(self.db.at[signal_name, "inf_freq"]), - int(self.db.at[signal_name, "sup_freq"])) + signal_freqs = (int(self.db.at[signal_name, constants.Signal.INF_FREQ]), + int(self.db.at[signal_name, constants.Signal.SUP_FREQ])) band_filter_ok = False any_checked = False - for btn, band_limits in zip(self.frequency_filters_btns, Constants.BANDS): + for btn, band_limits in zip(self.frequency_filters_btns, constants.BANDS): if btn.isChecked(): any_checked = True if signal_freqs[0] < band_limits.upper and signal_freqs[1] >= band_limits.lower: @@ -796,14 +789,14 @@ class MyApp(QMainWindow, Ui_MainWindow): lower_limit_ok = True upper_limit_ok = True if self.activate_low_freq_filter_btn.isChecked(): - if not signal_freqs[1] >= self.filters_ok(self.lower_freq_spinbox, - self.lower_freq_filter_unit, - self.lower_freq_confidence, -1): + if not signal_freqs[1] >= filters_ok(self.lower_freq_spinbox, + 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] < self.filters_ok(self.upper_freq_spinbox, - self.upper_freq_filter_unit, - self.upper_freq_confidence): + if not signal_freqs[0] < filters_ok(self.upper_freq_spinbox, + 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 @@ -813,34 +806,34 @@ class MyApp(QMainWindow, Ui_MainWindow): def band_filters_ok(self, signal_name): if not self.apply_remove_band_filter_btn.isChecked(): return True - undef_band = self.is_undef_band(self.db.loc[signal_name]) + undef_band = is_undef_band(self.db.loc[signal_name]) if undef_band: if self.include_undef_bands.isChecked(): return True else: return False - signal_bands = (int(self.db.at[signal_name, "inf_band"]), - int(self.db.at[signal_name, "sup_band"])) + signal_bands = (int(self.db.at[signal_name, constants.Signal.INF_BAND]), + int(self.db.at[signal_name, constants.Signal.SUP_BAND])) lower_limit_ok = True upper_limit_ok = True if self.activate_low_band_filter_btn.isChecked(): - if not signal_bands[1] >= self.filters_ok(self.lower_band_spinbox, - self.lower_band_filter_unit, - self.lower_band_confidence, -1): + if not signal_bands[1] >= filters_ok(self.lower_band_spinbox, + 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] < self.filters_ok(self.upper_band_spinbox, - self.upper_band_filter_unit, - self.upper_band_confidence): + if not signal_bands[0] < filters_ok(self.upper_band_spinbox, + self.upper_band_filter_unit, + self.upper_band_confidence): upper_limit_ok = False return lower_limit_ok and upper_limit_ok def category_filters_ok(self, signal_name): if not self.apply_remove_cat_filter_btn.isChecked(): return True - cat_code = self.db.at[signal_name, 'category_code'] + cat_code = self.db.at[signal_name, constants.Signal.CATEGORY_CODE] cat_checked = 0 positive_cases = 0 for index, cat in enumerate(self.cat_filter_btns): @@ -856,15 +849,15 @@ class MyApp(QMainWindow, Ui_MainWindow): def mode_filters_ok(self, signal_name): if not self.apply_remove_mode_filter_btn.isChecked(): return True - signal_mode = self.db.at[signal_name, "mode"] - if signal_mode == Constants.UNKNOWN: + signal_mode = self.db.at[signal_name, constants.Signal.MODE] + if signal_mode == constants.UNKNOWN: if self.include_unknown_modes_btn.isChecked(): return True else: return False selected_items = [item for item in self.mode_tree_widget.selectedItems()] selected_items_text = [i.text(0) for i in selected_items] - parents = [item for item in selected_items_text if item in Constants.MODES.keys()] + parents = [item for item in selected_items_text if item in constants.MODES.keys()] children = [item for item in selected_items_text if item not in parents] ok = [] for item in selected_items: @@ -877,7 +870,7 @@ class MyApp(QMainWindow, Ui_MainWindow): def modulation_filters_ok(self, signal_name): if not self.apply_remove_modulation_filter_btn.isChecked(): return True - signal_modulation = self.db.at[signal_name, "modulation"] + signal_modulation = self.db.at[signal_name, constants.Signal.MODULATION] for item in self.modulation_list.selectedItems(): if item.text() == signal_modulation: return True @@ -886,17 +879,12 @@ class MyApp(QMainWindow, Ui_MainWindow): def location_filters_ok(self, signal_name): if not self.apply_remove_location_filter_btn.isChecked(): return True - signal_location = self.db.at[signal_name, "location"] + signal_location = self.db.at[signal_name, constants.Signal.LOCATION] for item in self.locations_list.selectedItems(): if item.text() == signal_location: return True return False - @staticmethod - def filters_ok(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 - @pyqtSlot(QListWidgetItem, QListWidgetItem) def display_specs(self, item, previous_item): self.display_spectrogram() @@ -906,33 +894,31 @@ class MyApp(QMainWindow, Ui_MainWindow): self.name_lab.setAlignment(Qt.AlignHCenter) current_signal = self.db.loc[self.current_signal_name] self.url_button.setEnabled(True) - if not current_signal.at["url_clicked"]: + if not current_signal.at[constants.Signal.WIKI_CLICKED]: self.url_button.setStyleSheet(f"color: {self.url_button.colors.active};") else: self.url_button.setStyleSheet(f"color: {self.url_button.colors.clicked};") - category_code = current_signal.at["category_code"] - undef_freq = self.is_undef_freq(current_signal) - undef_band = self.is_undef_band(current_signal) + category_code = current_signal.at[constants.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(self.format_numbers( - current_signal.at["inf_freq"], - current_signal.at["sup_freq"]) + self.freq_lab.setText(format_numbers(current_signal.at[constants.Signal.INF_FREQ], + current_signal.at[constants.Signal.SUP_FREQ]) ) else: self.freq_lab.setText("Undefined") if not undef_band: - self.band_lab.setText(self.format_numbers( - current_signal.at["inf_band"], - current_signal.at["sup_band"]) + self.band_lab.setText(format_numbers(current_signal.at[constants.Signal.INF_BAND], + current_signal.at[constants.Signal.SUP_BAND]) ) else: self.band_lab.setText("Undefined") - self.mode_lab.setText(current_signal.at["mode"]) - self.modul_lab.setText(current_signal.at["modulation"]) - self.loc_lab.setText(current_signal.at["location"]) - self.acf_lab.setText(current_signal.at["acf"]) - self.description_text.setText(current_signal.at["description"]) + self.mode_lab.setText(current_signal.at[constants.Signal.MODE]) + self.modul_lab.setText(current_signal.at[constants.Signal.MODULATION]) + self.loc_lab.setText(current_signal.at[constants.Signal.LOCATION]) + self.acf_lab.setText(current_signal.at[constants.Signal.ACF]) + self.description_text.setText(current_signal.at[constants.Signal.DESCRIPTION]) for cat, cat_lab in zip(category_code, self.category_labels): if cat == '0': cat_lab.setStyleSheet(f"color: {self.inactive_color};") @@ -947,64 +933,22 @@ class MyApp(QMainWindow, Ui_MainWindow): self.name_lab.setText("No Signal") self.name_lab.setAlignment(Qt.AlignHCenter) for lab in self.property_labels: - lab.setText(Constants.UNKNOWN) + lab.setText(constants.UNKNOWN) for lab in self.category_labels: lab.setStyleSheet(f"color: {self.inactive_color};") self.set_band_range() self.audio_widget.set_audio_player() - - @staticmethod - def is_undef_freq(current_signal): - lower_freq = current_signal.at["inf_freq"] - upper_freq = current_signal.at["sup_freq"] - return lower_freq == Constants.UNKNOWN or upper_freq == Constants.UNKNOWN - - @staticmethod - def is_undef_band(current_signal): - lower_band = current_signal.at["inf_band"] - upper_band = current_signal.at["sup_band"] - return lower_band == Constants.UNKNOWN or upper_band == Constants.UNKNOWN - - @classmethod - def format_numbers(cls, lower, upper): - units = {1: 'Hz', 1000: 'kHz', 10**6: 'MHz', 10**9: 'GHz'} - lower_factor = cls.change_unit(lower) - upper_factor = cls.change_unit(upper) - pre_lower = lower - pre_upper = upper - lower = int(lower) / lower_factor - upper = int(upper) / upper_factor - if lower.is_integer(): - lower = int(lower) - if upper.is_integer(): - upper = int(upper) - if pre_lower != pre_upper: - return f"{lower:,} {units[lower_factor]} - {upper:,} {units[upper_factor]}" - else: - return f"{lower:,} {units[lower_factor]}" - - @staticmethod - def change_unit(num): - digits = len(num) - if digits < 4: - return 1 - elif digits < 7: - return 1000 - elif digits < 10: - return 10**6 - else: - return 10**9 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)) @@ -1015,10 +959,10 @@ class MyApp(QMainWindow, Ui_MainWindow): label.setStyleSheet(f"color: {color};") def set_band_range(self, current_signal = None): - if current_signal is not None and not self.is_undef_freq(current_signal): - lower_freq = int(current_signal.at["inf_freq"]) - upper_freq = int(current_signal.at["sup_freq"]) - zipped = list(zip(Constants.BANDS, self.band_labels)) + if current_signal is not None and not is_undef_freq(current_signal): + lower_freq = int(current_signal.at[constants.Signal.INF_FREQ]) + upper_freq = int(current_signal.at[constants.Signal.SUP_FREQ]) + zipped = list(zip(constants.BANDS, self.band_labels)) for i, w in enumerate(zipped): band, band_label = w if lower_freq >= band.lower and lower_freq < band.upper: @@ -1048,8 +992,8 @@ class MyApp(QMainWindow, Ui_MainWindow): def go_to_web_page_signal(self): if self.current_signal_name: self.url_button.setStyleSheet(f"color: {self.url_button.colors.clicked}") - webbrowser.open(self.db.at[self.current_signal_name, "url"]) - self.db.at[self.current_signal_name, "url_clicked"] = True + webbrowser.open(self.db.at[self.current_signal_name, constants.Signal.URL]) + self.db.at[self.current_signal_name, constants.Signal.WIKI_CLICKED] = True def closeEvent(self, event): if self.download_window.isVisible(): diff --git a/main_window.ui b/main_window.ui index 6b9a454..c1cc79b 100644 --- a/main_window.ui +++ b/main_window.ui @@ -174,7 +174,7 @@ QTabWidget::Rounded - 0 + 1 true @@ -1771,7 +1771,7 @@ p, li { white-space: pre-wrap; } - 5 + 6 true @@ -3873,6 +3873,282 @@ Inactive ACF + + + + + + 12 + 75 + true + + + + Include undefined ACFs + + + true + + + + + + + + + Qt::Horizontal + + + + 50 + 20 + + + + + + + + + 12 + 75 + true + + + + AC interval + + + + + + + false + + + + 0 + 0 + + + + + 80 + 0 + + + + + 100 + 16777215 + + + + + 12 + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 100000000 + + + 50 + + + + + + + + 12 + 75 + true + + + + ms + + + + + + + Qt::Horizontal + + + + 49 + 20 + + + + + + + + + 0 + 0 + + + + + 12 + 75 + true + + + + Confidence % + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 0 + 0 + + + + + 60 + 0 + + + + + 50 + 16777215 + + + + + 12 + + + + + + + false + + + Qt::AlignCenter + + + QAbstractSpinBox::UpDownArrows + + + + + + 100 + + + 0 + + + + + + + Qt::Horizontal + + + + 50 + 20 + + + + + + + + + + false + + + + 0 + 0 + + + + + 12 + 75 + false + true + false + + + + + + + Selected range: + +Inactive + + + Qt::AlignCenter + + + + + + + + 12 + 75 + true + + + + Apply + + + true + + + + + + + + 12 + 75 + true + + + + Reset + + + false + + + + diff --git a/themes/1-system/system.th b/themes/1-system/system.qss similarity index 100% rename from themes/1-system/system.th rename to themes/1-system/system.qss diff --git a/themes/2-dark/dark.th b/themes/2-dark/dark.qss similarity index 100% rename from themes/2-dark/dark.th rename to themes/2-dark/dark.qss diff --git a/themes/3-material_design/colors.txt b/themes/3-material_design/colors.txt new file mode 100644 index 0000000..cd95572 --- /dev/null +++ b/themes/3-material_design/colors.txt @@ -0,0 +1,2 @@ +active=#ddffdf +inactive=#949494 \ No newline at end of file diff --git a/themes/3-material_design/material_design.th b/themes/3-material_design/material_design.qss similarity index 100% rename from themes/3-material_design/material_design.th rename to themes/3-material_design/material_design.qss diff --git a/threads.py b/threads.py index 43ff35b..9c8efc0 100644 --- a/threads.py +++ b/threads.py @@ -6,13 +6,15 @@ from shutil import rmtree import urllib3 from zipfile import ZipFile from PyQt5.QtCore import QThread -from utilities import checksum_ok, Constants +import constants +from utilities import checksum_ok +import constants class ThreadStatus(Enum): - OK = auto() + OK = auto() NO_CONNECTION_ERR = auto() - UNKNOWN_ERR = auto() - BAD_DOWNLOAD_ERR = auto() + UNKNOWN_ERR = auto() + BAD_DOWNLOAD_ERR = auto() class DownloadThread(QThread): def __init__(self): @@ -30,8 +32,8 @@ class DownloadThread(QThread): def run(self): try: - db = urllib3.PoolManager().request('GET', Constants.DB_LOCATION) - # db = urllib.request.urlopen(Constants.DB_LOCATION) + db = urllib3.PoolManager().request('GET', constants.Database.LINK_LOC) + # db = urllib.request.urlopen(constants.Database.LINK_LOC) # raise urllib.error.URLError('Test') except urllib3.exceptions.MaxRetryError: # No internet connection. self.__status = ThreadStatus.NO_CONNECTION_ERR @@ -40,11 +42,11 @@ class DownloadThread(QThread): self.reason = db.reason self.__status = ThreadStatus.BAD_DOWNLOAD_ERR return - if not checksum_ok(db.data, "folder"): + if not checksum_ok(db.data, constants.ChecksumWhat.FOLDER): self.__status = ThreadStatus.BAD_DOWNLOAD_ERR return - if os.path.exists(Constants.DATA_FOLDER): - rmtree(Constants.DATA_FOLDER) + if os.path.exists(constants.DATA_FOLDER): + rmtree(constants.DATA_FOLDER) try: # data_folder = db.read() with ZipFile(BytesIO(db.data)) as zipped: diff --git a/utilities.py b/utilities.py index 3f58e4d..547e86d 100644 --- a/utilities.py +++ b/utilities.py @@ -1,159 +1,12 @@ -from collections import namedtuple +from functools import partial import hashlib import re from pandas import read_csv from PyQt5.QtWidgets import QMessageBox -# class _ReadOnlyProperty(object): -# def __init__(self, value): -# self.__value = value - -# def __get__(self, obj, objtype): -# return self.__value +import constants -# def __set__(self, obj, value): -# return NotImplementedError("Cannot change a constant.") - -# def __make_read_only(cls): -# for k, v in cls.__dict__.items(): -# if not callable(getattr(cls, k)) and '__' not in k: -# setattr(cls, k, _ReadOnlyProperty(v)) -# # def raise_err(self, attr, value): -# # raise NotImplementedError("Cannot add an attribute.") -# # setattr(cls, '__setattr__', raise_err) -# return cls - -# @__make_read_only -class Constants(object): - class Messages(object): - NO_DB_AVAIL = "No database available.\nGo to Updates->Update database." - NO_DB = "No database" - DB_LOCATION = "https://aresvalley.com/Storage/Artemis/Database/data.zip" - REF_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log" - DB_NAME = "db.csv" - DB_NAMES = ("name", - "inf_freq", - "sup_freq", - "mode", - "inf_band", - "sup_band", - "location", - "url", - "description", - "modulation", - "category_code", - "acf",) - DB_WIKI_CLICKED = "url_clicked" - DB_STRINGS = ('inf_freq', - 'sup_freq', - 'mode', - 'inf_band', - 'sup_band', - 'category_code',) - DATA_FOLDER = "Data" - SPECTRA_FOLDER = "Spectra" - SPECTRA_EXT = ".png" - AUDIO_FOLDER = "Audio" - THEMES_FOLDER = "themes" - THEME_EXTENSION = ".th" - ICONS_FOLDER = "icons" - DEFAULT_THEME = "1-system" - CURRENT_THEME = ".current_theme" - THEME_COLORS = "colors.txt" - NOT_AVAILABLE = "spectrumnotavailable.png" - NOT_SELECTED = "nosignalselected.png" - SEARCH_LABEL_IMG = "search_icon.png" - VOLUME_LABEL_IMG = "volume.png" - __Band = namedtuple("Band", ["lower", "upper"]) - __ELF = __Band(0, 30) # Formally it is (3, 30) Hz. - __SLF = __Band(30, 300) - __ULF = __Band(300, 3000) - __VLF = __Band(3000, 30000) - __LF = __Band(30 * 10**3, 300 * 10**3) - __MF = __Band(300 * 10 ** 3, 3000 * 10**3) - __HF = __Band(3 * 10**6, 30 * 10**6) - __VHF = __Band(30 * 10**6, 300 * 10**6) - __UHF = __Band(300 * 10**6, 3000 * 10**6) - __SHF = __Band(3 * 10**9, 30 * 10**9) - __EHF = __Band(30 * 10**9, 300 * 10**9) - BANDS = (__ELF, __SLF, __ULF, __VLF, __LF, __MF, __HF, __VHF, __UHF, __SHF, __EHF) - ACTIVE_COLOR = "#39eaff" - INACTIVE_COLOR = "#9f9f9f" - CONVERSION_FACTORS = {"Hz" : 1, - "kHz": 1000, - "MHz": 1000000, - "GHz": 1000000000} - MODES = {"FM": ("NFM", "WFM"), - "AM": (), - "CW": (), - "SK": ("FSK", "PSK", "MSK"), - "SB": ("LSB", "USB", "DSB"), - "Chirp Spread Spectrum": (), - "FHSS-TDM": (), - "RAW": (), - "SC-FDMA": (),} - APPLY = "Apply" - REMOVE = "Remove" - UNKNOWN = "N/A" - MODULATIONS = ("8VSB", - "AFSK", - "AM", - "BFSK", - "C4FM", - "CDMA", - "COFDM", - "CW", - "FFSK", - "FM", - "FMCW", - "FMOP", - "FSK", - "GFSK", - "GMSK", - "IFK", - "MFSK", - "MSK", - "OFDM", - "OOK", - "PAM", - "PPM", - "PSK", - "QAM", - "TDMA",) - LOCATIONS = (UNKNOWN, - "Australia", - "Canada", - "Central Europe", - "China", - "Cyprus", - "Eastern Europe", - "Europe", - "Europe, japan and Asia", - "Exmouth, Australia", - "Finland", - "France", - "Germany", - "Home Base Mobile , AL", - "Hungary", - "Iran", - "Israel", - "Japan", - "LaMour, North Dakota", - "Lualualei, Hawaii", - "North America", - "North Korea", - "Poland", - "Romania", - "Ruda, Sweden", - "UK", - "United Kingdom", - "United States", - "Varberg, Sweden", - "World Wide", - "Worldwide",) - -# Constants = __Constants() def reset_apply_remove_btn(button): if button.isChecked(): @@ -165,23 +18,70 @@ def throwable_message(cls, title, text, connection = None): msg.setWindowTitle(title) msg.setText(text) if connection: - msg.setText(text).finished.connect(connection) + msg.finished.connect(connection) return msg def checksum_ok(data, what): code = hashlib.sha256() code.update(data) - if what == "folder": + if what == constants.ChecksumWhat.FOLDER: n = 0 - elif what == "db": + elif what == constants.ChecksumWhat.DB: n = 1 else: raise ValueError("Wrong entry name.") try: - reference = read_csv(Constants.REF_LOC, delimiter = '*').iat[-1, n] + reference = read_csv(constants.Database.LINK_REF, + delimiter = constants.Database.DELIMITER).iat[-1, n] except HTTPError: return False return code.hexdigest() == reference def is_valid_html_color(color): return bool(re.match("#([a-zA-Z0-9]){6}", color)) + +def connect_to(objects_to_connect, fun_to_connect, fun_args): + for signal in objects_to_connect: + signal.connect(partial(fun_to_connect, *fun_args)) + +def filters_ok(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 + +def is_undef_freq(current_signal): + lower_freq = current_signal.at[constants.Signal.INF_FREQ] + upper_freq = current_signal.at[constants.Signal.SUP_FREQ] + return lower_freq == constants.UNKNOWN or upper_freq == constants.UNKNOWN + +def is_undef_band(current_signal): + lower_band = current_signal.at[constants.Signal.INF_BAND] + upper_band = current_signal.at[constants.Signal.SUP_BAND] + return lower_band == constants.UNKNOWN or upper_band == constants.UNKNOWN + +def change_unit(num): + digits = len(num) + if digits < 4: + return 1 + elif digits < 7: + return 1000 + elif digits < 10: + return 10**6 + else: + return 10**9 + +def format_numbers(lower, upper): + units = {1: 'Hz', 1000: 'kHz', 10**6: 'MHz', 10**9: 'GHz'} + lower_factor = change_unit(lower) + upper_factor = change_unit(upper) + pre_lower = lower + pre_upper = upper + lower = int(lower) / lower_factor + upper = int(upper) / upper_factor + if lower.is_integer(): + lower = int(lower) + if upper.is_integer(): + upper = int(upper) + if pre_lower != pre_upper: + return f"{lower:,} {units[lower_factor]} - {upper:,} {units[upper_factor]}" + else: + return f"{lower:,} {units[lower_factor]}" \ No newline at end of file