Refactoring

This commit is contained in:
alessandro90
2019-03-18 20:18:30 +01:00
parent d4c13f09a7
commit c284f13d7f
13 changed files with 659 additions and 366 deletions

4
.gitignore vendored
View File

@@ -3,7 +3,9 @@ Data
.ipynb_checkpoints .ipynb_checkpoints
*.ipynb *.ipynb
wav_converter.py wav_converter.py
*.txt to_do.txt
csv_info.txt
pyinstaller_cmd.txt
icons_imgs icons_imgs
TestData TestData
themes/.current_theme themes/.current_theme

View File

@@ -4,7 +4,7 @@ from pydub import AudioSegment
from pygame import mixer from pygame import mixer
from PyQt5.QtCore import QTimer, QTimer, pyqtSlot, QObject from PyQt5.QtCore import QTimer, QTimer, pyqtSlot, QObject
from utilities import Constants import constants
import qtawesome as qta import qtawesome as qta
@@ -82,7 +82,7 @@ class AudioPlayer(QObject): # Maybe useless inheriting from QObject
def set_audio_player(self, fname = ""): def set_audio_player(self, fname = ""):
self.__first_call = True self.__first_call = True
self.__reset_audio_widget() 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): if os.path.exists(full_name):
self.__play.setEnabled(True) self.__play.setEnabled(True)
self.__audio_file = full_name self.__audio_file = full_name

168
constants.py Normal file
View File

@@ -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",)

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>151</height> <height>137</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -29,7 +29,7 @@
</property> </property>
<property name="text"> <property name="text">
<string>Downloading database <string>Downloading database
Please wait</string> Please wait...</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>

View File

