Merge pull request #1 from AresValley/space_weather

Space weather
This commit is contained in:
Alessandro Ceccato
2019-04-25 20:00:35 +02:00
committed by GitHub
22 changed files with 3429 additions and 5540 deletions

4
.gitignore vendored
View File

@@ -5,9 +5,7 @@ wav_converter.py
to_do.txt to_do.txt
csv_info.txt csv_info.txt
pyinstaller_cmd.txt pyinstaller_cmd.txt
icons_imgs
TestData
themes/.current_theme themes/.current_theme
*.bat *.bat
*.sh *.sh
splash.jpg .vscode/

View File

@@ -1,6 +1,5 @@
from collections import namedtuple from collections import namedtuple
from functools import partial from functools import partial
from glob import glob
import webbrowser import webbrowser
import os import os
import sys import sys
@@ -14,7 +13,6 @@ from PyQt5.QtWidgets import (QMainWindow,
QListWidgetItem, QListWidgetItem,
QMessageBox, QMessageBox,
QSplashScreen, QSplashScreen,
QTreeView,
QTreeWidgetItem,) QTreeWidgetItem,)
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from PyQt5 import uic from PyQt5 import uic
@@ -23,10 +21,9 @@ from PyQt5.QtCore import (QFileInfo,
pyqtSlot,) pyqtSlot,)
from audio_player import AudioPlayer from audio_player import AudioPlayer
from space_weather_data import SpaceWeatherData
from double_text_button import DoubleTextButton
from download_window import DownloadWindow from download_window import DownloadWindow
from switchable_label import SwitchableLabelsIterable
from constants import (Constants, from constants import (Constants,
Ftype, Ftype,
GfdType, GfdType,
@@ -35,7 +32,6 @@ from constants import (Constants,
Messages, Messages,
Signal,) Signal,)
from themes import Theme from themes import Theme
from utilities import (checksum_ok, from utilities import (checksum_ok,
uncheck_and_emit, uncheck_and_emit,
pop_up, pop_up,
@@ -43,11 +39,10 @@ from utilities import (checksum_ok,
filters_ok, filters_ok,
is_undef_freq, is_undef_freq,
is_undef_band, is_undef_band,
change_unit,
format_numbers, format_numbers,
resource_path,) resource_path,)
import icon_rc # import icon_rc
qt_creator_file = resource_path("artemis.ui") qt_creator_file = resource_path("artemis.ui")
Ui_MainWindow, _ = uic.loadUiType(qt_creator_file) Ui_MainWindow, _ = uic.loadUiType(qt_creator_file)
@@ -59,6 +54,7 @@ class Artemis(QMainWindow, Ui_MainWindow):
self.setupUi(self) self.setupUi(self)
self.set_initial_size() self.set_initial_size()
self.download_window = DownloadWindow() self.download_window = DownloadWindow()
self.download_window.complete.connect(self.show_downloaded_signals)
self.actionExit.triggered.connect(qApp.quit) self.actionExit.triggered.connect(qApp.quit)
self.action_update_database.triggered.connect(self.ask_if_download) self.action_update_database.triggered.connect(self.ask_if_download)
self.action_check_db_ver.triggered.connect(self.check_db_ver) self.action_check_db_ver.triggered.connect(self.check_db_ver)
@@ -66,6 +62,66 @@ class Artemis(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.switchable_r_labels = SwitchableLabelsIterable(self.r0_now_lbl,
self.r1_now_lbl,
self.r2_now_lbl,
self.r3_now_lbl,
self.r4_now_lbl,
self.r5_now_lbl,)
self.switchable_s_labels = SwitchableLabelsIterable(self.s0_now_lbl,
self.s1_now_lbl,
self.s2_now_lbl,
self.s3_now_lbl,
self.s4_now_lbl,
self.s5_now_lbl,)
self.switchable_g_now_labels = SwitchableLabelsIterable(self.g0_now_lbl,
self.g1_now_lbl,
self.g2_now_lbl,
self.g3_now_lbl,
self.g4_now_lbl,
self.g5_now_lbl)
self.switchable_g_today_labels = SwitchableLabelsIterable(self.g0_today_lbl,
self.g1_today_lbl,
self.g2_today_lbl,
self.g3_today_lbl,
self.g4_today_lbl,
self.g5_today_lbl)
self.k_storm_labels = SwitchableLabelsIterable(self.k_ex_sev_storm_lbl,
self.k_very_sev_storm_lbl,
self.k_sev_storm_lbl,
self.k_maj_storm_lbl,
self.k_min_storm_lbl,
self.k_active_lbl,
self.k_unsettled_lbl,
self.k_quiet_lbl,
self.k_very_quiet_lbl,
self.k_inactive_lbl)
self.a_storm_labels = SwitchableLabelsIterable(self.a_sev_storm_lbl,
self.a_maj_storm_lbl,
self.a_min_storm_lbl,
self.a_active_lbl,
self.a_unsettled_lbl,
self.a_quiet_lbl)
self.forecast_labels = (self.forecast_lbl_0,
self.forecast_lbl_1,
self.forecast_lbl_2,
self.forecast_lbl_3,
self.forecast_lbl_4,
self.forecast_lbl_5,
self.forecast_lbl_6,
self.forecast_lbl_7,
self.forecast_lbl_8)
for lab in self.forecast_labels:
lab.set_default_stylesheet()
self.forecast_label_container.labels = self.forecast_labels
self.theme = Theme(self) self.theme = Theme(self)
# Manage frequency filters. # Manage frequency filters.
@@ -346,13 +402,10 @@ class Artemis(QMainWindow, Ui_MainWindow):
# ########################################################################################## # ##########################################################################################
# self.load_db()
# Left list widget and search bar. # Left list widget and search bar.
self.search_bar.textChanged.connect(self.display_signals) self.search_bar.textChanged.connect(self.display_signals)
self.result_list.currentItemChanged.connect(self.display_specs) self.result_list.currentItemChanged.connect(self.display_specs)
self.result_list.itemDoubleClicked.connect(lambda: self.main_tab.setCurrentWidget(self.signal_properties_tab)) self.result_list.itemDoubleClicked.connect(lambda: self.main_tab.setCurrentWidget(self.signal_properties_tab))
# self.display_signals()
self.audio_widget = AudioPlayer(self.play, self.audio_widget = AudioPlayer(self.play,
self.pause, self.pause,
self.stop, self.stop,
@@ -376,26 +429,186 @@ class Artemis(QMainWindow, Ui_MainWindow):
BandLabel(self.ehf_left, self.ehf, self.ehf_right), BandLabel(self.ehf_left, self.ehf, self.ehf_right),
] ]
# Space weather
self.info_now_btn.clicked.connect(lambda : webbrowser.open(Constants.FORECAST_INFO))
self.update_now_bar.clicked.connect(self.start_update_space_weather)
self.update_now_bar.set_idle()
self.space_weather_data = SpaceWeatherData()
self.space_weather_data.update_complete.connect(self.update_space_weather)
# Final operations. # Final operations.
self.theme.initialize() self.theme.initialize()
self.load_db() self.load_db()
self.display_signals() self.display_signals()
self.show()
@pyqtSlot()
def start_update_space_weather(self):
if not self.space_weather_data.is_updating:
self.update_now_bar.set_updating()
self.space_weather_data.update()
@pyqtSlot(bool)
def update_space_weather(self, status_ok):
self.update_now_bar.set_idle()
if status_ok:
xray_long = float(self.space_weather_data.xray[-1][7])
format_text = lambda letter, power : letter + f"{xray_long * 10**power:.1f}"
if xray_long < 1e-8 and xray_long != -1.00e+05:
self.peak_flux_lbl.setText(format_text("<A", 8))
elif xray_long >= 1e-8 and xray_long < 1e-7:
self.peak_flux_lbl.setText(format_text("A", 8))
elif xray_long >= 1e-7 and xray_long < 1e-6:
self.peak_flux_lbl.setText(format_text("B", 7))
elif xray_long >= 1e-6 and xray_long < 1e-5:
self.peak_flux_lbl.setText(format_text("C", 6))
elif xray_long >= 1e-5 and xray_long < 1e-4:
self.peak_flux_lbl.setText(format_text("M", 5))
elif xray_long >= 1e-4:
self.peak_flux_lbl.setText(format_text("X", 4))
elif xray_long == -1.00e+05:
self.peak_flux_lbl.setText("No Data")
if xray_long < 1e-5 and xray_long != -1.00e+05:
self.switchable_r_labels.switch_on(self.r0_now_lbl)
elif xray_long >= 1e-5 and xray_long < 5e-5:
self.switchable_r_labels.switch_on(self.r1_now_lbl)
elif xray_long >= 5e-5 and xray_long < 1e-4:
self.switchable_r_labels.switch_on(self.r2_now_lbl)
elif xray_long >= 1e-4 and xray_long < 1e-3:
self.switchable_r_labels.switch_on(self.r3_now_lbl)
elif xray_long >= 1e-3 and xray_long < 2e-3:
self.switchable_r_labels.switch_on(self.r4_now_lbl)
elif xray_long >= 2e-3:
self.switchable_r_labels.switch_on(self.r5_now_lbl)
elif xray_long == -1.00e+05:
self.switchable_r_labels.switch_off_all()
pro10 = float(self.space_weather_data.prot_el[-1][8])
if pro10 < 10 and pro10 != -1.00e+05:
self.switchable_s_labels.switch_on(self.s0_now_lbl)
elif pro10 >= 10 and pro10 < 100:
self.switchable_s_labels.switch_on(self.s1_now_lbl)
elif pro10 >= 100 and pro10 < 1000:
self.switchable_s_labels.switch_on(self.s2_now_lbl)
elif pro10 >= 1000 and pro10 < 10000:
self.switchable_s_labels.switch_on(self.s3_now_lbl)
elif pro10 >= 10000 and pro10 < 100000:
self.switchable_s_labels.switch_on(self.s4_now_lbl)
elif pro10 >= 100000:
self.switchable_s_labels.switch_on(self.s5_now_lbl)
elif pro10 == -1.00e+05:
self.switchable_s_labels.switch_off_all()
k_index = int(self.space_weather_data.ak_index[8][11].replace('.', ''))
self.k_index_lbl.setText(str(k_index))
a_index = int(self.space_weather_data.ak_index[7][7].replace('.', ''))
self.a_index_lbl.setText(str(a_index))
if k_index == 0:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_inactive_lbl)
elif k_index == 1:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_very_quiet_lbl)
elif k_index == 2:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_quiet_lbl)
elif k_index == 3:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_unsettled_lbl)
elif k_index == 4:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_active_lbl)
elif k_index == 5:
self.switchable_g_now_labels.switch_on(self.g1_now_lbl)
self.k_storm_labels.switch_on(self.k_min_storm_lbl)
elif k_index == 6:
self.switchable_g_now_labels.switch_on(self.g2_now_lbl)
self.k_storm_labels.switch_on(self.k_maj_storm_lbl)
elif k_index == 7:
self.switchable_g_now_labels.switch_on(self.g3_now_lbl)
self.k_storm_labels.switch_on(self.k_sev_storm_lbl)
elif k_index == 8:
self.switchable_g_now_labels.switch_on(self.g4_now_lbl)
self.k_storm_labels.switch_on(self.k_very_sev_storm_lbl)
elif k_index == 9:
self.switchable_g_now_labels.switch_on(self.g5_now_lbl)
self.k_storm_labels.switch_on(self.k_ex_sev_storm_lbl)
if a_index >= 0 and a_index < 8:
self.a_storm_labels.switch_on(self.a_quiet_lbl)
elif a_index >= 8 and a_index < 16:
self.a_storm_labels.switch_on(self.a_unsettled_lbl)
elif a_index >= 16 and a_index < 30:
self.a_storm_labels.switch_on(self.a_active_lbl)
elif a_index >= 30 and a_index < 50:
self.a_storm_labels.switch_on(self.a_min_storm_lbl)
elif a_index >= 50 and a_index < 100:
self.a_storm_labels.switch_on(self.a_maj_storm_lbl)
elif a_index >= 100 and a_index < 400:
self.a_storm_labels.switch_on(self.a_sev_storm_lbl)
index = self.space_weather_data.geo_storm[6].index("was") + 1
k_index_24_hmax = int(self.space_weather_data.geo_storm[6][index])
if k_index_24_hmax == 0:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl)
self.expected_noise_lbl.setText(" S0 - S1 (<-120 dBm) ")
elif k_index_24_hmax == 1:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl)
self.expected_noise_lbl.setText(" S0 - S1 (<-120 dBm) ")
elif k_index_24_hmax == 2:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl)
self.expected_noise_lbl.setText(" S1 - S2 (-115 dBm) ")
elif k_index_24_hmax == 3:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl)
self.expected_noise_lbl.setText(" S2 - S3 (-110 dBm) ")
elif k_index_24_hmax == 4:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl)
self.expected_noise_lbl.setText(" S3 - S4 (-100 dBm) ")
elif k_index_24_hmax == 5:
self.switchable_g_today_labels.switch_on(self.g1_today_lbl)
self.expected_noise_lbl.setText(" S4 - S6 (-90 dBm) ")
elif k_index_24_hmax == 6:
self.switchable_g_today_labels.switch_on(self.g2_today_lbl)
self.expected_noise_lbl.setText(" S6 - S9 (-80 dBm) ")
elif k_index_24_hmax == 7:
self.switchable_g_today_labels.switch_on(self.g3_today_lbl)
self.expected_noise_lbl.setText(" S9 - S20 (>-60 dBm) ")
elif k_index_24_hmax == 8:
self.switchable_g_today_labels.switch_on(self.g4_today_lbl)
self.expected_noise_lbl.setText(" S20 - S30 (>-60 dBm) ")
elif k_index_24_hmax == 9:
self.switchable_g_today_labels.switch_on(self.g5_today_lbl)
self.expected_noise_lbl.setText(" S30+ (>>-60 dBm) ")
self.expected_noise_lbl.switch_on()
val = int(self.space_weather_data.ak_index[7][2].replace('.', ''))
self.sfi_lbl.setText(f"{val}")
val = int([x[4] for x in self.space_weather_data.sgas if "SSN" in x][0])
self.sn_lbl.setText(f"{val:d}")
for label, pixmap in zip(self.forecast_labels, self.space_weather_data.images):
label.pixmap = pixmap
label.make_transparent()
label.apply_pixmap()
else:
pop_up(self, title = Messages.BAD_DOWNLOAD,
text = Messages.BAD_DOWNLOAD_MSG).show()
self.space_weather_data.remove_data()
@pyqtSlot() @pyqtSlot()
def go_to_gfd(self, by): def go_to_gfd(self, by):
query = "/?q=" query = "/?q="
if by == GfdType.FREQ: if by is GfdType.FREQ:
value_in_mhz = self.freq_gfd.value() * Constants.CONVERSION_FACTORS[self.unit_freq_gfd.currentText()] / Constants.CONVERSION_FACTORS["MHz"] value_in_mhz = self.freq_gfd.value() * Constants.CONVERSION_FACTORS[self.unit_freq_gfd.currentText()] / Constants.CONVERSION_FACTORS["MHz"]
query += str(value_in_mhz) query += str(value_in_mhz)
elif by == GfdType.LOC: elif by is GfdType.LOC:
query += self.gfd_line_edit.text() query += self.gfd_line_edit.text()
try: try:
webbrowser.open(Constants.GFD_SITE + query.lower()) webbrowser.open(Constants.GFD_SITE + query.lower())
except: except:
pass pass
@pyqtSlot(QListWidgetItem) @pyqtSlot(QListWidgetItem)
def remove_if_unselected_modulation(self, item): def remove_if_unselected_modulation(self, item):
if not item.isSelected(): if not item.isSelected():
@@ -417,7 +630,7 @@ class Artemis(QMainWindow, Ui_MainWindow):
def show_matching_strings(self, list_elements, text): def show_matching_strings(self, list_elements, text):
for index in range(list_elements.count()): for index in range(list_elements.count()):
item = list_elements.item(index) item = list_elements.item(index)
if text.upper() in item.text() or item.isSelected(): if text.lower() in item.text().lower() or item.isSelected():
item.setHidden(False) item.setHidden(False)
else: else:
item.setHidden(True) item.setHidden(True)
@@ -441,10 +654,8 @@ class Artemis(QMainWindow, Ui_MainWindow):
item.child(i).setSelected(True) item.child(i).setSelected(True)
def set_initial_size(self): def set_initial_size(self):
""" """Function to handle high resolution screens. The function sets bigger sizes
Function to handle high resolution screens. The function sets bigger sizes for all the relevant fixed-size widgets."""
for all the relevant fixed-size widgets.
"""
d = QDesktopWidget().availableGeometry() d = QDesktopWidget().availableGeometry()
w = d.width() w = d.width()
h = d.height() h = d.height()
@@ -492,7 +703,6 @@ class Artemis(QMainWindow, Ui_MainWindow):
@pyqtSlot() @pyqtSlot()
def download_db(self): def download_db(self):
if not self.download_window.isVisible(): if not self.download_window.isVisible():
self.download_window.download_thread.finished.connect(self.show_downloaded_signals)
self.download_window.download_thread.start() self.download_window.download_thread.start()
self.download_window.show() self.download_window.show()
@@ -536,6 +746,8 @@ class Artemis(QMainWindow, Ui_MainWindow):
text = Messages.NO_DB_AVAIL, text = Messages.NO_DB_AVAIL,
informative_text = Messages.DOWNLOAD_NOW_QUESTION, informative_text = Messages.DOWNLOAD_NOW_QUESTION,
is_question = True).exec() is_question = True).exec()
if answer == QMessageBox.Yes:
self.download_db()
else: else:
try: try:
is_checksum_ok = checksum_ok(db, ChecksumWhat.DB) is_checksum_ok = checksum_ok(db, ChecksumWhat.DB)
@@ -546,7 +758,6 @@ class Artemis(QMainWindow, Ui_MainWindow):
if is_checksum_ok: if is_checksum_ok:
pop_up(self, title = Messages.DB_UP_TO_DATE, pop_up(self, title = Messages.DB_UP_TO_DATE,
text = Messages.DB_UP_TO_DATE_MSG).show() text = Messages.DB_UP_TO_DATE_MSG).show()
else: else:
answer = pop_up(self, title = Messages.DB_NEW_VER, answer = pop_up(self, title = Messages.DB_NEW_VER,
text = Messages.DB_NEW_VER_MSG, text = Messages.DB_NEW_VER_MSG,
@@ -557,10 +768,9 @@ class Artemis(QMainWindow, Ui_MainWindow):
@pyqtSlot() @pyqtSlot()
def show_downloaded_signals(self): def show_downloaded_signals(self):
if self.download_window.everything_ok: self.search_bar.setEnabled(True)
self.search_bar.setEnabled(True) self.load_db()
self.load_db() self.display_signals()
self.display_signals()
def load_db(self): def load_db(self):
names = Database.NAMES names = Database.NAMES
@@ -585,7 +795,9 @@ class Artemis(QMainWindow, Ui_MainWindow):
self.db.fillna(Constants.UNKNOWN, inplace = True) self.db.fillna(Constants.UNKNOWN, inplace = True)
self.db[Signal.WIKI_CLICKED] = False self.db[Signal.WIKI_CLICKED] = False
self.update_status_tip(self.total_signals) self.update_status_tip(self.total_signals)
self.result_list.clear()
self.result_list.addItems(self.signal_names) self.result_list.addItems(self.signal_names)
self.result_list.setCurrentItem(None)
@pyqtSlot() @pyqtSlot()
def set_min_value_upper_limit(self, lower_combo_box, def set_min_value_upper_limit(self, lower_combo_box,
@@ -599,7 +811,6 @@ class Artemis(QMainWindow, Ui_MainWindow):
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()
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
@@ -703,7 +914,7 @@ class Artemis(QMainWindow, Ui_MainWindow):
else: else:
self.result_list.item(index).setHidden(True) self.result_list.item(index).setHidden(True)
# Remove selected item. # Remove selected item.
self.result_list.selectionModel().clear() self.result_list.setCurrentItem(None)
self.update_status_tip(available_signals) self.update_status_tip(available_signals)
def update_status_tip(self, available_signals): def update_status_tip(self, available_signals):
@@ -756,8 +967,15 @@ class Artemis(QMainWindow, Ui_MainWindow):
@pyqtSlot() @pyqtSlot()
def reset_mode_filters(self): def reset_mode_filters(self):
uncheck_and_emit(self.apply_remove_mode_filter_btn) uncheck_and_emit(self.apply_remove_mode_filter_btn)
parents = Constants.MODES.keys()
selected_children = []
for item in self.mode_tree_widget.selectedItems(): for item in self.mode_tree_widget.selectedItems():
item.setSelected(False) if item.text(0) in parents:
item.setSelected(False)
else:
selected_children.append(item)
for children in selected_children:
children.setSelected(False)
if self.include_unknown_modes_btn.isChecked(): if self.include_unknown_modes_btn.isChecked():
self.include_unknown_modes_btn.setChecked(False) self.include_unknown_modes_btn.setChecked(False)
@@ -877,7 +1095,6 @@ class Artemis(QMainWindow, Ui_MainWindow):
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]
ok = [] ok = []
for item in selected_items: for item in selected_items:
if item.text(0) in parents: if item.text(0) in parents:
@@ -1042,11 +1259,11 @@ class Artemis(QMainWindow, Ui_MainWindow):
if __name__ == '__main__': if __name__ == '__main__':
my_app = QApplication(sys.argv) my_app = QApplication(sys.argv)
img = QPixmap(":/icons/Artemis3.500px.png") # img = QPixmap(":/icons/Artemis3.500px.png")
# img = img.scaled(600, 600, aspectRatioMode = Qt.KeepAspectRatio) # splash = QSplashScreen(img)
splash = QSplashScreen(img) # splash.show()
splash.show() # sleep(2)
sleep(2) artemis = Artemis()
w = Artemis() artemis.show()
splash.finish(w) # splash.finish(w)
sys.exit(my_app.exec_()) sys.exit(my_app.exec_())

3131
artemis.ui

File diff suppressed because it is too large Load Diff

31
clickable_progress_bar.py Normal file
View File

@@ -0,0 +1,31 @@
from PyQt5.QtWidgets import QProgressBar
from PyQt5.QtCore import Qt, pyqtSignal
from constants import Constants
class ClickableProgressBar(QProgressBar):
clicked = pyqtSignal()
def __init__(self, parent = None):
self.__text = ''
super().__init__(parent)
def __set_text(self, text):
self.__text = text
def text(self):
return self.__text
def set_idle(self):
self.__set_text(Constants.CLICK_TO_UPDATE_STR)
self.setMaximum(self.minimum() + 1)
def set_updating(self):
self.__set_text(Constants.UPDATING_STR)
self.setMaximum(self.minimum())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.clicked.emit()
else:
super().mousePressEvent(event)

View File

@@ -67,50 +67,70 @@ class Database(object):
Signal.CATEGORY_CODE,) Signal.CATEGORY_CODE,)
class Constants(object): class Constants(object):
ACF_DOCS = "https://aresvalley.com/documentation/" CLICK_TO_UPDATE_STR = "Click to update"
SEARCH_LABEL_IMG = "search_icon.png" UPDATING_STR = "Updating..."
VOLUME_LABEL_IMG = "volume.png" ACF_DOCS = "https://aresvalley.com/documentation/"
DATA_FOLDER = "Data" FORECAST_XRAY = "https://services.swpc.noaa.gov/text/goes-xray-flux-primary.txt"
SPECTRA_FOLDER = "Spectra" FORECAST_PROT = "https://services.swpc.noaa.gov/text/goes-particle-flux-primary.txt"
SPECTRA_EXT = ".png" FORECAST_AK_IND = "https://services.swpc.noaa.gov/text/wwv.txt"
AUDIO_FOLDER = "Audio" FORECAST_SGAS = "https://services.swpc.noaa.gov/text/sgas.txt"
ACTIVE = "active" FORECAST_G = "https://services.swpc.noaa.gov/text/3-day-forecast.txt"
INACTIVE = "inactive" FORECAST_INFO = "https://www.swpc.noaa.gov/sites/default/files/images/NOAAscales.pdf"
NOT_AVAILABLE = "spectrumnotavailable.png" FORECAST_IMG_0 = "http://www.mmmonvhf.de/eme/eme.png"
NOT_SELECTED = "nosignalselected.png" FORECAST_IMG_1 = "http://www.mmmonvhf.de/ms/ms.png"
__Band = namedtuple("Band", ["lower", "upper"]) FORECAST_IMG_2 = "http://www.mmmonvhf.de/es/es.png"
__ELF = __Band(0, 30) # Formally it is (3, 30) Hz. FORECAST_IMG_3 = "http://www.mmmonvhf.de/solar/solar.png"
__SLF = __Band(30, 300) FORECAST_IMG_4 = "http://amunters.home.xs4all.nl/eskipstatusNA.gif"
__ULF = __Band(300, 3000) FORECAST_IMG_5 = "http://amunters.home.xs4all.nl/aurorastatus.gif"
__VLF = __Band(3000, 30000) FORECAST_IMG_6 = "http://amunters.home.xs4all.nl/eskipstatus.gif"
__LF = __Band(30 * 10**3, 300 * 10**3) FORECAST_IMG_7 = "http://amunters.home.xs4all.nl/eskip50status.gif"
__MF = __Band(300 * 10 ** 3, 3000 * 10**3) FORECAST_IMG_8 = "http://amunters.home.xs4all.nl/eskip70status.gif"
__HF = __Band(3 * 10**6, 30 * 10**6) SEARCH_LABEL_IMG = "search_icon.png"
__VHF = __Band(30 * 10**6, 300 * 10**6) VOLUME_LABEL_IMG = "volume.png"
__UHF = __Band(300 * 10**6, 3000 * 10**6) DATA_FOLDER = "Data"
__SHF = __Band(3 * 10**9, 30 * 10**9) SPECTRA_FOLDER = "Spectra"
__EHF = __Band(30 * 10**9, 300 * 10**9) SPECTRA_EXT = ".png"
BANDS = (__ELF, __SLF, __ULF, __VLF, __LF, __MF, __HF, __VHF, __UHF, __SHF, __EHF) AUDIO_FOLDER = "Audio"
MAX_DIGITS = 3 ACTIVE = "active"
RANGE_SEPARATOR = ' ÷ ' INACTIVE = "inactive"
GFD_SITE = "http://qrg.globaltuners.com/" LABEL_ON_COLOR = "on"
CONVERSION_FACTORS = {"Hz" : 1, LABEL_OFF_COLOR = "off"
"kHz": 1000, TEXT_COLOR = "text"
"MHz": 1000000, NOT_AVAILABLE = "spectrumnotavailable.png"
"GHz": 1000000000} NOT_SELECTED = "nosignalselected.png"
MODES = {"FM": ("NFM", "WFM"), __Band = namedtuple("Band", ["lower", "upper"])
"AM": (), __ELF = __Band(0, 30) # Formally it is (3, 30) Hz.
"CW": (), __SLF = __Band(30, 300)
"SK": ("FSK", "PSK", "MSK"), __ULF = __Band(300, 3000)
"SB": ("LSB", "USB", "DSB"), __VLF = __Band(3000, 30000)
"Chirp Spread Spectrum": (), __LF = __Band(30 * 10**3, 300 * 10**3)
"FHSS-TDM": (), __MF = __Band(300 * 10 ** 3, 3000 * 10**3)
"RAW": (), __HF = __Band(3 * 10**6, 30 * 10**6)
"SC-FDMA": (),} __VHF = __Band(30 * 10**6, 300 * 10**6)
APPLY = "Apply" __UHF = __Band(300 * 10**6, 3000 * 10**6)
REMOVE = "Remove" __SHF = __Band(3 * 10**9, 30 * 10**9)
UNKNOWN = "N/A" __EHF = __Band(30 * 10**9, 300 * 10**9)
MODULATIONS = ("8VSB", BANDS = (__ELF, __SLF, __ULF, __VLF, __LF, __MF, __HF, __VHF, __UHF, __SHF, __EHF)
MAX_DIGITS = 3
RANGE_SEPARATOR = ' ÷ '
GFD_SITE = "http://qrg.globaltuners.com/"
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", "AFSK",
"AM", "AM",
"BFSK", "BFSK",
@@ -135,34 +155,34 @@ class Constants(object):
"PSK", "PSK",
"QAM", "QAM",
"TDMA",) "TDMA",)
LOCATIONS = (UNKNOWN, LOCATIONS = (UNKNOWN,
"Australia", "Australia",
"Canada", "Canada",
"Central Europe", "Central Europe",
"China", "China",
"Cyprus", "Cyprus",
"Eastern Europe", "Eastern Europe",
"Europe", "Europe",
"Europe, japan and Asia", "Europe, japan and Asia",
"Exmouth, Australia", "Exmouth, Australia",
"Finland", "Finland",
"France", "France",
"Germany", "Germany",
"Home Base Mobile , AL", "Home Base Mobile , AL",
"Hungary", "Hungary",
"Iran", "Iran",
"Israel", "Israel",
"Japan", "Japan",
"LaMour, North Dakota", "LaMour, North Dakota",
"Lualualei, Hawaii", "Lualualei, Hawaii",
"North America", "North America",
"North Korea", "North Korea",
"Poland", "Poland",
"Romania", "Romania",
"Ruda, Sweden", "Ruda, Sweden",
"UK", "UK",
"United Kingdom", "United Kingdom",
"United States", "United States",
"Varberg, Sweden", "Varberg, Sweden",
"World Wide", "World Wide",
"Worldwide",) "Worldwide",)

