4
.gitignore
vendored
4
.gitignore
vendored
@@ -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/
|
||||||
|
|||||||
283
artemis.py
283
artemis.py
@@ -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,7 +768,6 @@ 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()
|
||||||
@@ -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():
|
||||||
|
if item.text(0) in parents:
|
||||||
item.setSelected(False)
|
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_())
|
||||||
|
|||||||
2557
artemis.ui
2557
artemis.ui
File diff suppressed because it is too large
Load Diff
31
clickable_progress_bar.py
Normal file
31
clickable_progress_bar.py
Normal 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)
|
||||||
20
constants.py
20
constants.py
@@ -67,7 +67,24 @@ class Database(object):
|
|||||||
Signal.CATEGORY_CODE,)
|
Signal.CATEGORY_CODE,)
|
||||||
|
|
||||||
class Constants(object):
|
class Constants(object):
|
||||||
|
CLICK_TO_UPDATE_STR = "Click to update"
|
||||||
|
UPDATING_STR = "Updating..."
|
||||||
ACF_DOCS = "https://aresvalley.com/documentation/"
|
ACF_DOCS = "https://aresvalley.com/documentation/"
|
||||||
|
FORECAST_XRAY = "https://services.swpc.noaa.gov/text/goes-xray-flux-primary.txt"
|
||||||
|
FORECAST_PROT = "https://services.swpc.noaa.gov/text/goes-particle-flux-primary.txt"
|
||||||
|
FORECAST_AK_IND = "https://services.swpc.noaa.gov/text/wwv.txt"
|
||||||
|
FORECAST_SGAS = "https://services.swpc.noaa.gov/text/sgas.txt"
|
||||||
|
FORECAST_G = "https://services.swpc.noaa.gov/text/3-day-forecast.txt"
|
||||||
|
FORECAST_INFO = "https://www.swpc.noaa.gov/sites/default/files/images/NOAAscales.pdf"
|
||||||
|
FORECAST_IMG_0 = "http://www.mmmonvhf.de/eme/eme.png"
|
||||||
|
FORECAST_IMG_1 = "http://www.mmmonvhf.de/ms/ms.png"
|
||||||
|
FORECAST_IMG_2 = "http://www.mmmonvhf.de/es/es.png"
|
||||||
|
FORECAST_IMG_3 = "http://www.mmmonvhf.de/solar/solar.png"
|
||||||
|
FORECAST_IMG_4 = "http://amunters.home.xs4all.nl/eskipstatusNA.gif"
|
||||||
|
FORECAST_IMG_5 = "http://amunters.home.xs4all.nl/aurorastatus.gif"
|
||||||
|
FORECAST_IMG_6 = "http://amunters.home.xs4all.nl/eskipstatus.gif"
|
||||||
|
FORECAST_IMG_7 = "http://amunters.home.xs4all.nl/eskip50status.gif"
|
||||||
|
FORECAST_IMG_8 = "http://amunters.home.xs4all.nl/eskip70status.gif"
|
||||||
SEARCH_LABEL_IMG = "search_icon.png"
|
SEARCH_LABEL_IMG = "search_icon.png"
|
||||||
VOLUME_LABEL_IMG = "volume.png"
|
VOLUME_LABEL_IMG = "volume.png"
|
||||||
DATA_FOLDER = "Data"
|
DATA_FOLDER = "Data"
|
||||||
@@ -76,6 +93,9 @@ class Constants(object):
|
|||||||
AUDIO_FOLDER = "Audio"
|
AUDIO_FOLDER = "Audio"
|
||||||
ACTIVE = "active"
|
ACTIVE = "active"
|
||||||
INACTIVE = "inactive"
|
INACTIVE = "inactive"
|
||||||
|
LABEL_ON_COLOR = "on"
|
||||||
|
LABEL_OFF_COLOR = "off"
|
||||||
|
TEXT_COLOR = "text"
|
||||||
NOT_AVAILABLE = "spectrumnotavailable.png"
|
NOT_AVAILABLE = "spectrumnotavailable.png"
|
||||||
NOT_SELECTED = "nosignalselected.png"
|
NOT_SELECTED = "nosignalselected.png"
|
||||||
__Band = namedtuple("Band", ["lower", "upper"])
|
__Band = namedtuple("Band", ["lower", "upper"])
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
27
fixed_aspect_ratio_label.py
Normal file
27
fixed_aspect_ratio_label.py
Normal 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()
|
||||||
20
fixed_aspect_ratio_widget.py
Normal file
20
fixed_aspect_ratio_widget.py
Normal 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 |
4916
main_window.ui
4916
main_window.ui
File diff suppressed because it is too large
Load Diff
66
space_weather_data.py
Normal file
66
space_weather_data.py
Normal 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
56
switchable_label.py
Normal 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()
|
||||||
100
themes.py
100
themes.py
@@ -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
|
||||||
|
if self.__theme_path != self.__current_theme:
|
||||||
self.__change()
|
self.__change()
|
||||||
self.__parent.display_specs(self.__parent.result_list.currentItem(), None)
|
self.__parent.display_specs(self.__parent.result_list.currentItem(), None)
|
||||||
self.__refresh_range_labels()
|
self.__refresh_range_labels()
|
||||||
self.__parent.audio_widget.refresh_btns_colors(self.__parent.active_color, self.__parent.inactive_color)
|
self.__parent.audio_widget.refresh_btns_colors(self.__parent.active_color, self.__parent.inactive_color)
|
||||||
|
self.__forecast_labels.refresh()
|
||||||
|
|
||||||
|
def __pretty_name(self, bad_name):
|
||||||
|
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(',')]
|
||||||
|
else:
|
||||||
|
color = [color]
|
||||||
|
if len(color) > 2:
|
||||||
|
break
|
||||||
|
if is_valid_html_color(color):
|
||||||
|
if quality.lower() == Constants.ACTIVE:
|
||||||
|
self.__parent.active_color = color[0]
|
||||||
active_color_ok = True
|
active_color_ok = True
|
||||||
if quality.lower() == Constants.INACTIVE and is_valid_html_color(color):
|
if quality.lower() == Constants.INACTIVE:
|
||||||
self.__parent.inactive_color = color
|
self.__parent.inactive_color = color[0]
|
||||||
inactive_color_ok = True
|
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.__theme_names[theme_name].setChecked(True)
|
||||||
self.__apply(theme_path)
|
self.__apply(theme_path)
|
||||||
|
else:
|
||||||
|
self.__theme_names[self.__pretty_name(ThemeConstants.DEFAULT)].setChecked(True)
|
||||||
|
self.__apply(os.path.join(ThemeConstants.FOLDER, ThemeConstants.DEFAULT))
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
active=#4da6ff
|
active=#4545e5
|
||||||
inactive=#546E7A
|
inactive=#546E7A
|
||||||
|
off=#283048,#859398
|
||||||
|
on=#4776e6, #8e54e9
|
||||||
|
text=#ffffff
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
active=#88cc00
|
active=#88cc00
|
||||||
inactive=#546E7A
|
inactive=#546E7A
|
||||||
|
off=#304352,#d7d2cc
|
||||||
|
on=#3ca55c,#b5ac49
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
active=#6ECE12
|
active=#6ECE12
|
||||||
inactive=#b3b3cc
|
inactive=#b3b3cc
|
||||||
|
off=#948e99,#2e1437
|
||||||
|
on=#b993d6,#8ca6db
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
45
threads.py
45
threads.py
@@ -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
|
||||||
|
|||||||
12
utilities.py
12
utilities.py
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user