Merge pull request #2 from AresValley/forecast_screen

Forecast screen
This commit is contained in:
Alessandro Ceccato
2019-05-12 19:59:00 +02:00
committed by GitHub
12 changed files with 2560 additions and 570 deletions

3
.gitignore vendored
View File

@@ -6,6 +6,7 @@ to_do.txt
csv_info.txt csv_info.txt
pyinstaller_cmd.txt pyinstaller_cmd.txt
themes/.current_theme themes/.current_theme
*.bat launch.bat
designer.bat
*.sh *.sh
.vscode/ .vscode/

View File

@@ -22,7 +22,7 @@ from PyQt5.QtCore import (QFileInfo,
pyqtSlot,) pyqtSlot,)
from audio_player import AudioPlayer from audio_player import AudioPlayer
from space_weather_data import SpaceWeatherData from weatherdata import SpaceWeatherData, ForecastData
from download_window import DownloadWindow from download_window import DownloadWindow
from switchable_label import SwitchableLabelsIterable from switchable_label import SwitchableLabelsIterable
from constants import (Constants, from constants import (Constants,
@@ -32,7 +32,7 @@ from constants import (Constants,
ChecksumWhat, ChecksumWhat,
Messages, Messages,
Signal,) Signal,)
from themes import Theme from themesmanager import ThemeManager
from utilities import (checksum_ok, from utilities import (checksum_ok,
uncheck_and_emit, uncheck_and_emit,
pop_up, pop_up,
@@ -80,6 +80,7 @@ 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.switchable_r_labels = SwitchableLabelsIterable(
self.r0_now_lbl, self.r0_now_lbl,
self.r1_now_lbl, self.r1_now_lbl,
@@ -138,23 +139,34 @@ class Artemis(QMainWindow, Ui_MainWindow):
self.a_quiet_lbl self.a_quiet_lbl
) )
self.forecast_labels = ( self.space_weather_labels = (
self.forecast_lbl_0, self.space_weather_lbl_0,
self.forecast_lbl_1, self.space_weather_lbl_1,
self.forecast_lbl_2, self.space_weather_lbl_2,
self.forecast_lbl_3, self.space_weather_lbl_3,
self.forecast_lbl_4, self.space_weather_lbl_4,
self.forecast_lbl_5, self.space_weather_lbl_5,
self.forecast_lbl_6, self.space_weather_lbl_6,
self.forecast_lbl_7, self.space_weather_lbl_7,
self.forecast_lbl_8 self.space_weather_lbl_8
) )
for lab in self.forecast_labels: for lab in self.space_weather_labels:
lab.set_default_stylesheet() lab.set_default_stylesheet()
self.forecast_label_container.labels = self.forecast_labels self.space_weather_label_container.labels = self.space_weather_labels
self.theme = Theme(self) self.space_weather_label_name_container.labels = [
self.eme_lbl,
self.ms_lbl,
self.muf_lbl,
self.hi_lbl,
self.eu50_lbl,
self.eu70_lbl,
self.eu144_lbl,
self.na_lbl,
self.aurora_lbl
]
self.theme_manager = ThemeManager(self)
# Manage frequency filters. # Manage frequency filters.
self.frequency_filters_btns = ( self.frequency_filters_btns = (
@@ -452,7 +464,7 @@ class Artemis(QMainWindow, Ui_MainWindow):
) )
self.apply_remove_acf_filter_btn.clicked.connect(self.display_signals) self.apply_remove_acf_filter_btn.clicked.connect(self.display_signals)
self.reset_acf_filters_btn.clicked.connect(self.reset_acf_filters) self.reset_acf_filters_btn.clicked.connect(self.reset_acf_filters)
self.acf_info_btn.clicked.connect(lambda : webbrowser.open(Constants.ACF_DOCS)) self.acf_info_btn.clicked.connect(lambda: webbrowser.open(Constants.ACF_DOCS))
connect_events_to_func( connect_events_to_func(
events_to_connect=[self.acf_spinbox.valueChanged, events_to_connect=[self.acf_spinbox.valueChanged,
@@ -501,30 +513,56 @@ class Artemis(QMainWindow, Ui_MainWindow):
# Space weather # Space weather
self.info_now_btn.clicked.connect( self.info_now_btn.clicked.connect(
lambda : webbrowser.open(Constants.FORECAST_INFO) lambda: webbrowser.open(Constants.SPACE_WEATHER_INFO)
) )
self.update_now_bar.clicked.connect(self.start_update_space_weather) self.update_now_bar.clicked.connect(self.start_update_space_weather)
self.update_now_bar.set_idle() self.update_now_bar.set_idle()
self.space_weather_data = SpaceWeatherData() self.space_weather_data = SpaceWeatherData()
self.space_weather_data.update_complete.connect(self.update_space_weather) self.space_weather_data.update_complete.connect(self.update_space_weather)
# Forecast
self.forecast_info_btn.clicked.connect(
lambda: webbrowser.open(Constants.SPACE_WEATHER_INFO)
)
self.forecast_data = ForecastData(self)
self.update_forecast_bar.clicked.connect(self.start_update_forecast)
self.update_forecast_bar.set_idle()
self.forecast_data.update_complete.connect(self.update_forecast)
# Final operations. # Final operations.
self.theme.initialize() self.theme_manager.start()
self.load_db() self.load_db()
self.display_signals() self.display_signals()
@pyqtSlot()
def start_update_forecast(self):
if not self.forecast_data.is_updating:
self.update_forecast_bar.set_updating()
self.forecast_data.update()
@pyqtSlot() @pyqtSlot()
def start_update_space_weather(self): def start_update_space_weather(self):
if not self.space_weather_data.is_updating: if not self.space_weather_data.is_updating:
self.update_now_bar.set_updating() self.update_now_bar.set_updating()
self.space_weather_data.update() self.space_weather_data.update()
@pyqtSlot(bool)
def update_forecast(self, status_ok):
self.update_forecast_bar.set_idle()
if status_ok:
self.forecast_data.update_all_labels()
elif not self.closing:
pop_up(self, title=Messages.BAD_DOWNLOAD,
text=Messages.BAD_DOWNLOAD_MSG).show()
self.forecast_data.remove_data()
@pyqtSlot(bool) @pyqtSlot(bool)
def update_space_weather(self, status_ok): def update_space_weather(self, status_ok):
self.update_now_bar.set_idle() self.update_now_bar.set_idle()
if status_ok: if status_ok:
xray_long = float(self.space_weather_data.xray[-1][7]) xray_long = float(self.space_weather_data.xray[-1][7])
format_text = lambda letter, power : letter + f"{xray_long * 10**power:.1f}" format_text = lambda letter, power: letter + f"{xray_long * 10**power:.1f}"
if xray_long < 1e-8 and xray_long != -1.00e+05: if xray_long < 1e-8 and xray_long != -1.00e+05:
self.peak_flux_lbl.setText(format_text("<A", 8)) self.peak_flux_lbl.setText(format_text("<A", 8))
elif xray_long >= 1e-8 and xray_long < 1e-7: elif xray_long >= 1e-8 and xray_long < 1e-7:
@@ -579,33 +617,44 @@ class Artemis(QMainWindow, Ui_MainWindow):
if k_index == 0: if k_index == 0:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl) self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_inactive_lbl) self.k_storm_labels.switch_on(self.k_inactive_lbl)
self.expected_noise_lbl.setText(" S0 - S1 (<-120 dBm) ")
elif k_index == 1: elif k_index == 1:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl) self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_very_quiet_lbl) self.k_storm_labels.switch_on(self.k_very_quiet_lbl)
self.expected_noise_lbl.setText(" S0 - S1 (<-120 dBm) ")
elif k_index == 2: elif k_index == 2:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl) self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_quiet_lbl) self.k_storm_labels.switch_on(self.k_quiet_lbl)
self.expected_noise_lbl.setText(" S1 - S2 (-115 dBm) ")
elif k_index == 3: elif k_index == 3:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl) self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_unsettled_lbl) self.k_storm_labels.switch_on(self.k_unsettled_lbl)
self.expected_noise_lbl.setText(" S2 - S3 (-110 dBm) ")
elif k_index == 4: elif k_index == 4:
self.switchable_g_now_labels.switch_on(self.g0_now_lbl) self.switchable_g_now_labels.switch_on(self.g0_now_lbl)
self.k_storm_labels.switch_on(self.k_active_lbl) self.k_storm_labels.switch_on(self.k_active_lbl)
self.expected_noise_lbl.setText(" S3 - S4 (-100 dBm) ")
elif k_index == 5: elif k_index == 5:
self.switchable_g_now_labels.switch_on(self.g1_now_lbl) self.switchable_g_now_labels.switch_on(self.g1_now_lbl)
self.k_storm_labels.switch_on(self.k_min_storm_lbl) self.k_storm_labels.switch_on(self.k_min_storm_lbl)
self.expected_noise_lbl.setText(" S4 - S6 (-90 dBm) ")
elif k_index == 6: elif k_index == 6:
self.switchable_g_now_labels.switch_on(self.g2_now_lbl) self.switchable_g_now_labels.switch_on(self.g2_now_lbl)
self.k_storm_labels.switch_on(self.k_maj_storm_lbl) self.k_storm_labels.switch_on(self.k_maj_storm_lbl)
self.expected_noise_lbl.setText(" S6 - S9 (-80 dBm) ")
elif k_index == 7: elif k_index == 7:
self.switchable_g_now_labels.switch_on(self.g3_now_lbl) self.switchable_g_now_labels.switch_on(self.g3_now_lbl)
self.k_storm_labels.switch_on(self.k_sev_storm_lbl) self.k_storm_labels.switch_on(self.k_sev_storm_lbl)
self.expected_noise_lbl.setText(" S9 - S20 (>-60 dBm) ")
elif k_index == 8: elif k_index == 8:
self.switchable_g_now_labels.switch_on(self.g4_now_lbl) self.switchable_g_now_labels.switch_on(self.g4_now_lbl)
self.k_storm_labels.switch_on(self.k_very_sev_storm_lbl) self.k_storm_labels.switch_on(self.k_very_sev_storm_lbl)
self.expected_noise_lbl.setText(" S20 - S30 (>-60 dBm) ")
elif k_index == 9: elif k_index == 9:
self.switchable_g_now_labels.switch_on(self.g5_now_lbl) self.switchable_g_now_labels.switch_on(self.g5_now_lbl)
self.k_storm_labels.switch_on(self.k_ex_sev_storm_lbl) self.k_storm_labels.switch_on(self.k_ex_sev_storm_lbl)
self.expected_noise_lbl.setText(" S30+ (>>-60 dBm) ")
self.expected_noise_lbl.switch_on()
if a_index >= 0 and a_index < 8: if a_index >= 0 and a_index < 8:
self.a_storm_labels.switch_on(self.a_quiet_lbl) self.a_storm_labels.switch_on(self.a_quiet_lbl)
@@ -624,42 +673,31 @@ class Artemis(QMainWindow, Ui_MainWindow):
k_index_24_hmax = int(self.space_weather_data.geo_storm[6][index]) k_index_24_hmax = int(self.space_weather_data.geo_storm[6][index])
if k_index_24_hmax == 0: if k_index_24_hmax == 0:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl) 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: elif k_index_24_hmax == 1:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl) 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: elif k_index_24_hmax == 2:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl) 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: elif k_index_24_hmax == 3:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl) 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: elif k_index_24_hmax == 4:
self.switchable_g_today_labels.switch_on(self.g0_today_lbl) 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: elif k_index_24_hmax == 5:
self.switchable_g_today_labels.switch_on(self.g1_today_lbl) 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: elif k_index_24_hmax == 6:
self.switchable_g_today_labels.switch_on(self.g2_today_lbl) 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: elif k_index_24_hmax == 7:
self.switchable_g_today_labels.switch_on(self.g3_today_lbl) 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: elif k_index_24_hmax == 8:
self.switchable_g_today_labels.switch_on(self.g4_today_lbl) 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: elif k_index_24_hmax == 9:
self.switchable_g_today_labels.switch_on(self.g5_today_lbl) 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('.', '')) val = int(self.space_weather_data.ak_index[7][2].replace('.', ''))
self.sfi_lbl.setText(f"{val}") self.sfi_lbl.setText(f"{val}")
val = int([x[4] for x in self.space_weather_data.sgas if "SSN" in x][0]) val = int([x[4] for x in self.space_weather_data.sgas if "SSN" in x][0])
self.sn_lbl.setText(f"{val:d}") self.sn_lbl.setText(f"{val:d}")
for label, pixmap in zip(self.forecast_labels, self.space_weather_data.images): for label, pixmap in zip(self.space_weather_labels, self.space_weather_data.images):
label.pixmap = pixmap label.pixmap = pixmap
label.make_transparent() label.make_transparent()
label.apply_pixmap() label.apply_pixmap()
@@ -729,7 +767,9 @@ class Artemis(QMainWindow, Ui_MainWindow):
def set_initial_size(self): def set_initial_size(self):
"""Function to handle high resolution screens. The function sets bigger """Function to handle high resolution screens. The function sets bigger
sizes for all the relevant fixed-size widgets.""" sizes for all the relevant fixed-size widgets.
Also by default it sets the size to 3/4 of the available space
both vertically and horizontally."""
d = QDesktopWidget().availableGeometry() d = QDesktopWidget().availableGeometry()
w = d.width() w = d.width()
h = d.height() h = d.height()
@@ -1066,6 +1106,10 @@ class Artemis(QMainWindow, Ui_MainWindow):
def reset_modulation_filters(self): def reset_modulation_filters(self):
uncheck_and_emit(self.apply_remove_modulation_filter_btn) uncheck_and_emit(self.apply_remove_modulation_filter_btn)
self.search_bar_modulation.setText('') self.search_bar_modulation.setText('')
self.show_matching_strings(
self.modulation_list,
self.search_bar_modulation.text()
)
for i in range(self.modulation_list.count()): for i in range(self.modulation_list.count()):
if self.modulation_list.item(i).isSelected(): if self.modulation_list.item(i).isSelected():
self.modulation_list.item(i).setSelected(False) self.modulation_list.item(i).setSelected(False)
@@ -1074,6 +1118,10 @@ class Artemis(QMainWindow, Ui_MainWindow):
def reset_location_filters(self): def reset_location_filters(self):
uncheck_and_emit(self.apply_remove_location_filter_btn) uncheck_and_emit(self.apply_remove_location_filter_btn)
self.search_bar_location.setText('') self.search_bar_location.setText('')
self.show_matching_strings(
self.locations_list,
self.search_bar_location.text()
)
for i in range(self.locations_list.count()): for i in range(self.locations_list.count()):
if self.locations_list.item(i).isSelected(): if self.locations_list.item(i).isSelected():
self.locations_list.item(i).setSelected(False) self.locations_list.item(i).setSelected(False)
@@ -1366,6 +1414,8 @@ class Artemis(QMainWindow, Ui_MainWindow):
self.download_window.close() self.download_window.close()
if self.space_weather_data.is_updating: if self.space_weather_data.is_updating:
self.space_weather_data.shutdown_thread() self.space_weather_data.shutdown_thread()
if self.forecast_data.is_updating:
self.forecast_data.shutdown_thread()
super().closeEvent(event) super().closeEvent(event)