View File

@@ -1,5 +1,5 @@
from PyQt5 import uic from PyQt5 import uic
from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
from threads import DownloadThread, ThreadStatus from threads import DownloadThread, ThreadStatus
from utilities import pop_up, resource_path from utilities import pop_up, resource_path
@@ -8,6 +8,9 @@ from constants import Messages
Ui_Download_window, _ = uic.loadUiType(resource_path("download_db_window.ui")) Ui_Download_window, _ = uic.loadUiType(resource_path("download_db_window.ui"))
class DownloadWindow(QWidget, Ui_Download_window): class DownloadWindow(QWidget, Ui_Download_window):
complete = pyqtSignal()
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setupUi(self) self.setupUi(self)
@@ -18,7 +21,6 @@ class DownloadWindow(QWidget, Ui_Download_window):
Qt.WindowCloseButtonHint #| Qt.WindowCloseButtonHint #|
# Qt.WindowStaysOnTopHint # Qt.WindowStaysOnTopHint
) )
self.everything_ok = True
self.no_internet_msg = pop_up(self, title = Messages.NO_CONNECTION, self.no_internet_msg = pop_up(self, title = Messages.NO_CONNECTION,
text = Messages.NO_CONNECTION_MSG, text = Messages.NO_CONNECTION_MSG,
@@ -30,17 +32,8 @@ class DownloadWindow(QWidget, Ui_Download_window):
self.download_thread = DownloadThread() self.download_thread = DownloadThread()
self.download_thread.finished.connect(self.wait_close) self.download_thread.finished.connect(self.wait_close)
self.cancel_btn.clicked.connect(self.terminate_process) self.cancel_btn.clicked.connect(self.terminate_process)
def show_no_connection_warning(self):
self.no_internet_msg.show()
self.everything_ok = False
def show_bad_download_warning(self):
self.bad_db_download_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():
@@ -50,12 +43,13 @@ class DownloadWindow(QWidget, Ui_Download_window):
@pyqtSlot() @pyqtSlot()
def wait_close(self): def wait_close(self):
if self.download_thread.status == ThreadStatus.OK: if self.download_thread.status is ThreadStatus.OK:
self.complete.emit()
self.close() self.close()
elif self.download_thread.status == ThreadStatus.NO_CONNECTION_ERR: elif self.download_thread.status is ThreadStatus.NO_CONNECTION_ERR:
self.show_no_connection_warning() self.no_internet_msg.show()
elif self.download_thread.status == ThreadStatus.BAD_DOWNLOAD_ERR: elif self.download_thread.status is ThreadStatus.BAD_DOWNLOAD_ERR:
self.show_bad_download_warning() self.bad_db_download_msg.show()
else: else:
self.close() self.close()

View File

@@ -0,0 +1,27 @@
from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import Qt
class FixedAspectRatioLabel(QLabel):
def __init__(self, parent = None):
super().__init__(parent)
self.pixmap = None
def set_default_stylesheet(self):
self.setStyleSheet("""
color: #ffffff;
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,stop:0 #304352 ,stop: 1 #d7d2cc);
""")
def make_transparent(self):
self.setText('')
self.setStyleSheet("background-color: transparent;")
def apply_pixmap(self):
if self.pixmap:
self.setPixmap(
self.pixmap.scaled(
self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
def rescale(self, size):
self.resize(size)
self.apply_pixmap()

View File

@@ -0,0 +1,20 @@
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import QSize
class FixedAspectRatioWidget(QWidget):
space = 10
def __init__(self, parent = None):
super().__init__(parent)
self.labels = []
def resizeEvent(self, event):
h, w = self.height(), self.width()
h_lbl = h / 9 - self.space
w_lbl = 5 * h_lbl
if w_lbl > w:
w_lbl = w
h_lbl = h / 9 - self.space
for label in self.labels:
label.rescale(QSize(w_lbl, h_lbl))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because it is too large Load Diff

66
space_weather_data.py Normal file
View File

@@ -0,0 +1,66 @@
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from threads import UpadteSpaceWeatherThread, ThreadStatus
class SpaceWeatherData(QObject):
update_complete = pyqtSignal(bool)
def __init__(self):
super().__init__()
self.xray = ''
self.prot_el = ''
self.ak_index = ''
self.sgas = ''
self.geo_storm = ''
self.images = [QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap()]
self.__update_thread = UpadteSpaceWeatherThread(self)
self.__update_thread.finished.connect(self.__parse_and_emit_signal)
@property
def is_updating(self):
return self.__update_thread.isRunning()
@pyqtSlot()
def update(self):
self.__update_thread.start()
def __parse_data(self):
double_split = lambda string : [i.split() for i in string.splitlines()]
self.xray = double_split(self.xray)
self.prot_el = double_split(self.prot_el)
self.ak_index = double_split(self.ak_index)
self.sgas = double_split(self.sgas)
self.geo_storm = double_split(self.geo_storm)
def remove_data(self):
self.xray = ''
self.prot_el = ''
self.ak_index = ''
self.sgas = ''
self.geo_storm = ''
self.images = [QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap()]
@pyqtSlot()
def __parse_and_emit_signal(self):
if self.__update_thread.status is not ThreadStatus.OK:
status_ok = False
else:
status_ok = True
self.__parse_data()
self.update_complete.emit(status_ok)

56
switchable_label.py Normal file
View File

@@ -0,0 +1,56 @@
from PyQt5.QtWidgets import QLabel
class SwitchableLabel(QLabel):
def __init__(self, parent = None):
super().__init__(parent)
self.switch_on_colors = ()
self.switch_off_colors = ()
self.text_color = ''
self.is_on = False
def switch_on(self):
self.is_on = True
self.__apply_colors(*self.switch_on_colors)
def switch_off(self):
self.is_on = False
self.__apply_colors(*self.switch_off_colors)
def __apply_colors(self, start, end):
self.setStyleSheet(
f"""
color:{self.text_color};
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,stop:0 {start} ,stop: 1 {end});
"""
)
class SwitchableLabelsIterable(object):
def __init__(self, *labels):
self.labels = labels
def __iter__(self):
for lab in self.labels:
yield lab
def switch_on(self, label):
for lab in self.labels:
if lab is label:
lab.switch_on()
else:
lab.switch_off()
def switch_off_all(self):
for lab in self.labels:
lab.switch_off()
def set(self, attr, value):
for lab in self.labels:
setattr(lab, attr, value)
def refresh(self):
for lab in self.labels:
if lab.is_on:
lab.switch_on()
else:
lab.switch_off()

114
themes.py
View File

@@ -1,10 +1,13 @@
from functools import partial from functools import partial
from itertools import chain
import os import os
from PyQt5.QtWidgets import QAction import re
from PyQt5.QtWidgets import QAction, QActionGroup
from PyQt5.QtCore import pyqtSlot from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from constants import Constants from constants import Constants
from utilities import pop_up, is_valid_html_color from switchable_label import SwitchableLabelsIterable
from utilities import pop_up
class ThemeConstants(object): class ThemeConstants(object):
FOLDER = "themes" FOLDER = "themes"
@@ -14,8 +17,11 @@ class ThemeConstants(object):
CURRENT = ".current_theme" CURRENT = ".current_theme"
COLORS = "colors.txt" COLORS = "colors.txt"
COLOR_SEPARATOR = "=" COLOR_SEPARATOR = "="
DEFAULT_ACTIVE_COLOR = "#39eaff" DEFAULT_ACTIVE_COLOR = "#000000"
DEFAULT_INACTIVE_COLOR = "#9f9f9f" DEFAULT_INACTIVE_COLOR = "#9f9f9f"
DEFAULT_OFF_COLORS = "#000000", "#434343"
DEFAULT_ON_COLORS = "#4b79a1", "#283e51"
DEFAULT_TEXT_COLOR = "#ffffff"
THEME_NOT_FOUND = "Theme not found" THEME_NOT_FOUND = "Theme not found"
MISSING_THEME = "Missing theme in '" + FOLDER + "' folder." MISSING_THEME = "Missing theme in '" + FOLDER + "' folder."
@@ -24,10 +30,26 @@ class Theme(object):
self.__parent = parent self.__parent = parent
self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR
self.__parent.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR self.__parent.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR
self.__theme_path = ThemeConstants.DEFAULT
self.__theme_path = ""
self.__current_theme = ""
self.__parent.default_images_folder = os.path.join(ThemeConstants.FOLDER, self.__parent.default_images_folder = os.path.join(ThemeConstants.FOLDER,
ThemeConstants.DEFAULT, ThemeConstants.DEFAULT,
ThemeConstants.ICONS_FOLDER) ThemeConstants.ICONS_FOLDER)
self.__forecast_labels = SwitchableLabelsIterable(*list(chain(self.__parent.switchable_r_labels,
self.__parent.switchable_s_labels,
self.__parent.switchable_g_now_labels,
self.__parent.switchable_g_today_labels,
self.__parent.k_storm_labels,
self.__parent.a_storm_labels,
[self.__parent.expected_noise_lbl])))
self.__forecast_labels.set("switch_on_colors", ThemeConstants.DEFAULT_ON_COLORS)
self.__forecast_labels.set("switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS)
self.__theme_names = {}
self.__detect_themes() self.__detect_themes()
def __refresh_range_labels(self): def __refresh_range_labels(self):
@@ -41,6 +63,7 @@ class Theme(object):
self.__parent.upper_band_filter_unit, self.__parent.upper_band_filter_unit,
self.__parent.upper_band_confidence, self.__parent.upper_band_confidence,
self.__parent.band_range_lbl) self.__parent.band_range_lbl)
self.__parent.set_band_filter_label(self.__parent.activate_low_freq_filter_btn, self.__parent.set_band_filter_label(self.__parent.activate_low_freq_filter_btn,
self.__parent.lower_freq_spinbox, self.__parent.lower_freq_spinbox,
self.__parent.lower_freq_filter_unit, self.__parent.lower_freq_filter_unit,
@@ -54,26 +77,33 @@ class Theme(object):
@pyqtSlot() @pyqtSlot()
def __apply(self, theme_path): def __apply(self, theme_path):
self.__theme_path = theme_path self.__theme_path = theme_path
self.__change() if self.__theme_path != self.__current_theme:
self.__parent.display_specs(self.__parent.result_list.currentItem(), None) self.__change()
self.__refresh_range_labels() self.__parent.display_specs(self.__parent.result_list.currentItem(), None)
self.__parent.audio_widget.refresh_btns_colors(self.__parent.active_color, self.__parent.inactive_color) self.__refresh_range_labels()
self.__parent.audio_widget.refresh_btns_colors(self.__parent.active_color, self.__parent.inactive_color)
self.__forecast_labels.refresh()
def __pretty_name(self, bad_name):
return ' '.join(
map(lambda s: s.capitalize(),
bad_name.split('-')[1].split('_')
)
)
def __detect_themes(self): def __detect_themes(self):
themes = [] themes = []
ag = QActionGroup(self.__parent, exclusive = True)
for theme_folder in os.listdir(ThemeConstants.FOLDER): for theme_folder in os.listdir(ThemeConstants.FOLDER):
relative_folder = os.path.join(ThemeConstants.FOLDER, theme_folder) relative_folder = os.path.join(ThemeConstants.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(ThemeConstants.FOLDER, theme_folder) relative_folder = os.path.join(ThemeConstants.FOLDER, theme_folder)
themes.append(relative_folder) themes.append(relative_folder)
for theme_path in themes: for theme_path in themes:
theme_name = '&' + ' '.join( theme_name = '&' + self.__pretty_name(os.path.basename(theme_path))
map(lambda s: s.capitalize(), new_theme = ag.addAction(QAction(theme_name, self.__parent, checkable = True))
os.path.basename(theme_path).split('-')[1].split('_')
)
)
new_theme = QAction(theme_name, self.__parent)
self.__parent.menu_themes.addAction(new_theme) self.__parent.menu_themes.addAction(new_theme)
self.__theme_names[theme_name.lstrip('&')] = new_theme
new_theme.triggered.connect(partial(self.__apply, theme_path)) new_theme.triggered.connect(partial(self.__apply, theme_path))
def __change(self): def __change(self):
@@ -128,27 +158,53 @@ class Theme(object):
path_to_colors = os.path.join(self.__theme_path, ThemeConstants.COLORS) path_to_colors = os.path.join(self.__theme_path, ThemeConstants.COLORS)
active_color_ok = False active_color_ok = False
inactive_color_ok = False inactive_color_ok = False
valid_format = False switch_on_color_ok = False
valid_file = False switch_off_color_ok = False
text_color_ok = False
if os.path.exists(path_to_colors): if os.path.exists(path_to_colors):
valid_file = True is_valid_html_color = lambda colors : all([bool(re.match("#([a-zA-Z0-9]){6}", color)) for color in colors])
with open(path_to_colors, "r") as colors_file: with open(path_to_colors, "r") as colors_file:
for line in colors_file: for line in colors_file:
if ThemeConstants.COLOR_SEPARATOR in line: if ThemeConstants.COLOR_SEPARATOR in line:
valid_format = True
quality, color = line.split(ThemeConstants.COLOR_SEPARATOR) quality, color = line.split(ThemeConstants.COLOR_SEPARATOR)
color = color.rstrip() color = color.rstrip()
if quality.lower() == Constants.ACTIVE and is_valid_html_color(color): if ',' in color:
self.__parent.active_color = color color = [c.rstrip().lstrip() for c in color.split(',')]
active_color_ok = True else:
if quality.lower() == Constants.INACTIVE and is_valid_html_color(color): color = [color]
self.__parent.inactive_color = color if len(color) > 2:
inactive_color_ok = True break
if is_valid_html_color(color):
if quality.lower() == Constants.ACTIVE:
self.__parent.active_color = color[0]
active_color_ok = True
if quality.lower() == Constants.INACTIVE:
self.__parent.inactive_color = color[0]
inactive_color_ok = True
if len(color) == 2:
if quality.lower() == Constants.LABEL_ON_COLOR:
switch_on_color_ok = True
self.__forecast_labels.set("switch_on_colors", color)
if quality.lower() == Constants.LABEL_OFF_COLOR:
switch_off_color_ok = True
self.__forecast_labels.set("switch_off_colors", color)
if quality.lower() == Constants.TEXT_COLOR:
text_color_ok = True
self.__forecast_labels.set("text_color", color[0])
if not all([valid_file, valid_format, active_color_ok, inactive_color_ok]): if not (active_color_ok and inactive_color_ok):
self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR
self.__parent.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR self.__parent.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR
if not (switch_on_color_ok and switch_off_color_ok):
self.__forecast_labels.set("switch_on_colors", ThemeConstants.DEFAULT_ON_COLORS)
self.__forecast_labels.set("switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS)
if not text_color_ok:
self.__forecast_labels.set("text_color", ThemeConstants.DEFAULT_TEXT_COLOR)
self.__current_theme = self.__theme_path
try: try:
with open(os.path.join(ThemeConstants.FOLDER, with open(os.path.join(ThemeConstants.FOLDER,
ThemeConstants.CURRENT), "w") as current_theme: ThemeConstants.CURRENT), "w") as current_theme:
@@ -161,5 +217,9 @@ class Theme(object):
if os.path.exists(current_theme_file): if os.path.exists(current_theme_file):
with open(current_theme_file, "r") as current_theme_path: with open(current_theme_file, "r") as current_theme_path:
theme_path = current_theme_path.read() theme_path = current_theme_path.read()
if theme_path != ThemeConstants.DEFAULT: theme_name = self.__pretty_name(os.path.basename(theme_path))
self.__apply(theme_path) self.__theme_names[theme_name].setChecked(True)
self.__apply(theme_path)
else:
self.__theme_names[self.__pretty_name(ThemeConstants.DEFAULT)].setChecked(True)
self.__apply(os.path.join(ThemeConstants.FOLDER, ThemeConstants.DEFAULT))

View File

@@ -1,2 +1,5 @@
active=#4da6ff active=#4545e5
inactive=#546E7A inactive=#546E7A
off=#283048,#859398
on=#4776e6, #8e54e9
text=#ffffff

View File

@@ -433,6 +433,7 @@ TreeViewMenu (Mode)
QTreeView { QTreeView {
background-color: transparent; background-color: transparent;
selection-background-color: transparent; selection-background-color: transparent;
border: 0px;
} }
QTreeView::item { QTreeView::item {

View File

@@ -1,2 +1,4 @@
active=#88cc00 active=#88cc00
inactive=#546E7A inactive=#546E7A
off=#304352,#d7d2cc
on=#3ca55c,#b5ac49

View File

@@ -452,6 +452,7 @@ TreeViewMenu (Mode)
QTreeView { QTreeView {
background-color: transparent; background-color: transparent;
selection-background-color: transparent; selection-background-color: transparent;
border: 0px;
} }
QTreeView::item { QTreeView::item {

View File

@@ -1,2 +1,4 @@
active=#6ECE12 active=#6ECE12
inactive=#b3b3cc inactive=#b3b3cc
off=#948e99,#2e1437
on=#b993d6,#8ca6db

View File

@@ -452,6 +452,7 @@ TreeViewMenu (Mode)
QTreeView { QTreeView {
background-color: transparent; background-color: transparent;
selection-background-color: transparent; selection-background-color: transparent;
border: 0px;
} }
QTreeView::item { QTreeView::item {

View File

@@ -8,18 +8,18 @@ from zipfile import ZipFile
from PyQt5.QtCore import QThread from PyQt5.QtCore import QThread
from constants import Constants, Database, ChecksumWhat from constants import Constants, Database, ChecksumWhat
from utilities import checksum_ok from utilities import checksum_ok
import constants
class ThreadStatus(Enum): class ThreadStatus(Enum):
OK = auto() OK = auto()
NO_CONNECTION_ERR = auto() NO_CONNECTION_ERR = auto()
UNKNOWN_ERR = auto() UNKNOWN_ERR = auto()
BAD_DOWNLOAD_ERR = auto() BAD_DOWNLOAD_ERR = auto()
UNDEFINED = auto()
class DownloadThread(QThread): class DownloadThread(QThread):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.__status = ThreadStatus.OK self.__status = ThreadStatus.UNDEFINED
self.reason = 0 self.reason = 0
@property @property
@@ -33,8 +33,6 @@ class DownloadThread(QThread):
def run(self): def run(self):
try: try:
db = urllib3.PoolManager().request('GET', Database.LINK_LOC) db = urllib3.PoolManager().request('GET', Database.LINK_LOC)
# db = urllib.request.urlopen(constants.Database.LINK_LOC)
# 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
return return
@@ -54,8 +52,45 @@ class DownloadThread(QThread):
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()
with ZipFile(BytesIO(db.data)) as zipped: with ZipFile(BytesIO(db.data)) as zipped:
zipped.extractall() zipped.extractall()
except: except:
self.__status = ThreadStatus.UNKNOWN_ERR self.__status = ThreadStatus.UNKNOWN_ERR
else:
self.__status = ThreadStatus.OK
class UpadteSpaceWeatherThread(QThread):
def __init__(self, space_weather_data):
super().__init__()
self.__status = ThreadStatus.UNDEFINED
self.__space_weather_data = space_weather_data
@property
def status(self):
return self.__status
def __del__(self):
self.terminate()
self.wait()
def run(self):
try:
self.__space_weather_data.xray = str(urllib3.PoolManager().request('GET', Constants.FORECAST_XRAY).data, 'utf-8')
self.__space_weather_data.prot_el = str(urllib3.PoolManager().request('GET', Constants.FORECAST_PROT).data, 'utf-8')
self.__space_weather_data.ak_index = str(urllib3.PoolManager().request('GET', Constants.FORECAST_AK_IND).data, 'utf-8')
self.__space_weather_data.sgas = str(urllib3.PoolManager().request('GET', Constants.FORECAST_SGAS).data, 'utf-8')
self.__space_weather_data.geo_storm = str(urllib3.PoolManager().request('GET', Constants.FORECAST_G).data, 'utf-8')
self.__space_weather_data.images[0].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_0).data)
self.__space_weather_data.images[1].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_1).data)
self.__space_weather_data.images[2].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_2).data)
self.__space_weather_data.images[3].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_3).data)
self.__space_weather_data.images[4].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_4).data)
self.__space_weather_data.images[5].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_5).data)
self.__space_weather_data.images[6].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_6).data)
self.__space_weather_data.images[7].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_7).data)
self.__space_weather_data.images[8].loadFromData(urllib3.PoolManager().request('GET', Constants.FORECAST_IMG_8).data)
except:
self.__status = ThreadStatus.UNKNOWN_ERR
else:
self.__status = ThreadStatus.OK