@@ -1,7 +1,9 @@
from PyQt5 import uic from PyQt5 import uic
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import QWidget, QMessageBox from PyQt5.QtWidgets import QWidget
from threads import DownloadThread, ThreadStatus from threads import DownloadThread, ThreadStatus
from utilities import throwable_message
from constants import Messages
Ui_Download_window, _ = uic.loadUiType("download_db_window.ui") Ui_Download_window, _ = uic.loadUiType("download_db_window.ui")
@@ -17,24 +19,19 @@ class DownloadWindow(QWidget, Ui_Download_window):
# Qt.WindowStaysOnTopHint # Qt.WindowStaysOnTopHint
) )
self.everything_ok = True 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.no_internet_msg = throwable_message(self, title = Messages.NO_CONNECTION,
self.bad_db_download_msg.setWindowTitle("Something wrong") text = Messages.NO_CONNECTION_MSG,
self.bad_db_download_msg.setText("""Something went wrong with the downaload. connection = self.close)
Check your internet connection and try again.""")
self.bad_db_download_msg.finished.connect(self.close)
self.bad_file_msg = QMessageBox(self) self.bad_db_download_msg = throwable_message(self, title = Messages.BAD_DOWNLOAD,
self.bad_file_msg.setWindowTitle("Bad file detected") text = Messages.BAD_DOWNLOAD_MSG,
self.bad_file_msg.setText("""The downloaded file seems to be corrupted. connection = self.close)
The old database has not been deleted and
the downloaded file has been discarded.""") # Never used (should exploit the checksum check for the single file)
self.bad_file_msg.finished.connect(self.close) 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 = DownloadThread()
self.download_thread.finished.connect(self.wait_close) 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) self.cancel_btn.clicked.connect(self.terminate_process)
def show_no_connection_warning(self): 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.no_internet_msg.show()
self.everything_ok = False self.everything_ok = False
@@ -51,6 +46,10 @@ class DownloadWindow(QWidget, Ui_Download_window):
self.bad_db_download_msg.show() self.bad_db_download_msg.show()
self.everything_ok = False self.everything_ok = False
def show_bad_file_warning(self):
self.bad_file_msg.show()
self.everything_ok = False
@pyqtSlot() @pyqtSlot()
def terminate_process(self): def terminate_process(self):
if self.download_thread.isRunning(): if self.download_thread.isRunning():
@@ -65,7 +64,7 @@ class DownloadWindow(QWidget, Ui_Download_window):
elif self.download_thread.status == ThreadStatus.NO_CONNECTION_ERR: elif self.download_thread.status == ThreadStatus.NO_CONNECTION_ERR:
self.show_no_connection_warning() self.show_no_connection_warning()
elif self.download_thread.status == ThreadStatus.BAD_DOWNLOAD_ERR: elif self.download_thread.status == ThreadStatus.BAD_DOWNLOAD_ERR:
self.show_bad_download_warning self.show_bad_download_warning()
else: else:
self.close() self.close()

282
main.py
View File

@@ -9,7 +9,6 @@ from pandas import read_csv
from PyQt5.QtWidgets import (QMainWindow, from PyQt5.QtWidgets import (QMainWindow,
QApplication, QApplication,
QAction, QAction,
QMessageBox,
qApp, qApp,
QDesktopWidget, QDesktopWidget,
QListWidgetItem, QListWidgetItem,
@@ -27,17 +26,23 @@ from audio_player import AudioPlayer
from double_text_button import DoubleTextButton from double_text_button import DoubleTextButton
from download_window import DownloadWindow from download_window import DownloadWindow
import constants
from utilities import (Constants, from utilities import (reset_apply_remove_btn,
reset_apply_remove_btn,
throwable_message, 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" qt_creator_file = "main_window.ui"
Ui_MainWindow, _ = uic.loadUiType(qt_creator_file) Ui_MainWindow, _ = uic.loadUiType(qt_creator_file)
class MyApp(QMainWindow, Ui_MainWindow):
class MyApp(QMainWindow, Ui_MainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setupUi(self) self.setupUi(self)
@@ -49,8 +54,8 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.current_signal_name = '' self.current_signal_name = ''
self.signal_names = [] self.signal_names = []
self.total_signals = 0 self.total_signals = 0
self.active_color = Constants.ACTIVE_COLOR self.active_color = constants.Theme.DEFAULT_ACTIVE_COLOR
self.inactive_color = Constants.INACTIVE_COLOR self.inactive_color = constants.Theme.DEFAULT_INACTIVE_COLOR
# Manage frequency filters. # Manage frequency filters.
self.frequency_filters_btns = ( self.frequency_filters_btns = (
@@ -67,7 +72,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.ehf_filter_btn, self.ehf_filter_btn,
) )
self.connect_to( connect_to(
objects_to_connect = [self.lower_freq_spinbox.valueChanged, objects_to_connect = [self.lower_freq_spinbox.valueChanged,
self.upper_freq_spinbox.valueChanged, self.upper_freq_spinbox.valueChanged,
self.lower_freq_filter_unit.currentTextChanged, self.lower_freq_filter_unit.currentTextChanged,
@@ -80,7 +85,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.upper_freq_spinbox] self.upper_freq_spinbox]
) )
self.connect_to( connect_to(
objects_to_connect = [self.lower_freq_spinbox.valueChanged, objects_to_connect = [self.lower_freq_spinbox.valueChanged,
self.upper_freq_spinbox.valueChanged, self.upper_freq_spinbox.valueChanged,
self.lower_freq_filter_unit.currentTextChanged, self.lower_freq_filter_unit.currentTextChanged,
@@ -117,7 +122,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.upper_freq_confidence) 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.apply_remove_freq_filter_btn.set_slave_filters(
[ [
*self.frequency_filters_btns, *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.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. # Manage bandwidth filters.
self.connect_to( connect_to(
objects_to_connect = [self.lower_band_spinbox.valueChanged, objects_to_connect = [self.lower_band_spinbox.valueChanged,
self.upper_band_spinbox.valueChanged, self.upper_band_spinbox.valueChanged,
self.lower_band_filter_unit.currentTextChanged, self.lower_band_filter_unit.currentTextChanged,
@@ -156,7 +161,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.upper_band_spinbox] self.upper_band_spinbox]
) )
self.connect_to( connect_to(
objects_to_connect = [self.lower_band_spinbox.valueChanged, objects_to_connect = [self.lower_band_spinbox.valueChanged,
self.upper_band_spinbox.valueChanged, self.upper_band_spinbox.valueChanged,
self.lower_band_filter_unit.currentTextChanged, self.lower_band_filter_unit.currentTextChanged,
@@ -193,7 +198,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.upper_band_confidence) 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.apply_remove_band_filter_btn.set_slave_filters(
[ [
self.include_undef_bands, 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.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 # Manage category filters
@@ -237,7 +242,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.number_stations_btn, self.number_stations_btn,
self.time_signal_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.apply_remove_cat_filter_btn.set_slave_filters([*self.cat_filter_btns,
self.cat_at_least_one, self.cat_at_least_one,
self.cat_all]) self.cat_all])
@@ -283,16 +288,16 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.set_mode_tree_widget() self.set_mode_tree_widget()
self.mode_tree_widget.itemSelectionChanged.connect(self.manage_mode_selections) self.mode_tree_widget.itemSelectionChanged.connect(self.manage_mode_selections)
self.reset_mode_filters_btn.clicked.connect(self.reset_mode_filters) 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.apply_remove_mode_filter_btn.set_slave_filters([self.mode_tree_widget,
self.include_unknown_modes_btn]) self.include_unknown_modes_btn])
self.apply_remove_mode_filter_btn.clicked.connect(self.display_signals) self.apply_remove_mode_filter_btn.clicked.connect(self.display_signals)
# Set modulation filter screen. # 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.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.apply_remove_modulation_filter_btn.set_slave_filters([self.search_bar_modulation,
self.modulation_list]) self.modulation_list])
self.apply_remove_modulation_filter_btn.clicked.connect(self.display_signals) self.apply_remove_modulation_filter_btn.clicked.connect(self.display_signals)
@@ -301,9 +306,9 @@ class MyApp(QMainWindow, Ui_MainWindow):
# Set location filter screen. # 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.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.apply_remove_location_filter_btn.set_slave_filters([self.search_bar_location,
self.locations_list]) self.locations_list])
self.apply_remove_location_filter_btn.clicked.connect(self.display_signals) 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) self.locations_list.itemClicked.connect(self.remove_if_unselected_location)
# Find available themes. # Find available themes.
self.default_images_folder = os.path.join(Constants.THEMES_FOLDER, self.default_images_folder = os.path.join(constants.Theme.FOLDER,
Constants.DEFAULT_THEME, constants.Theme.DEFAULT,
Constants.ICONS_FOLDER) constants.Theme.ICONS_FOLDER)
# self.find_themes()
# self.set_theme()
# ########################################################################################## # ##########################################################################################
@@ -357,10 +360,10 @@ class MyApp(QMainWindow, Ui_MainWindow):
def find_themes(self): def find_themes(self):
themes = [] themes = []
for theme_folder in os.listdir(Constants.THEMES_FOLDER): for theme_folder in os.listdir(constants.Theme.FOLDER):
relative_folder = os.path.join(Constants.THEMES_FOLDER, theme_folder) relative_folder = os.path.join(constants.Theme.FOLDER, theme_folder)
if os.path.isdir(os.path.abspath(relative_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) themes.append(relative_folder)
for theme in themes: for theme in themes:
theme_name = '&' + ' '.join( theme_name = '&' + ' '.join(
@@ -382,26 +385,26 @@ class MyApp(QMainWindow, Ui_MainWindow):
try: try:
with open(os.path.join( with open(os.path.join(
theme_path, theme_path,
os.path.basename(theme_path).split('-')[1] + Constants.THEME_EXTENSION) os.path.basename(theme_path).split('-')[1] + constants.Theme.EXTENSION)
) as stylesheet: ) as stylesheet:
style = stylesheet.read() style = stylesheet.read()
self.setStyleSheet(style) self.setStyleSheet(style)
self.download_window.setStyleSheet(style) self.download_window.setStyleSheet(style)
except FileNotFoundError: except FileNotFoundError:
throwable_message(self, title = "Theme not found", throwable_message(self, title = constants.Messages.THEME_NOT_FOUND,
text = f"Missing theme in {Constants.THEMES_FOLDER} folder.").show() text = constants.Messages.MISSING_THEME).show()
else: else:
icons_path = os.path.join(theme_path, Constants.ICONS_FOLDER) icons_path = os.path.join(theme_path, constants.Theme.ICONS_FOLDER)
default_icons_path = os.path.join(Constants.THEMES_FOLDER, Constants.DEFAULT_THEME, Constants.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 \ if os.path.exists(os.path.join(icons_path, constants.NOT_SELECTED)) and \
os.path.exists(os.path.join(icons_path, Constants.NOT_AVAILABLE)): os.path.exists(os.path.join(icons_path, constants.NOT_AVAILABLE)):
self.default_images_folder = icons_path self.default_images_folder = icons_path
else: else:
self.default_images_folder = default_icons_path self.default_images_folder = default_icons_path
path_to_search_label = os.path.join(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) default_search_label = os.path.join(default_icons_path, constants.SEARCH_LABEL_IMG)
if os.path.exists(path_to_search_label): if os.path.exists(path_to_search_label):
self.search_label.setPixmap(QPixmap(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.modulation_search_label.setScaledContents(True)
self.location_search_label.setScaledContents(True) self.location_search_label.setScaledContents(True)
path_to_volume_label = os.path.join(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) default_volume_label = os.path.join(default_icons_path, constants.VOLUME_LABEL_IMG)
if os.path.exists(path_to_volume_label): if os.path.exists(path_to_volume_label):
self.volume_label.setPixmap(QPixmap(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) 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 active_color_ok = False
inactive_color_ok = False inactive_color_ok = False
valid_format = False valid_format = False
@@ -435,36 +438,36 @@ class MyApp(QMainWindow, Ui_MainWindow):
valid_file = True valid_file = True
with open(path_to_colors, "r") as colors_file: with open(path_to_colors, "r") as colors_file:
for line in colors_file: for line in colors_file:
if '=' in line: if constants.Theme.COLOR_SEPARATOR in line:
valid_format = True valid_format = True
quality, color = line.split("=") quality, color = line.split(constants.Theme.COLOR_SEPARATOR)
color = color.rstrip() 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 self.active_color = color
active_color_ok = True 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 self.inactive_color = color
inactive_color_ok = True inactive_color_ok = True
if not all([valid_file, valid_format, active_color_ok, inactive_color_ok]): if not all([valid_file, valid_format, active_color_ok, inactive_color_ok]):
self.active_color = Constants.ACTIVE_COLOR self.active_color = constants.Theme.DEFAULT_ACTIVE_COLOR
self.inactive_color = Constants.INACTIVE_COLOR self.inactive_color = constants.Theme.DEFAULT_INACTIVE_COLOR
self.audio_widget.refresh_btns_colors(self.active_color, self.inactive_color) self.audio_widget.refresh_btns_colors(self.active_color, self.inactive_color)
try: try:
with open(os.path.join(Constants.THEMES_FOLDER, with open(os.path.join(constants.Theme.FOLDER,
Constants.CURRENT_THEME), "w") as current_theme: constants.Theme.CURRENT), "w") as current_theme:
current_theme.write(theme_path) current_theme.write(theme_path)
except: except:
pass pass
def set_theme(self): 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): if os.path.exists(current_theme_file):
with open(current_theme_file) as current_theme: with open(current_theme_file) as current_theme:
theme = current_theme.read() theme = current_theme.read()
if theme != Constants.DEFAULT_THEME: if theme != constants.Theme.DEFAULT:
self.change_theme(theme) self.change_theme(theme)
@pyqtSlot(QListWidgetItem) @pyqtSlot(QListWidgetItem)
@@ -494,7 +497,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
item.setHidden(True) item.setHidden(True)
def set_mode_tree_widget(self): def set_mode_tree_widget(self):
for parent, children in Constants.MODES.items(): for parent, children in constants.MODES.items():
iparent = QTreeWidgetItem([parent]) iparent = QTreeWidgetItem([parent])
self.mode_tree_widget.addTopLevelItem(iparent) self.mode_tree_widget.addTopLevelItem(iparent)
for child in children: for child in children:
@@ -504,11 +507,11 @@ class MyApp(QMainWindow, Ui_MainWindow):
def manage_mode_selections(self): def manage_mode_selections(self):
selected_items = self.mode_tree_widget.selectedItems() selected_items = self.mode_tree_widget.selectedItems()
parents = Constants.MODES.keys() parents = constants.MODES.keys()
for parent in parents: for parent in parents:
for item in selected_items: for item in selected_items:
if parent == item.text(0): 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) item.child(i).setSelected(True)
def set_initial_size(self): def set_initial_size(self):
@@ -571,32 +574,25 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.display_signals() self.display_signals()
def load_db(self): def load_db(self):
names = Constants.DB_NAMES names = constants.Database.NAMES
try: try:
self.db = read_csv(os.path.join(Constants.DATA_FOLDER, Constants.DB_NAME), self.db = read_csv(os.path.join(constants.DATA_FOLDER, constants.Database.NAME),
sep = '*', sep = constants.Database.DELIMITER,
header = None, header = None,
index_col = 0, index_col = 0,
dtype = {name : str for name in Constants.DB_STRINGS}, dtype = {name : str for name in constants.Database.STRINGS},
names = names,) names = names,)
except FileNotFoundError: except FileNotFoundError:
self.search_bar.setDisabled(True) self.search_bar.setDisabled(True)
box = QMessageBox(self) throwable_message(self, title = constants.Messages.NO_DB,
box.setWindowTitle(Constants.Messages.NO_DB) text = constants.Messages.NO_DB_AVAIL).show()
box.setText(Constants.Messages.NO_DB_AVAIL)
box.show()
else: else:
self.signal_names = self.db.index self.signal_names = self.db.index
self.total_signals = len(self.signal_names) self.total_signals = len(self.signal_names)
self.db.fillna(Constants.UNKNOWN, inplace = True) self.db.fillna(constants.UNKNOWN, inplace = True)
self.db[Constants.DB_WIKI_CLICKED] = False self.db[constants.Signal.WIKI_CLICKED] = False
self.update_status_tip(self.total_signals) 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() @pyqtSlot()
def set_min_value_upper_limit(self, lower_combo_box, def set_min_value_upper_limit(self, lower_combo_box,
lower_spin_box, lower_spin_box,
@@ -605,14 +601,13 @@ class MyApp(QMainWindow, Ui_MainWindow):
if lower_spin_box.isEnabled(): if lower_spin_box.isEnabled():
unit_conversion = {'Hz' : ['kHz', 'MHz', 'GHz'], unit_conversion = {'Hz' : ['kHz', 'MHz', 'GHz'],
'kHz': ['MHz', 'GHz'], 'kHz': ['MHz', 'GHz'],
'MHz': ['GHz'] 'MHz': ['GHz']}
}
lower_units = lower_combo_box.currentText() lower_units = lower_combo_box.currentText()
upper_units = upper_combo_box.currentText() upper_units = upper_combo_box.currentText()
lower_value = lower_spin_box.value() lower_value = lower_spin_box.value()
upper_value = upper_spin_box.value() upper_value = upper_spin_box.value()
inf_limit = (lower_value * Constants.CONVERSION_FACTORS[lower_units]) \ inf_limit = (lower_value * constants.CONVERSION_FACTORS[lower_units]) \
// Constants.CONVERSION_FACTORS[upper_units] // constants.CONVERSION_FACTORS[upper_units]
counter = 0 counter = 0
while inf_limit > upper_spin_box.maximum(): while inf_limit > upper_spin_box.maximum():
counter += 1 counter += 1
@@ -685,8 +680,6 @@ class MyApp(QMainWindow, Ui_MainWindow):
@pyqtSlot() @pyqtSlot()
def display_signals(self): def display_signals(self):
# for i in range(self.result_list.count()):
# self.result_list.item(i).setHidden(True)
text = self.search_bar.text() text = self.search_bar.text()
available_signals = 0 available_signals = 0
for index, signal in enumerate(self.signal_names): for index, signal in enumerate(self.signal_names):
@@ -712,7 +705,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
@pyqtSlot() @pyqtSlot()
def reset_fb_filters(self, ftype): 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'") raise ValueError("Wrong ftype in function 'reset_fb_filters'")
apply_remove_btn = getattr(self, 'apply_remove_' + ftype + '_filter_btn') apply_remove_btn = getattr(self, 'apply_remove_' + ftype + '_filter_btn')
include_undef_btn = getattr(self, 'include_undef_' + ftype + 's') 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') upper_spinbox = getattr(self, 'upper_' + ftype + '_spinbox')
lower_confidence = getattr(self, 'lower_' + ftype + '_confidence') lower_confidence = getattr(self, 'lower_' + ftype + '_confidence')
upper_confidence = getattr(self, 'lower_' + ftype + '_confidence') upper_confidence = getattr(self, 'lower_' + ftype + '_confidence')
default_val = 1 if ftype == 'freq' else 5000 default_val = 1 if ftype == constants.Ftype.FREQ else 5000
if ftype == 'freq': if ftype == constants.Ftype.FREQ:
for f in self.frequency_filters_btns: for f in self.frequency_filters_btns:
if f.isChecked(): if f.isChecked():
f.setChecked(False) f.setChecked(False)
@@ -776,19 +769,19 @@ class MyApp(QMainWindow, Ui_MainWindow):
def frequency_filters_ok(self, signal_name): def frequency_filters_ok(self, signal_name):
if not self.apply_remove_freq_filter_btn.isChecked(): if not self.apply_remove_freq_filter_btn.isChecked():
return True 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 undef_freq:
if self.include_undef_freqs.isChecked(): if self.include_undef_freqs.isChecked():
return True return True
else: else:
return False return False
signal_freqs = (int(self.db.at[signal_name, "inf_freq"]), signal_freqs = (int(self.db.at[signal_name, constants.Signal.INF_FREQ]),
int(self.db.at[signal_name, "sup_freq"])) int(self.db.at[signal_name, constants.Signal.SUP_FREQ]))
band_filter_ok = False band_filter_ok = False
any_checked = 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(): if btn.isChecked():
any_checked = True any_checked = True
if signal_freqs[0] < band_limits.upper and signal_freqs[1] >= band_limits.lower: if signal_freqs[0] < band_limits.upper and signal_freqs[1] >= band_limits.lower:
@@ -796,12 +789,12 @@ class MyApp(QMainWindow, Ui_MainWindow):
lower_limit_ok = True lower_limit_ok = True
upper_limit_ok = True upper_limit_ok = True
if self.activate_low_freq_filter_btn.isChecked(): if self.activate_low_freq_filter_btn.isChecked():
if not signal_freqs[1] >= self.filters_ok(self.lower_freq_spinbox, if not signal_freqs[1] >= filters_ok(self.lower_freq_spinbox,
self.lower_freq_filter_unit, self.lower_freq_filter_unit,
self.lower_freq_confidence, -1): self.lower_freq_confidence, -1):
lower_limit_ok = False lower_limit_ok = False
if self.activate_up_freq_filter_btn.isChecked(): if self.activate_up_freq_filter_btn.isChecked():
if not signal_freqs[0] < self.filters_ok(self.upper_freq_spinbox, if not signal_freqs[0] < filters_ok(self.upper_freq_spinbox,
self.upper_freq_filter_unit, self.upper_freq_filter_unit,
self.upper_freq_confidence): self.upper_freq_confidence):
upper_limit_ok = False upper_limit_ok = False
@@ -813,25 +806,25 @@ class MyApp(QMainWindow, Ui_MainWindow):
def band_filters_ok(self, signal_name): def band_filters_ok(self, signal_name):
if not self.apply_remove_band_filter_btn.isChecked(): if not self.apply_remove_band_filter_btn.isChecked():
return True 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 undef_band:
if self.include_undef_bands.isChecked(): if self.include_undef_bands.isChecked():
return True return True
else: else:
return False return False
signal_bands = (int(self.db.at[signal_name, "inf_band"]), signal_bands = (int(self.db.at[signal_name, constants.Signal.INF_BAND]),
int(self.db.at[signal_name, "sup_band"])) int(self.db.at[signal_name, constants.Signal.SUP_BAND]))
lower_limit_ok = True lower_limit_ok = True
upper_limit_ok = True upper_limit_ok = True
if self.activate_low_band_filter_btn.isChecked(): if self.activate_low_band_filter_btn.isChecked():
if not signal_bands[1] >= self.filters_ok(self.lower_band_spinbox, if not signal_bands[1] >= filters_ok(self.lower_band_spinbox,
self.lower_band_filter_unit, self.lower_band_filter_unit,
self.lower_band_confidence, -1): self.lower_band_confidence, -1):
lower_limit_ok = False lower_limit_ok = False
if self.activate_up_band_filter_btn.isChecked(): if self.activate_up_band_filter_btn.isChecked():
if not signal_bands[0] < self.filters_ok(self.upper_band_spinbox, if not signal_bands[0] < filters_ok(self.upper_band_spinbox,
self.upper_band_filter_unit, self.upper_band_filter_unit,
self.upper_band_confidence): self.upper_band_confidence):
upper_limit_ok = False upper_limit_ok = False
@@ -840,7 +833,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
def category_filters_ok(self, signal_name): def category_filters_ok(self, signal_name):
if not self.apply_remove_cat_filter_btn.isChecked(): if not self.apply_remove_cat_filter_btn.isChecked():
return True 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 cat_checked = 0
positive_cases = 0 positive_cases = 0
for index, cat in enumerate(self.cat_filter_btns): 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): def mode_filters_ok(self, signal_name):
if not self.apply_remove_mode_filter_btn.isChecked(): if not self.apply_remove_mode_filter_btn.isChecked():
return True return True
signal_mode = self.db.at[signal_name, "mode"] signal_mode = self.db.at[signal_name, constants.Signal.MODE]
if signal_mode == Constants.UNKNOWN: if signal_mode == constants.UNKNOWN:
if self.include_unknown_modes_btn.isChecked(): if self.include_unknown_modes_btn.isChecked():
return True return True
else: else:
return False return False
selected_items = [item for item in self.mode_tree_widget.selectedItems()] selected_items = [item for item in self.mode_tree_widget.selectedItems()]
selected_items_text = [i.text(0) for i in selected_items] 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] children = [item for item in selected_items_text if item not in parents]
ok = [] ok = []
for item in selected_items: for item in selected_items:
@@ -877,7 +870,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
def modulation_filters_ok(self, signal_name): def modulation_filters_ok(self, signal_name):
if not self.apply_remove_modulation_filter_btn.isChecked(): if not self.apply_remove_modulation_filter_btn.isChecked():
return True 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(): for item in self.modulation_list.selectedItems():
if item.text() == signal_modulation: if item.text() == signal_modulation:
return True return True
@@ -886,17 +879,12 @@ class MyApp(QMainWindow, Ui_MainWindow):
def location_filters_ok(self, signal_name): def location_filters_ok(self, signal_name):
if not self.apply_remove_location_filter_btn.isChecked(): if not self.apply_remove_location_filter_btn.isChecked():
return True 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(): for item in self.locations_list.selectedItems():
if item.text() == signal_location: if item.text() == signal_location:
return True return True
return False 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) @pyqtSlot(QListWidgetItem, QListWidgetItem)
def display_specs(self, item, previous_item): def display_specs(self, item, previous_item):
self.display_spectrogram() self.display_spectrogram()
@@ -906,33 +894,31 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.name_lab.setAlignment(Qt.AlignHCenter) self.name_lab.setAlignment(Qt.AlignHCenter)
current_signal = self.db.loc[self.current_signal_name] current_signal = self.db.loc[self.current_signal_name]
self.url_button.setEnabled(True) 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};") self.url_button.setStyleSheet(f"color: {self.url_button.colors.active};")
else: 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["category_code"] category_code = current_signal.at[constants.Signal.CATEGORY_CODE]
undef_freq = self.is_undef_freq(current_signal) undef_freq = is_undef_freq(current_signal)
undef_band = self.is_undef_band(current_signal) undef_band = is_undef_band(current_signal)
if not undef_freq: if not undef_freq:
self.freq_lab.setText(self.format_numbers( self.freq_lab.setText(format_numbers(current_signal.at[constants.Signal.INF_FREQ],
current_signal.at["inf_freq"], current_signal.at[constants.Signal.SUP_FREQ])
current_signal.at["sup_freq"])
) )
else: else:
self.freq_lab.setText("Undefined") self.freq_lab.setText("Undefined")
if not undef_band: if not undef_band:
self.band_lab.setText(self.format_numbers( self.band_lab.setText(format_numbers(current_signal.at[constants.Signal.INF_BAND],
current_signal.at["inf_band"], current_signal.at[constants.Signal.SUP_BAND])
current_signal.at["sup_band"])
) )
else: else:
self.band_lab.setText("Undefined") self.band_lab.setText("Undefined")
self.mode_lab.setText(current_signal.at["mode"]) self.mode_lab.setText(current_signal.at[constants.Signal.MODE])
self.modul_lab.setText(current_signal.at["modulation"]) self.modul_lab.setText(current_signal.at[constants.Signal.MODULATION])
self.loc_lab.setText(current_signal.at["location"]) self.loc_lab.setText(current_signal.at[constants.Signal.LOCATION])
self.acf_lab.setText(current_signal.at["acf"]) self.acf_lab.setText(current_signal.at[constants.Signal.ACF])
self.description_text.setText(current_signal.at["description"]) self.description_text.setText(current_signal.at[constants.Signal.DESCRIPTION])
for cat, cat_lab in zip(category_code, self.category_labels): for cat, cat_lab in zip(category_code, self.category_labels):
if cat == '0': if cat == '0':
cat_lab.setStyleSheet(f"color: {self.inactive_color};") 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.setText("No Signal")
self.name_lab.setAlignment(Qt.AlignHCenter) self.name_lab.setAlignment(Qt.AlignHCenter)
for lab in self.property_labels: for lab in self.property_labels:
lab.setText(Constants.UNKNOWN) lab.setText(constants.UNKNOWN)
for lab in self.category_labels: for lab in self.category_labels:
lab.setStyleSheet(f"color: {self.inactive_color};") lab.setStyleSheet(f"color: {self.inactive_color};")
self.set_band_range() self.set_band_range()
self.audio_widget.set_audio_player() 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): 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() item = self.result_list.currentItem()
if item: if item:
spectrogram_name = item.text() spectrogram_name = item.text()
path_spectr = os.path.join(Constants.DATA_FOLDER, path_spectr = os.path.join(constants.DATA_FOLDER,
Constants.SPECTRA_FOLDER, constants.SPECTRA_FOLDER,
spectrogram_name + Constants.SPECTRA_EXT) spectrogram_name + constants.SPECTRA_EXT)
if not QFileInfo(path_spectr).exists(): 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: else:
path_spectr = default_pic path_spectr = default_pic
self.spectrogram.setPixmap(QPixmap(path_spectr)) self.spectrogram.setPixmap(QPixmap(path_spectr))
@@ -1015,10 +959,10 @@ class MyApp(QMainWindow, Ui_MainWindow):
label.setStyleSheet(f"color: {color};") label.setStyleSheet(f"color: {color};")
def set_band_range(self, current_signal = None): def set_band_range(self, current_signal = None):
if current_signal is not None and not self.is_undef_freq(current_signal): if current_signal is not None and not is_undef_freq(current_signal):
lower_freq = int(current_signal.at["inf_freq"]) lower_freq = int(current_signal.at[constants.Signal.INF_FREQ])
upper_freq = int(current_signal.at["sup_freq"]) upper_freq = int(current_signal.at[constants.Signal.SUP_FREQ])
zipped = list(zip(Constants.BANDS, self.band_labels)) zipped = list(zip(constants.BANDS, self.band_labels))
for i, w in enumerate(zipped): for i, w in enumerate(zipped):
band, band_label = w band, band_label = w
if lower_freq >= band.lower and lower_freq < band.upper: 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): def go_to_web_page_signal(self):
if self.current_signal_name: 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, "url"]) webbrowser.open(self.db.at[self.current_signal_name, constants.Signal.URL])
self.db.at[self.current_signal_name, "url_clicked"] = True self.db.at[self.current_signal_name, constants.Signal.WIKI_CLICKED] = True
def closeEvent(self, event): def closeEvent(self, event):
if self.download_window.isVisible(): if self.download_window.isVisible():

View File

@@ -174,7 +174,7 @@
<enum>QTabWidget::Rounded</enum> <enum>QTabWidget::Rounded</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<property name="movable"> <property name="movable">
<bool>true</bool> <bool>true</bool>
@@ -1771,7 +1771,7 @@ p, li { white-space: pre-wrap; }
<string notr="true"/> <string notr="true"/>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>5</number> <number>6</number>
</property> </property>
<property name="movable"> <property name="movable">
<bool>true</bool> <bool>true</bool>
@@ -3873,6 +3873,282 @@ Inactive</string>
<attribute name="title"> <attribute name="title">
<string>ACF</string> <string>ACF</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QPushButton" name="include_undef_acf">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Include undefined ACFs</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_25">
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>AC interval</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="acf_spinbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100000000</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>49</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_16">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string> Confidence %</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="lower_band_confidence_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::UpDownArrows</enum>
</property>
<property name="specialValueText">
<string/>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="acf_range_lbl">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
<underline>false</underline>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Selected range:
Inactive</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="DoubleTextButton" name="apply_remove_acf_filter_btn">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Apply</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reset_acf_filters_btn">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Reset</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</widget> </widget>
</item> </item>

View File

@@ -0,0 +1,2 @@
active=#ddffdf
inactive=#949494

View File

@@ -6,7 +6,9 @@ from shutil import rmtree
import urllib3 import urllib3
from zipfile import ZipFile from zipfile import ZipFile
from PyQt5.QtCore import QThread from PyQt5.QtCore import QThread
from utilities import checksum_ok, Constants import constants
from utilities import checksum_ok
import constants
class ThreadStatus(Enum): class ThreadStatus(Enum):
OK = auto() OK = auto()
@@ -30,8 +32,8 @@ class DownloadThread(QThread):
def run(self): def run(self):
try: try:
db = urllib3.PoolManager().request('GET', Constants.DB_LOCATION) db = urllib3.PoolManager().request('GET', constants.Database.LINK_LOC)
# db = urllib.request.urlopen(Constants.DB_LOCATION) # db = urllib.request.urlopen(constants.Database.LINK_LOC)
# raise urllib.error.URLError('Test') # raise urllib.error.URLError('Test')
except urllib3.exceptions.MaxRetryError: # No internet connection. except urllib3.exceptions.MaxRetryError: # No internet connection.
self.__status = ThreadStatus.NO_CONNECTION_ERR self.__status = ThreadStatus.NO_CONNECTION_ERR
@@ -40,11 +42,11 @@ class DownloadThread(QThread):
self.reason = db.reason self.reason = db.reason
self.__status = ThreadStatus.BAD_DOWNLOAD_ERR self.__status = ThreadStatus.BAD_DOWNLOAD_ERR
return return
if not checksum_ok(db.data, "folder"): if not checksum_ok(db.data, constants.ChecksumWhat.FOLDER):
self.__status = ThreadStatus.BAD_DOWNLOAD_ERR self.__status = ThreadStatus.BAD_DOWNLOAD_ERR
return return
if os.path.exists(Constants.DATA_FOLDER): if os.path.exists(constants.DATA_FOLDER):
rmtree(Constants.DATA_FOLDER) rmtree(constants.DATA_FOLDER)
try: try:
# data_folder = db.read() # data_folder = db.read()
with ZipFile(BytesIO(db.data)) as zipped: with ZipFile(BytesIO(db.data)) as zipped:

View File

@@ -1,159 +1,12 @@
from collections import namedtuple from functools import partial
import hashlib import hashlib
import re import re
from pandas import read_csv from pandas import read_csv
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
# class _ReadOnlyProperty(object): import constants
# def __init__(self, value):
# self.__value = value
# def __get__(self, obj, objtype):
# return self.__value
# 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): def reset_apply_remove_btn(button):
if button.isChecked(): if button.isChecked():
@@ -165,23 +18,70 @@ def throwable_message(cls, title, text, connection = None):
msg.setWindowTitle(title) msg.setWindowTitle(title)
msg.setText(text) msg.setText(text)
if connection: if connection:
msg.setText(text).finished.connect(connection) msg.finished.connect(connection)
return msg return msg
def checksum_ok(data, what): def checksum_ok(data, what):
code = hashlib.sha256() code = hashlib.sha256()
code.update(data) code.update(data)
if what == "folder": if what == constants.ChecksumWhat.FOLDER:
n = 0 n = 0
elif what == "db": elif what == constants.ChecksumWhat.DB:
n = 1 n = 1
else: else:
raise ValueError("Wrong entry name.") raise ValueError("Wrong entry name.")
try: 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: except HTTPError:
return False return False
return code.hexdigest() == reference return code.hexdigest() == reference
def is_valid_html_color(color): def is_valid_html_color(color):
return bool(re.match("#([a-zA-Z0-9]){6}", 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]}"