2153
artemis.ui

File diff suppressed because it is too large Load Diff

View File

@@ -11,18 +11,20 @@ class ClickableProgressBar(QProgressBar):
self.__text = '' self.__text = ''
super().__init__(parent) super().__init__(parent)
def __set_text(self, text): # def __set_text(self, text):
self.__text = text # self.__text = text
def text(self): def text(self):
return self.__text return self.__text
def set_idle(self): def set_idle(self):
self.__set_text(Constants.CLICK_TO_UPDATE_STR) # self.__set_text(Constants.CLICK_TO_UPDATE_STR)
self.__text = Constants.CLICK_TO_UPDATE_STR
self.setMaximum(self.minimum() + 1) self.setMaximum(self.minimum() + 1)
def set_updating(self): def set_updating(self):
self.__set_text(Constants.UPDATING_STR) # self.__set_text(Constants.UPDATING_STR)
self.__text = Constants.UPDATING_STR
self.setMaximum(self.minimum()) self.setMaximum(self.minimum())
def mousePressEvent(self, event): def mousePressEvent(self, event):

View File

@@ -2,7 +2,7 @@ from collections import namedtuple
from enum import Enum, auto from enum import Enum, auto
class Ftype(object): class Ftype:
FREQ = "freq" FREQ = "freq"
BAND = "band" BAND = "band"
@@ -17,7 +17,7 @@ class ChecksumWhat(Enum):
DB = auto() DB = auto()
class Messages(object): class Messages:
DB_UP_TO_DATE = "Already up to date" DB_UP_TO_DATE = "Already up to date"
DB_UP_TO_DATE_MSG = "No newer version to download." DB_UP_TO_DATE_MSG = "No newer version to download."
DB_NEW_VER = "New version available" DB_NEW_VER = "New version available"
@@ -32,7 +32,7 @@ class Messages(object):
BAD_DOWNLOAD_MSG = "Something went wrong with the downaload.\nCheck your internet connection and try again." BAD_DOWNLOAD_MSG = "Something went wrong with the downaload.\nCheck your internet connection and try again."
class Signal(object): class Signal:
NAME = "name" NAME = "name"
INF_FREQ = "inf_freq" INF_FREQ = "inf_freq"
SUP_FREQ = "sup_freq" SUP_FREQ = "sup_freq"
@@ -48,7 +48,7 @@ class Signal(object):
WIKI_CLICKED = "url_clicked" WIKI_CLICKED = "url_clicked"
class Database(object): class Database:
LINK_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip" LINK_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip"
LINK_REF = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log" LINK_REF = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log"
NAME = "db.csv" NAME = "db.csv"
@@ -73,128 +73,138 @@ class Database(object):
Signal.CATEGORY_CODE,) Signal.CATEGORY_CODE,)
class Constants(object): class ForecastColors:
CLICK_TO_UPDATE_STR = "Click to update" WARNING_COLOR = "#F95423"
SIGIDWIKI = "https://www.sigidwiki.com/wiki/Signal_Identification_Guide" KP9_COLOR = "#FFCCCB"
ADD_SIGNAL_LINK = "https://www.sigidwiki.com/index.php/Special:FormEdit/Signal/?preload=Signal_Identification_Wiki:Signal_form_preload_text" KP8_COLOR = "#FFCC9A"
FORUM_LINK = "https://aresvalley.com/community/" KP7_COLOR = "#FFFECD"
ARESVALLEY_LINK = "https://aresvalley.com/" KP6_COLOR = "#CDFFCC"
RTL_SDL_LINK = "https://www.rtl-sdr.com/" KP5_COLOR = "#BEE3FE"
UPDATING_STR = "Updating..."
ACF_DOCS = "https://aresvalley.com/documentation/"
FORECAST_XRAY = "https://services.swpc.noaa.gov/text/goes-xray-flux-primary.txt" class Constants:
FORECAST_PROT_EL = "https://services.swpc.noaa.gov/text/goes-particle-flux-primary.txt" CLICK_TO_UPDATE_STR = "Click to update"
FORECAST_AK_INDEX = "https://services.swpc.noaa.gov/text/wwv.txt" SIGIDWIKI = "https://www.sigidwiki.com/wiki/Signal_Identification_Guide"
FORECAST_SGAS = "https://services.swpc.noaa.gov/text/sgas.txt" ADD_SIGNAL_LINK = "https://www.sigidwiki.com/index.php/Special:FormEdit/Signal/?preload=Signal_Identification_Wiki:Signal_form_preload_text"
FORECAST_GEO_STORM = "https://services.swpc.noaa.gov/text/3-day-forecast.txt" FORUM_LINK = "https://aresvalley.com/community/"
FORECAST_INFO = "https://www.swpc.noaa.gov/sites/default/files/images/NOAAscales.pdf" ARESVALLEY_LINK = "https://aresvalley.com/"
FORECAST_IMGS = ["http://www.mmmonvhf.de/eme/eme.png", RTL_SDL_LINK = "https://www.rtl-sdr.com/"
"http://www.mmmonvhf.de/ms/ms.png", UPDATING_STR = "Updating..."
"http://www.mmmonvhf.de/es/es.png", ACF_DOCS = "https://aresvalley.com/documentation/"
"http://www.mmmonvhf.de/solar/solar.png", FORECAST_PROBABILITIES = "https://services.swpc.noaa.gov/text/sgarf.txt"
"http://amunters.home.xs4all.nl/eskip50status.gif", SPACE_WEATHER_XRAY = "https://services.swpc.noaa.gov/text/goes-xray-flux-primary.txt"
"http://amunters.home.xs4all.nl/eskip70status.gif", SPACE_WEATHER_PROT_EL = "https://services.swpc.noaa.gov/text/goes-particle-flux-primary.txt"
"http://amunters.home.xs4all.nl/eskipstatus.gif", SPACE_WEATHER_AK_INDEX = "https://services.swpc.noaa.gov/text/wwv.txt"
"https://amunters.home.xs4all.nl/eskipstatusNA.gif", SPACE_WEATHER_SGAS = "https://services.swpc.noaa.gov/text/sgas.txt"
"https://amunters.home.xs4all.nl/aurorastatus.gif"] SPACE_WEATHER_GEO_STORM = "https://services.swpc.noaa.gov/text/3-day-forecast.txt"
SEARCH_LABEL_IMG = "search_icon.png" SPACE_WEATHER_INFO = "https://www.swpc.noaa.gov/sites/default/files/images/NOAAscales.pdf"
VOLUME_LABEL_IMG = "volume.png" SPACE_WEATHER_IMGS = ["http://www.mmmonvhf.de/eme/eme.png",
DATA_FOLDER = "Data" "http://www.mmmonvhf.de/ms/ms.png",
SPECTRA_FOLDER = "Spectra" "http://www.mmmonvhf.de/es/es.png",
SPECTRA_EXT = ".png" "http://www.mmmonvhf.de/solar/solar.png",
AUDIO_FOLDER = "Audio" "http://amunters.home.xs4all.nl/eskip50status.gif",
ACTIVE = "active" "http://amunters.home.xs4all.nl/eskip70status.gif",
INACTIVE = "inactive" "http://amunters.home.xs4all.nl/eskipstatus.gif",
LABEL_ON_COLOR = "on" "https://amunters.home.xs4all.nl/eskipstatusNA.gif",
LABEL_OFF_COLOR = "off" "https://amunters.home.xs4all.nl/aurorastatus.gif"]
TEXT_COLOR = "text" SEARCH_LABEL_IMG = "search_icon.png"
NOT_AVAILABLE = "spectrumnotavailable.png" VOLUME_LABEL_IMG = "volume.png"
NOT_SELECTED = "nosignalselected.png" DATA_FOLDER = "Data"
__Band = namedtuple("Band", ["lower", "upper"]) SPECTRA_FOLDER = "Spectra"
__ELF = __Band(0, 30) # Formally it is (3, 30) Hz. SPECTRA_EXT = ".png"
__SLF = __Band(30, 300) AUDIO_FOLDER = "Audio"
__ULF = __Band(300, 3000) ACTIVE = "active"
__VLF = __Band(3000, 30000) INACTIVE = "inactive"
__LF = __Band(30 * 10**3, 300 * 10**3) LABEL_ON_COLOR = "on"
__MF = __Band(300 * 10 ** 3, 3000 * 10**3) LABEL_OFF_COLOR = "off"
__HF = __Band(3 * 10**6, 30 * 10**6) TEXT_COLOR = "text"
__VHF = __Band(30 * 10**6, 300 * 10**6) NOT_AVAILABLE = "spectrumnotavailable.png"
__UHF = __Band(300 * 10**6, 3000 * 10**6) NOT_SELECTED = "nosignalselected.png"
__SHF = __Band(3 * 10**9, 30 * 10**9) __Band = namedtuple("Band", ["lower", "upper"])
__EHF = __Band(30 * 10**9, 300 * 10**9) __ELF = __Band(0, 30) # Formally it is (3, 30) Hz.
BANDS = (__ELF, __SLF, __ULF, __VLF, __LF, __MF, __HF, __VHF, __UHF, __SHF, __EHF) __SLF = __Band(30, 300)
MAX_DIGITS = 3 __ULF = __Band(300, 3000)
RANGE_SEPARATOR = ' ÷ ' __VLF = __Band(3000, 30000)
GFD_SITE = "http://qrg.globaltuners.com/" __LF = __Band(30 * 10**3, 300 * 10**3)
CONVERSION_FACTORS = {"Hz" : 1, __MF = __Band(300 * 10 ** 3, 3000 * 10**3)
"kHz": 1000, __HF = __Band(3 * 10**6, 30 * 10**6)
"MHz": 1000000, __VHF = __Band(30 * 10**6, 300 * 10**6)
"GHz": 1000000000} __UHF = __Band(300 * 10**6, 3000 * 10**6)
MODES = {"FM": ("NFM", "WFM"), __SHF = __Band(3 * 10**9, 30 * 10**9)
"AM": (), __EHF = __Band(30 * 10**9, 300 * 10**9)
"CW": (), BANDS = (__ELF, __SLF, __ULF, __VLF, __LF, __MF, __HF, __VHF, __UHF, __SHF, __EHF)
"SK": ("FSK", "PSK", "MSK"), MAX_DIGITS = 3
"SB": ("LSB", "USB", "DSB"), RANGE_SEPARATOR = ' ÷ '
"Chirp Spread Spectrum": (), GFD_SITE = "http://qrg.globaltuners.com/"
"FHSS-TDM": (), CONVERSION_FACTORS = {"Hz" : 1,
"RAW": (), "kHz": 1000,
"SC-FDMA": (),} "MHz": 1000000,
APPLY = "Apply" "GHz": 1000000000}
REMOVE = "Remove" MODES = {"FM": ("NFM", "WFM"),
UNKNOWN = "N/A" "AM": (),
MODULATIONS = ("8VSB", "CW": (),
"AFSK", "SK": ("FSK", "PSK", "MSK"),
"AM", "SB": ("LSB", "USB", "DSB"),
"BFSK", "Chirp Spread Spectrum": (),
"C4FM", "FHSS-TDM": (),
"CDMA", "RAW": (),
"COFDM", "SC-FDMA": (),}
"CW", APPLY = "Apply"
"FFSK", REMOVE = "Remove"
"FM", UNKNOWN = "N/A"
"FMCW", MODULATIONS = ("8VSB",
"FMOP", "AFSK",
"FSK", "AM",
"GFSK", "BFSK",
"GMSK", "C4FM",
"IFK", "CDMA",
"MFSK", "COFDM",
"MSK", "CW",
"OFDM", "FFSK",
"OOK", "FM",
"PAM", "FMCW",
"PPM", "FMOP",
"PSK", "FSK",
"QAM", "GFSK",
"TDMA",) "GMSK",
LOCATIONS = (UNKNOWN, "IFK",
"Australia", "MFSK",
"Canada", "MSK",
"Central Europe", "OFDM",
"China", "OOK",
"Cyprus", "PAM",
"Eastern Europe", "PPM",
"Europe", "PSK",
"Europe, japan and Asia", "QAM",
"Exmouth, Australia", "TDMA",)
"Finland", LOCATIONS = (UNKNOWN,
"France", "Australia",
"Germany", "Canada",
"Home Base Mobile , AL", "Central Europe",
"Hungary", "China",
"Iran", "Cyprus",
"Israel", "Eastern Europe",
"Japan", "Europe",
"LaMour, North Dakota", "Europe, japan and Asia",
"Lualualei, Hawaii", "Exmouth, Australia",
"North America", "Finland",
"North Korea", "France",
"Poland", "Germany",
"Romania", "Home Base Mobile , AL",
"Ruda, Sweden", "Hungary",
"UK", "Iran",
"United Kingdom", "Israel",
"United States", "Japan",
"Varberg, Sweden", "LaMour, North Dakota",
"World Wide", "Lualualei, Hawaii",
"Worldwide",) "North America",
"North Korea",
"Poland",
"Romania",
"Ruda, Sweden",
"UK",
"United Kingdom",
"United States",
"Varberg, Sweden",
"World Wide",
"Worldwide",)

View File

@@ -8,14 +8,14 @@ class FixedAspectRatioLabel(QLabel):
self.pixmap = None self.pixmap = None
def set_default_stylesheet(self): def set_default_stylesheet(self):
self.setStyleSheet(""" self.setStyleSheet("""border-width: 1px;
color: #ffffff; border-style: solid;
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,stop:0 #304352 ,stop: 1 #d7d2cc); border-color: black;"""
""") )
def make_transparent(self): def make_transparent(self):
self.setText('') self.setText('')
self.setStyleSheet("background-color: transparent;") self.setStyleSheet("border-width: 0px;")
def apply_pixmap(self): def apply_pixmap(self):
if self.pixmap: if self.pixmap:

View File

@@ -12,10 +12,10 @@ class FixedAspectRatioWidget(QWidget):
h, w = self.height(), self.width() h, w = self.height(), self.width()
h_lbl = h / 9 - self.space h_lbl = h / 9 - self.space
w_lbl = 5 * h_lbl w_lbl = 5 * h_lbl
w_pad = w - 10
if w_lbl > w: if w_lbl > w_pad:
w_lbl = w w_lbl = w_pad
h_lbl = h / 9 - self.space h_lbl = w_pad / 5
for label in self.labels: for label in self.labels:
label.rescale(QSize(w_lbl, h_lbl)) label.rescale(QSize(w_lbl, h_lbl))

View File

@@ -1,74 +0,0 @@
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):
status_ok = False
if self.__update_thread.status is ThreadStatus.OK:
status_ok = True
self.__parse_data()
self.update_complete.emit(status_ok)
def shutdown_thread(self):
self.__update_thread.terminate()
self.__update_thread.wait()

View File

@@ -1,20 +1,33 @@
from PyQt5.QtWidgets import QLabel from PyQt5.QtWidgets import QLabel
from constants import ForecastColors
class SwitchableLabel(QLabel): class _BaseSwitchableLabel(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.is_on = False
self.level = 0
def switch_on(self):
self.is_on = True
def switch_off(self):
self.is_on = False
class SwitchableLabel(_BaseSwitchableLabel):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.switch_on_colors = () self.switch_on_colors = ()
self.switch_off_colors = () self.switch_off_colors = ()
self.text_color = '' self.text_color = ''
self.is_on = False
def switch_on(self): def switch_on(self):
self.is_on = True super().switch_on()
self.__apply_colors(*self.switch_on_colors) self.__apply_colors(*self.switch_on_colors)
def switch_off(self): def switch_off(self):
self.is_on = False super().switch_off()
self.__apply_colors(*self.switch_off_colors) self.__apply_colors(*self.switch_off_colors)
def __apply_colors(self, start, end): def __apply_colors(self, start, end):
@@ -26,7 +39,54 @@ class SwitchableLabel(QLabel):
) )
class SwitchableLabelsIterable(object): class SingleColorSwitchableLabel(_BaseSwitchableLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.active_color = ForecastColors.WARNING_COLOR
def switch_on(self):
if self.level >= 30:
super().switch_on()
self.setStyleSheet(f"color: {self.active_color}"
# f"""background-color: {self.active_color};
# color: #000000;"""
)
def switch_off(self):
super().switch_off()
# self.setStyleSheet("""background-color: transparent;""")
self.setStyleSheet("")
class MultiColorSwitchableLabel(_BaseSwitchableLabel):
LEVEL_COLORS = {
9: ForecastColors.KP9_COLOR,
8: ForecastColors.KP8_COLOR,
7: ForecastColors.KP7_COLOR,
6: ForecastColors.KP6_COLOR,
5: ForecastColors.KP5_COLOR
}
def __init__(self, parent=None):
super().__init__(parent)
def switch_on(self):
if 5 <= self.level <= 9:
super().switch_on()
self.setStyleSheet(f"color: {self.LEVEL_COLORS[self.level]}"
# f"""background-color: {self.LEVEL_COLORS[self.level]};
# color: #000000;
# """
)
def switch_off(self):
super().switch_off()
# self.setStyleSheet("background-color: transparent;")
self.setStyleSheet("")
class SwitchableLabelsIterable:
def __init__(self, *labels): def __init__(self, *labels):
self.labels = labels self.labels = labels

View File

@@ -10,7 +10,7 @@ from switchable_label import SwitchableLabelsIterable
from utilities import pop_up from utilities import pop_up
class ThemeConstants(object): class ThemeConstants:
FOLDER = "themes" FOLDER = "themes"
EXTENSION = ".qss" EXTENSION = ".qss"
ICONS_FOLDER = "icons" ICONS_FOLDER = "icons"
@@ -26,7 +26,7 @@ class ThemeConstants(object):
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."
class Theme(object): class ThemeManager:
def __init__(self, parent): def __init__(self, parent):
self.__parent = parent self.__parent = parent
self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR self.__parent.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR
@@ -41,7 +41,7 @@ class Theme(object):
ThemeConstants.ICONS_FOLDER ThemeConstants.ICONS_FOLDER
) )
self.__forecast_labels = SwitchableLabelsIterable( self.__space_weather_labels = SwitchableLabelsIterable(
*list( *list(
chain( chain(
self.__parent.switchable_r_labels, self.__parent.switchable_r_labels,
@@ -55,11 +55,11 @@ class Theme(object):
) )
) )
self.__forecast_labels.set( self.__space_weather_labels.set(
"switch_on_colors", "switch_on_colors",
ThemeConstants.DEFAULT_ON_COLORS ThemeConstants.DEFAULT_ON_COLORS
) )
self.__forecast_labels.set( self.__space_weather_labels.set(
"switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS "switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS
) )
@@ -106,7 +106,7 @@ class Theme(object):
self.__parent.active_color, self.__parent.active_color,
self.__parent.inactive_color self.__parent.inactive_color
) )
self.__forecast_labels.refresh() self.__space_weather_labels.refresh()
def __pretty_name(self, bad_name): def __pretty_name(self, bad_name):
return ' '.join( return ' '.join(
@@ -257,20 +257,20 @@ class Theme(object):
inactive_color_ok = True inactive_color_ok = True
if quality.lower() == Constants.TEXT_COLOR: if quality.lower() == Constants.TEXT_COLOR:
text_color_ok = True text_color_ok = True
self.__forecast_labels.set( self.__space_weather_labels.set(
"text_color", "text_color",
color color
) )
if color_len == 2: if color_len == 2:
if quality.lower() == Constants.LABEL_ON_COLOR: if quality.lower() == Constants.LABEL_ON_COLOR:
switch_on_color_ok = True switch_on_color_ok = True
self.__forecast_labels.set( self.__space_weather_labels.set(
"switch_on_colors", "switch_on_colors",
color color
) )
if quality.lower() == Constants.LABEL_OFF_COLOR: if quality.lower() == Constants.LABEL_OFF_COLOR:
switch_off_color_ok = True switch_off_color_ok = True
self.__forecast_labels.set( self.__space_weather_labels.set(
"switch_off_colors", "switch_off_colors",
color color
) )
@@ -280,17 +280,17 @@ class Theme(object):
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): if not (switch_on_color_ok and switch_off_color_ok):
self.__forecast_labels.set( self.__space_weather_labels.set(
"switch_on_colors", "switch_on_colors",
ThemeConstants.DEFAULT_ON_COLORS ThemeConstants.DEFAULT_ON_COLORS
) )
self.__forecast_labels.set( self.__space_weather_labels.set(
"switch_off_colors", "switch_off_colors",
ThemeConstants.DEFAULT_OFF_COLORS ThemeConstants.DEFAULT_OFF_COLORS
) )
if not text_color_ok: if not text_color_ok:
self.__forecast_labels.set( self.__space_weather_labels.set(
"text_color", "text_color",
ThemeConstants.DEFAULT_TEXT_COLOR ThemeConstants.DEFAULT_TEXT_COLOR
) )
@@ -305,7 +305,7 @@ class Theme(object):
except Exception: except Exception:
pass pass
def initialize(self): def start(self):
current_theme_file = os.path.join( current_theme_file = os.path.join(
ThemeConstants.FOLDER, ThemeConstants.FOLDER,
ThemeConstants.CURRENT ThemeConstants.CURRENT

View File

@@ -19,7 +19,7 @@ class ThreadStatus(Enum):
UNDEFINED = auto() UNDEFINED = auto()
class _BaseDownloadThread(QThread): class BaseDownloadThread(QThread):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.status = ThreadStatus.UNDEFINED self.status = ThreadStatus.UNDEFINED
@@ -27,10 +27,9 @@ class _BaseDownloadThread(QThread):
def __del__(self): def __del__(self):
self.terminate() self.terminate()
self.wait() self.wait()
super().__del__()
class DownloadThread(_BaseDownloadThread): class DownloadThread(BaseDownloadThread):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.reason = 0 self.reason = 0
@@ -66,7 +65,13 @@ class DownloadThread(_BaseDownloadThread):
self.status = ThreadStatus.OK self.status = ThreadStatus.OK
class UpadteSpaceWeatherThread(_BaseDownloadThread): class _AsyncDownloader:
async def _download_resource(self, session, link):
resp = await session.get(link)
return await resp.read()
class UpdateSpaceWeatherThread(BaseDownloadThread, _AsyncDownloader):
__properties = ("xray", "prot_el", "ak_index", "sgas", "geo_storm") __properties = ("xray", "prot_el", "ak_index", "sgas", "geo_storm")
@@ -74,20 +79,16 @@ class UpadteSpaceWeatherThread(_BaseDownloadThread):
super().__init__() super().__init__()
self.__space_weather_data = space_weather_data self.__space_weather_data = space_weather_data
async def __download_resource(self, session, link):
resp = await session.get(link)
return await resp.read()
async def __download_property(self, session, property_name): async def __download_property(self, session, property_name):
link = getattr(Constants, "FORECAST_" + property_name.upper()) link = getattr(Constants, "SPACE_WEATHER_" + property_name.upper())
data = await self.__download_resource(session, link) data = await self._download_resource(session, link)
setattr(self.__space_weather_data, property_name, str(data, 'utf-8')) setattr(self.__space_weather_data, property_name, str(data, 'utf-8'))
async def __download_image(self, session, n): async def __download_image(self, session, n):
im = await self.__download_resource(session, Constants.FORECAST_IMGS[n]) im = await self._download_resource(session, Constants.SPACE_WEATHER_IMGS[n])
self.__space_weather_data.images[n].loadFromData(im) self.__space_weather_data.images[n].loadFromData(im)
async def __download_resources(self, *links): async def _download_resources(self):
session = aiohttp.ClientSession() session = aiohttp.ClientSession()
try: try:
t = [] t = []
@@ -96,7 +97,7 @@ class UpadteSpaceWeatherThread(_BaseDownloadThread):
asyncio.create_task(self.__download_property(session, p)) asyncio.create_task(self.__download_property(session, p))
) )
tot_images = range(len(Constants.FORECAST_IMGS)) tot_images = range(len(Constants.SPACE_WEATHER_IMGS))
t1 = [] t1 = []
for im_number in tot_images: for im_number in tot_images:
t1.append( t1.append(
@@ -112,4 +113,53 @@ class UpadteSpaceWeatherThread(_BaseDownloadThread):
def run(self): def run(self):
self.status = ThreadStatus.UNDEFINED self.status = ThreadStatus.UNDEFINED
asyncio.run(self.__download_resources()) asyncio.run(self._download_resources())
class UpdateForecastThread(BaseDownloadThread, _AsyncDownloader):
class _PropertyName(Enum):
FORECAST = auto()
PROBABILITIES = auto()
def __init__(self, parent):
super().__init__()
self.parent = parent
async def __download_property(self, session, link, prop_name):
resp = await self._download_resource(session, link)
resp = str(resp, 'utf-8')
if prop_name is self._PropertyName.FORECAST:
self.parent.forecast = resp
if prop_name is self._PropertyName.PROBABILITIES:
self.parent.probabilities = resp
async def _download_resources(self):
session = aiohttp.ClientSession()
try:
await asyncio.gather(
asyncio.create_task(
self.__download_property(
session,
Constants.SPACE_WEATHER_GEO_STORM,
self._PropertyName.FORECAST
)
),
asyncio.create_task(
self.__download_property(
session,
Constants.FORECAST_PROBABILITIES,
self._PropertyName.PROBABILITIES
)
)
)
except Exception:
self.status = ThreadStatus.UNKNOWN_ERR
else:
self.status = ThreadStatus.OK
finally:
await session.close()
def run(self):
self.status = ThreadStatus.UNDEFINED
asyncio.run(self._download_resources())

316
weatherdata.py Normal file
View File

@@ -0,0 +1,316 @@
import re
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from threads import (BaseDownloadThread,
UpdateSpaceWeatherThread,
ThreadStatus,
UpdateForecastThread)
from constants import Constants
from switchable_label import MultiColorSwitchableLabel
class _BaseWeatherData(QObject):
update_complete = pyqtSignal(bool)
def __init__(self):
super().__init__()
self._update_thread = BaseDownloadThread()
@property
def is_updating(self):
return self._update_thread.isRunning()
def update(self):
self._update_thread.start()
def _parse_data(self):
pass
@pyqtSlot()
def _parse_and_emit_signal(self):
status_ok = False
if self._update_thread.status is ThreadStatus.OK:
status_ok = True
self._parse_data()
self.update_complete.emit(status_ok)
def _double_split(self, string):
return [i.split() for i in string.splitlines()]
def shutdown_thread(self):
self._update_thread.terminate()
self._update_thread.wait()
class SpaceWeatherData(_BaseWeatherData):
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 = UpdateSpaceWeatherThread(self)
self._update_thread.finished.connect(self._parse_and_emit_signal)
def _parse_data(self):
self.xray = self._double_split(self.xray)
self.prot_el = self._double_split(self.prot_el)
self.ak_index = self._double_split(self.ak_index)
self.sgas = self._double_split(self.sgas)
self.geo_storm = self._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()
]
class ForecastData(_BaseWeatherData):
ROW_KEYWORDS = {
"solar_row": "S1 or greater",
"event_row": "III. Event probabilities",
"rb_now_row": "R1-R2",
"ga_now_row": "Geomagnetic Activity Probabilities",
"kp_index_row": "NOAA Kp index breakdown"
}
def __init__(self, parent):
super().__init__()
self.forecast = ''
self.probabilities = ''
self.__labels_table = []
self.__solar_row = None
self.__event_row = None
self.__rb_now_row = None
self.__ga_now_row = None
self.__kp_index_row = None
self._update_thread = UpdateForecastThread(self)
self._update_thread.finished.connect(self._parse_and_emit_signal)
self.today_lbl = parent.today_lbl
self.today_p1_lbl = parent.today_p1_lbl
self.today_p2_lbl = parent.today_p2_lbl
self.__today_lbls = []
self.__today_p1_lbls = []
self.__today_p2_lbls = []
self.__all_lbls = []
flags = ['', 'p1_', 'p2_']
for flag in flags:
title_lbl = getattr(self, "today_" + flag + "lbl")
title_lbl.setText("-")
for index in range(20):
label = getattr(
parent,
"forecast_today_" + flag + str(index) + "_lbl"
)
label.setText(Constants.UNKNOWN)
if flag == flags[0]:
self.__today_lbls.append(label)
if flag == flags[1]:
self.__today_p1_lbls.append(label)
if flag == flags[2]:
self.__today_p2_lbls.append(label)
self.__all_lbls = [
self.__today_lbls,
self.__today_p1_lbls,
self.__today_p2_lbls
]
def _parse_data(self):
self.forecast = self.forecast.splitlines()
# Remove possible '(G\d)' from the kp_index table
self.probabilities = re.sub(
'(G\d)', lambda obj: '', self.probabilities
)
self.probabilities = self.probabilities.splitlines()
def __split_lists(self):
self.forecast = [i.split() for i in self.forecast]
self.probabilities = [i.split() for i in self.probabilities]
def __find_row_with(self, data, text):
for i, row in enumerate(data):
if text in row:
return i
return None
def __get_rows(self):
self.__solar_row = self.__find_row_with(
self.forecast,
self.ROW_KEYWORDS["solar_row"]
)
self.__event_row = self.__find_row_with(
self.probabilities,
self.ROW_KEYWORDS["event_row"]
)
self.__rb_now_row = self.__find_row_with(
self.forecast,
self.ROW_KEYWORDS["rb_now_row"]
)
self.__ga_now_row = self.__find_row_with(
self.probabilities,
self.ROW_KEYWORDS["ga_now_row"]
)
self.__kp_index_row = self.__find_row_with(
self.forecast,
self.ROW_KEYWORDS["kp_index_row"]
)
is_none = lambda x: x is None
if any([
is_none(self.__solar_row),
is_none(self.__event_row),
is_none(self.__rb_now_row),
is_none(self.__ga_now_row),
is_none(self.__kp_index_row)
]):
raise Exception('Missing Rows')
def __set_dates(self):
month = self.forecast[self.__solar_row - 1][0]
today = self.forecast[self.__solar_row - 1][1]
today_p1 = self.forecast[self.__solar_row - 1][3]
today_p2 = self.forecast[self.__solar_row - 1][5]
self.today_lbl.setText(month + ' ' + today)
self.today_p1_lbl.setText(month + ' ' + today_p1)
self.today_p2_lbl.setText(month + ' ' + today_p2)
def __make_labels_table(self):
get_first_split = lambda x: x.split("/")[0]
get_second_split = lambda x: x.split("/")[1]
get_third_split = lambda x: x.split("/")[2]
self.__labels_table = [
[
[self.forecast, self.__solar_row, 3, None],
[self.probabilities, self.__event_row + 1, 2, get_first_split],
[self.probabilities, self.__event_row + 2, 2, get_first_split],
[self.probabilities, self.__event_row + 3, 1, get_first_split],
[self.forecast, self.__rb_now_row, 1, None],
[self.forecast, self.__rb_now_row + 1, 3, None],
[self.probabilities, self.__ga_now_row + 2, 1, get_first_split],
[self.probabilities, self.__ga_now_row + 3, 2, get_first_split],
[self.probabilities, self.__ga_now_row + 4, 2, get_first_split],
[self.probabilities, self.__ga_now_row + 6, 1, get_first_split],
[self.probabilities, self.__ga_now_row + 7, 2, get_first_split],
[self.probabilities, self.__ga_now_row + 8, 2, get_first_split],
[self.forecast, self.__kp_index_row + 3, 1, None],
[self.forecast, self.__kp_index_row + 4, 1, None],
[self.forecast, self.__kp_index_row + 5, 1, None],
[self.forecast, self.__kp_index_row + 6, 1, None],
[self.forecast, self.__kp_index_row + 7, 1, None],
[self.forecast, self.__kp_index_row + 8, 1, None],
[self.forecast, self.__kp_index_row + 9, 1, None],
[self.forecast, self.__kp_index_row + 10, 1, None]
],
[
[self.forecast, self.__solar_row, 4, None],
[self.probabilities, self.__event_row + 1, 2, get_second_split],
[self.probabilities, self.__event_row + 2, 2, get_second_split],
[self.probabilities, self.__event_row + 3, 1, get_second_split],
[self.forecast, self.__rb_now_row, 2, None],
[self.forecast, self.__rb_now_row + 1, 4, None],
[self.probabilities, self.__ga_now_row + 2, 1, get_second_split],
[self.probabilities, self.__ga_now_row + 3, 2, get_second_split],
[self.probabilities, self.__ga_now_row + 4, 2, get_second_split],
[self.probabilities, self.__ga_now_row + 6, 1, get_second_split],
[self.probabilities, self.__ga_now_row + 7, 2, get_second_split],
[self.probabilities, self.__ga_now_row + 8, 2, get_second_split],
[self.forecast, self.__kp_index_row + 3, 2, None],
[self.forecast, self.__kp_index_row + 4, 2, None],
[self.forecast, self.__kp_index_row + 5, 2, None],
[self.forecast, self.__kp_index_row + 6, 2, None],
[self.forecast, self.__kp_index_row + 7, 2, None],
[self.forecast, self.__kp_index_row + 8, 2, None],
[self.forecast, self.__kp_index_row + 9, 2, None],
[self.forecast, self.__kp_index_row + 10, 2, None]
],
[
[self.forecast, self.__solar_row, 5, None],
[self.probabilities, self.__event_row + 1, 2, get_third_split],
[self.probabilities, self.__event_row + 2, 2, get_third_split],
[self.probabilities, self.__event_row + 3, 1, get_third_split],
[self.forecast, self.__rb_now_row, 3, None],
[self.forecast, self.__rb_now_row + 1, 5, None],
[self.probabilities, self.__ga_now_row + 2, 1, get_third_split],
[self.probabilities, self.__ga_now_row + 3, 2, get_third_split],
[self.probabilities, self.__ga_now_row + 4, 2, get_third_split],
[self.probabilities, self.__ga_now_row + 6, 1, get_third_split],
[self.probabilities, self.__ga_now_row + 7, 2, get_third_split],
[self.probabilities, self.__ga_now_row + 8, 2, get_third_split],
[self.forecast, self.__kp_index_row + 3, 3, None],
[self.forecast, self.__kp_index_row + 4, 3, None],
[self.forecast, self.__kp_index_row + 5, 3, None],
[self.forecast, self.__kp_index_row + 6, 3, None],
[self.forecast, self.__kp_index_row + 7, 3, None],
[self.forecast, self.__kp_index_row + 8, 3, None],
[self.forecast, self.__kp_index_row + 9, 3, None],
[self.forecast, self.__kp_index_row + 10, 3, None]
]
]
def __get_lbl_value(self, data, row, col, f = None):
val = data[row][col]
if f is not None:
val = f(val)
val = val.lstrip('0').rstrip('%')
return val
def __is_integer(self, s):
try:
int(s)
except Exception:
return False
else:
return True
def __set_labels_values(self):
for lbl_list, table in zip(self.__all_lbls, self.__labels_table):
for lbl, row in zip(lbl_list, table):
lbl.switch_off()
value = self.__get_lbl_value(*row)
if self.__is_integer(value):
lbl.level = int(value)
if not isinstance(lbl, MultiColorSwitchableLabel):
value += '%'
lbl.setText(value)
lbl.switch_on()
def update_all_labels(self):
try:
self.__get_rows()
self.__split_lists()
self.__make_labels_table()
self.__set_dates()
self.__set_labels_values()
except Exception:
pass
def remove_data(self):
self.forecast = ''
self.probabilities = ''