View File

@@ -1,6 +1,5 @@
from functools import partial from functools import partial
import hashlib import hashlib
import re
import sys import sys
import os import os
from pandas import read_csv from pandas import read_csv
@@ -42,9 +41,9 @@ def pop_up(cls, title, text,
def checksum_ok(data, what): def checksum_ok(data, what):
code = hashlib.sha256() code = hashlib.sha256()
code.update(data) code.update(data)
if what == ChecksumWhat.FOLDER: if what is ChecksumWhat.FOLDER:
n = 0 n = 0
elif what == ChecksumWhat.DB: elif what is ChecksumWhat.DB:
n = 1 n = 1
else: else:
raise ValueError("Wrong entry name.") raise ValueError("Wrong entry name.")
@@ -55,9 +54,6 @@ def checksum_ok(data, what):
raise raise
return code.hexdigest() == reference return code.hexdigest() == reference
def is_valid_html_color(color):
return bool(re.match("#([a-zA-Z0-9]){6}", color))
def connect_to(events_to_connect, fun_to_connect, fun_args): def connect_to(events_to_connect, fun_to_connect, fun_args):
if fun_args: if fun_args:
for event in events_to_connect: for event in events_to_connect:
@@ -101,8 +97,12 @@ def format_numbers(lower, upper):
upper = int(upper) / upper_factor upper = int(upper) / upper_factor
if lower.is_integer(): if lower.is_integer():
lower = int(lower) lower = int(lower)
else:
lower = round(lower, 2)
if upper.is_integer(): if upper.is_integer():
upper = int(upper) upper = int(upper)
else:
upper = round(upper, 2)
if pre_lower != pre_upper: if pre_lower != pre_upper:
return f"{lower:,} {units[lower_factor]} - {upper:,} {units[upper_factor]}" return f"{lower:,} {units[lower_factor]} - {upper:,} {units[upper_factor]}"
else: else: