Major refactor of filters and spaceweather
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ src/themes/.current_theme
|
||||
designer.bat
|
||||
launch.bat
|
||||
.vscode/
|
||||
.code-workspace
|
||||
|
||||
7
artemis-workspace.code-workspace
Normal file
7
artemis-workspace.code-workspace
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
1017
src/artemis.py
1017
src/artemis.py
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ class AudioPlayer(QObject):
|
||||
"""Subclass QObject. Audio player widget for the audio samples.
|
||||
|
||||
The only public methods are the __init__
|
||||
method, set_audio_player, which loads the current file and refresh_btns_colors.
|
||||
method, set_audio_player, which loads the current file and refresh.
|
||||
Everything else is managed internally."""
|
||||
|
||||
_TIME_STEP = 500 # Milliseconds.
|
||||
@@ -47,7 +47,7 @@ class AudioPlayer(QObject):
|
||||
self._pause.setIconSize(self._pause.size())
|
||||
self._stop.setIconSize(self._stop.size())
|
||||
self._loop.setIconSize(self._loop.size())
|
||||
self.refresh_btns_colors(active_color, inactive_color)
|
||||
self.refresh(active_color, inactive_color)
|
||||
|
||||
@pyqtSlot()
|
||||
def _set_loop_icon(self):
|
||||
@@ -67,7 +67,7 @@ class AudioPlayer(QObject):
|
||||
)
|
||||
self._loop.setIcon(loop_icon)
|
||||
|
||||
def refresh_btns_colors(self, active_color, inactive_color):
|
||||
def refresh(self, active_color, inactive_color):
|
||||
"""Repaint the buttons of the widgetd after the theme has changed."""
|
||||
self._active_color = active_color
|
||||
self._inactive_color = inactive_color
|
||||
|
||||
@@ -41,7 +41,7 @@ class Messages:
|
||||
NO_CONNECTION = "No connection"
|
||||
NO_CONNECTION_MSG = "Unable to establish an internet connection."
|
||||
BAD_DOWNLOAD = "Something went wrong"
|
||||
BAD_DOWNLOAD_MSG = "Something went wrong with the downaload.\nCheck your internet connection and try again."
|
||||
BAD_DOWNLOAD_MSG = "Something went wrong with the download.\nCheck your internet connection and try again."
|
||||
SLOW_CONN = "Slow internet connection"
|
||||
SLOW_CONN_MSG = "Your internet connection is unstable or too slow."
|
||||
|
||||
|
||||
850
src/filters.py
Normal file
850
src/filters.py
Normal file
@@ -0,0 +1,850 @@
|
||||
"""This module contains all the filter-related classes and functions.
|
||||
|
||||
The only class exposed is Filters which provides the following methods:
|
||||
- ok(signal_name): to check if all the filters are passed;
|
||||
- reset(): to reset all the applied filters;
|
||||
- refresh(): used when the theme is changed."""
|
||||
|
||||
from collections import namedtuple
|
||||
from functools import partial
|
||||
import webbrowser
|
||||
|
||||
from PyQt5.QtWidgets import QListWidgetItem, QTreeWidgetItem
|
||||
from PyQt5.QtCore import pyqtSlot, QObject
|
||||
|
||||
from constants import (Constants,
|
||||
Ftype,
|
||||
Signal,)
|
||||
from utilities import (uncheck_and_emit,
|
||||
connect_events_to_func,
|
||||
filters_limit,
|
||||
is_undef_freq,
|
||||
is_undef_band,
|
||||
safe_cast,
|
||||
show_matching_strings,
|
||||
get_field_entries,)
|
||||
|
||||
|
||||
class _BaseFilter(QObject):
|
||||
"""Base class for all filters."""
|
||||
|
||||
def __init__(self, owner):
|
||||
"""Positional argument:
|
||||
owner - the object containing the filter screen."""
|
||||
super().__init__()
|
||||
self._owner = owner
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh the screen."""
|
||||
pass
|
||||
|
||||
|
||||
class _FreqBandMixIn:
|
||||
"""Mixin class for the frequency and band filters.
|
||||
|
||||
Provides some functions used in both classes."""
|
||||
|
||||
@pyqtSlot()
|
||||
def _set_min_value_upper_limit(self, lower_combo_box,
|
||||
lower_spin_box,
|
||||
upper_combo_box,
|
||||
upper_spin_box):
|
||||
"""Forbid to a lower limit to be greater than the corresponding upper one.
|
||||
|
||||
Used for frequency and bandwidth screens."""
|
||||
if lower_spin_box.isEnabled():
|
||||
unit_conversion = {
|
||||
'Hz': ['kHz', 'MHz', 'GHz'],
|
||||
'kHz': ['MHz', 'GHz'],
|
||||
'MHz': ['GHz']
|
||||
}
|
||||
lower_units = lower_combo_box.currentText()
|
||||
upper_units = upper_combo_box.currentText()
|
||||
lower_value = lower_spin_box.value()
|
||||
inf_limit = (lower_value * Constants.CONVERSION_FACTORS[lower_units]) \
|
||||
// Constants.CONVERSION_FACTORS[upper_units]
|
||||
counter = 0
|
||||
while inf_limit > upper_spin_box.maximum():
|
||||
counter += 1
|
||||
inf_limit //= 1000
|
||||
if upper_spin_box.minimum() != inf_limit:
|
||||
upper_spin_box.setMinimum(inf_limit)
|
||||
if counter > 0:
|
||||
new_unit = unit_conversion[upper_units][counter - 1]
|
||||
upper_combo_box.disconnect()
|
||||
upper_combo_box.setCurrentText(new_unit)
|
||||
upper_combo_box.currentTextChanged.connect(
|
||||
partial(
|
||||
self._set_min_value_upper_limit,
|
||||
lower_combo_box,
|
||||
lower_spin_box,
|
||||
upper_combo_box,
|
||||
upper_spin_box
|
||||
)
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def _reset_fb_filters(self, ftype):
|
||||
"""Reset the Frequency or Bandwidth depending on 'ftype'.
|
||||
|
||||
ftype can be either Ftype.FREQ or Ftype.BAND.
|
||||
"""
|
||||
if ftype != Ftype.FREQ and ftype != Ftype.BAND:
|
||||
raise ValueError("Wrong ftype in function '_reset_fb_filters'")
|
||||
|
||||
apply_remove_btn = getattr(self._owner, 'apply_remove_' + ftype + '_filter_btn')
|
||||
include_undef_btn = getattr(self._owner, 'include_undef_' + ftype + 's')
|
||||
activate_low = getattr(self._owner, 'activate_low_' + ftype + '_filter_btn')
|
||||
activate_up = getattr(self._owner, 'activate_up_' + ftype + '_filter_btn')
|
||||
lower_unit = getattr(self._owner, 'lower_' + ftype + '_filter_unit')
|
||||
upper_unit = getattr(self._owner, 'upper_' + ftype + '_filter_unit')
|
||||
lower_spinbox = getattr(self._owner, 'lower_' + ftype + '_spinbox')
|
||||
upper_spinbox = getattr(self._owner, 'upper_' + ftype + '_spinbox')
|
||||
lower_confidence = getattr(self._owner, 'lower_' + ftype + '_confidence')
|
||||
upper_confidence = getattr(self._owner, 'lower_' + ftype + '_confidence')
|
||||
|
||||
default_val = 1 if ftype == Ftype.FREQ else 5000
|
||||
if ftype == Ftype.FREQ:
|
||||
for f in self._frequency_filters_btns:
|
||||
if f.isChecked():
|
||||
f.setChecked(False)
|
||||
uncheck_and_emit(apply_remove_btn)
|
||||
if include_undef_btn.isChecked():
|
||||
include_undef_btn.setChecked(False)
|
||||
uncheck_and_emit(activate_low)
|
||||
uncheck_and_emit(activate_up)
|
||||
lower_unit.setCurrentText("MHz")
|
||||
upper_unit.setCurrentText("MHz")
|
||||
lower_spinbox.setValue(default_val)
|
||||
upper_spinbox.setMinimum(1)
|
||||
upper_spinbox.setValue(default_val)
|
||||
lower_confidence.setValue(0)
|
||||
upper_confidence.setValue(0)
|
||||
|
||||
@pyqtSlot()
|
||||
def _set_band_filter_label(self,
|
||||
activate_low_btn,
|
||||
lower_spinbox,
|
||||
lower_unit,
|
||||
lower_confidence,
|
||||
activate_up_btn,
|
||||
upper_spinbox,
|
||||
upper_unit,
|
||||
upper_confidence,
|
||||
range_lbl):
|
||||
"""Display the actual range applied for the signal's property search.
|
||||
|
||||
Used for frequency and bandwidth screens."""
|
||||
activate_low = False
|
||||
activate_high = False
|
||||
color = self._owner.inactive_color
|
||||
title = ''
|
||||
to_display = ''
|
||||
if activate_low_btn.isChecked():
|
||||
activate_low = True
|
||||
color = self._owner.active_color
|
||||
min_value = lower_spinbox.value()
|
||||
if lower_confidence.value() != 0:
|
||||
min_value -= lower_spinbox.value() * lower_confidence.value() / 100
|
||||
to_display += str(round(min_value, Constants.MAX_DIGITS)) \
|
||||
+ ' ' + lower_unit.currentText()
|
||||
else:
|
||||
to_display += 'DC'
|
||||
to_display += Constants.RANGE_SEPARATOR
|
||||
if activate_up_btn.isChecked():
|
||||
max_value = upper_spinbox.value()
|
||||
activate_high = True
|
||||
color = self._owner.active_color
|
||||
if upper_confidence.value() != 0:
|
||||
max_value += upper_spinbox.value() * upper_confidence.value() / 100
|
||||
to_display += str(round(max_value, Constants.MAX_DIGITS)) + ' ' \
|
||||
+ upper_unit.currentText()
|
||||
else:
|
||||
to_display += 'INF'
|
||||
if activate_low and activate_high:
|
||||
title = 'Band-pass\n\n'
|
||||
elif activate_low and not activate_high:
|
||||
title = 'Low-pass\n\n'
|
||||
elif not activate_low and activate_high:
|
||||
title = 'High-pass\n\n'
|
||||
else:
|
||||
title = "Selected range:\n\n"
|
||||
to_display = "Inactive"
|
||||
to_display = title + to_display
|
||||
range_lbl.setText(to_display)
|
||||
range_lbl.setStyleSheet(f'color: {color};')
|
||||
|
||||
|
||||
class FreqFilter(_BaseFilter, _FreqBandMixIn):
|
||||
"""Frequency filter class."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_freq_filter_btn
|
||||
self.reset_btn = self._owner.reset_frequency_filters_btn
|
||||
self._frequency_filters_btns = (
|
||||
self._owner.elf_filter_btn,
|
||||
self._owner.slf_filter_btn,
|
||||
self._owner.ulf_filter_btn,
|
||||
self._owner.vlf_filter_btn,
|
||||
self._owner.lf_filter_btn,
|
||||
self._owner.mf_filter_btn,
|
||||
self._owner.hf_filter_btn,
|
||||
self._owner.vhf_filter_btn,
|
||||
self._owner.uhf_filter_btn,
|
||||
self._owner.shf_filter_btn,
|
||||
self._owner.ehf_filter_btn,
|
||||
)
|
||||
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
*self._frequency_filters_btns,
|
||||
self._owner.include_undef_freqs,
|
||||
self._owner.activate_low_freq_filter_btn,
|
||||
self._owner.activate_up_freq_filter_btn
|
||||
],
|
||||
radio_1=self._owner.activate_low_freq_filter_btn,
|
||||
ruled_by_radio_1=[
|
||||
self._owner.lower_freq_spinbox,
|
||||
self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_confidence
|
||||
],
|
||||
radio_2=self._owner.activate_up_freq_filter_btn,
|
||||
ruled_by_radio_2=[
|
||||
self._owner.upper_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_confidence
|
||||
]
|
||||
)
|
||||
|
||||
connect_events_to_func(
|
||||
events_to_connect=[self._owner.lower_freq_spinbox.valueChanged,
|
||||
self._owner.upper_freq_spinbox.valueChanged,
|
||||
self._owner.lower_freq_filter_unit.currentTextChanged,
|
||||
self._owner.upper_freq_filter_unit.currentTextChanged,
|
||||
self._owner.activate_low_freq_filter_btn.toggled],
|
||||
fun_to_connect=self._set_min_value_upper_limit,
|
||||
fun_args=[self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_spinbox]
|
||||
)
|
||||
|
||||
connect_events_to_func(
|
||||
events_to_connect=[self._owner.lower_freq_spinbox.valueChanged,
|
||||
self._owner.upper_freq_spinbox.valueChanged,
|
||||
self._owner.lower_freq_filter_unit.currentTextChanged,
|
||||
self._owner.upper_freq_filter_unit.currentTextChanged,
|
||||
self._owner.activate_low_freq_filter_btn.clicked,
|
||||
self._owner.activate_up_freq_filter_btn.clicked,
|
||||
self._owner.lower_freq_confidence.valueChanged,
|
||||
self._owner.upper_freq_confidence.valueChanged],
|
||||
fun_to_connect=self._set_band_filter_label,
|
||||
fun_args=[self._owner.activate_low_freq_filter_btn,
|
||||
self._owner.lower_freq_spinbox,
|
||||
self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_confidence,
|
||||
self._owner.activate_up_freq_filter_btn,
|
||||
self._owner.upper_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_confidence,
|
||||
self._owner.freq_range_lbl]
|
||||
)
|
||||
|
||||
self._owner.activate_low_freq_filter_btn.toggled.connect(
|
||||
partial(self._owner.activate_if_toggled,
|
||||
self._owner.activate_low_freq_filter_btn,
|
||||
self._owner.lower_freq_spinbox,
|
||||
self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_confidence)
|
||||
)
|
||||
|
||||
self._owner.activate_up_freq_filter_btn.toggled.connect(
|
||||
partial(self._owner.activate_if_toggled,
|
||||
self._owner.activate_up_freq_filter_btn,
|
||||
self._owner.upper_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_confidence)
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the filter screen."""
|
||||
self._reset_fb_filters(Ftype.FREQ)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the frequency filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
undef_freq = is_undef_freq(self._owner.db.loc[signal_name])
|
||||
if undef_freq:
|
||||
if self._owner.include_undef_freqs.isChecked():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
signal_freqs = (
|
||||
safe_cast(self._owner.db.at[signal_name, Signal.INF_FREQ], int),
|
||||
safe_cast(self._owner.db.at[signal_name, Signal.SUP_FREQ], int)
|
||||
)
|
||||
|
||||
band_filter_ok = False
|
||||
any_checked = False
|
||||
for btn, band_limits in zip(self._frequency_filters_btns, Constants.BANDS):
|
||||
if btn.isChecked():
|
||||
any_checked = True
|
||||
if signal_freqs[0] < band_limits.upper and signal_freqs[1] >= band_limits.lower:
|
||||
band_filter_ok = True
|
||||
lower_limit_ok = True
|
||||
upper_limit_ok = True
|
||||
if self._owner.activate_low_freq_filter_btn.isChecked():
|
||||
if not signal_freqs[1] >= filters_limit(self._owner.lower_freq_spinbox,
|
||||
self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_confidence, -1):
|
||||
lower_limit_ok = False
|
||||
if self._owner.activate_up_freq_filter_btn.isChecked():
|
||||
if not signal_freqs[0] < filters_limit(self._owner.upper_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_confidence):
|
||||
upper_limit_ok = False
|
||||
if any_checked:
|
||||
return band_filter_ok and lower_limit_ok and upper_limit_ok
|
||||
else:
|
||||
return lower_limit_ok and upper_limit_ok
|
||||
|
||||
def refresh(self):
|
||||
"""Extend _BaseFilter.refresh."""
|
||||
super().refresh()
|
||||
self._set_band_filter_label(
|
||||
self._owner.activate_low_band_filter_btn,
|
||||
self._owner.lower_band_spinbox,
|
||||
self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_confidence,
|
||||
self._owner.activate_up_band_filter_btn,
|
||||
self._owner.upper_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_confidence,
|
||||
self._owner.band_range_lbl
|
||||
)
|
||||
|
||||
|
||||
class BandFilter(_BaseFilter, _FreqBandMixIn):
|
||||
"""Band filter class."""
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_band_filter_btn
|
||||
self.reset_btn = self._owner.reset_band_filters_btn
|
||||
connect_events_to_func(
|
||||
events_to_connect=[self._owner.lower_band_spinbox.valueChanged,
|
||||
self._owner.upper_band_spinbox.valueChanged,
|
||||
self._owner.lower_band_filter_unit.currentTextChanged,
|
||||
self._owner.upper_band_filter_unit.currentTextChanged,
|
||||
self._owner.activate_low_band_filter_btn.toggled],
|
||||
fun_to_connect=self._set_min_value_upper_limit,
|
||||
fun_args=[self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_spinbox]
|
||||
)
|
||||
|
||||
connect_events_to_func(
|
||||
events_to_connect=[self._owner.lower_band_spinbox.valueChanged,
|
||||
self._owner.upper_band_spinbox.valueChanged,
|
||||
self._owner.lower_band_filter_unit.currentTextChanged,
|
||||
self._owner.upper_band_filter_unit.currentTextChanged,
|
||||
self._owner.activate_low_band_filter_btn.clicked,
|
||||
self._owner.activate_up_band_filter_btn.clicked,
|
||||
self._owner.lower_band_confidence.valueChanged,
|
||||
self._owner.upper_band_confidence.valueChanged],
|
||||
fun_to_connect=self._set_band_filter_label,
|
||||
fun_args=[self._owner.activate_low_band_filter_btn,
|
||||
self._owner.lower_band_spinbox,
|
||||
self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_confidence,
|
||||
self._owner.activate_up_band_filter_btn,
|
||||
self._owner.upper_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_confidence,
|
||||
self._owner.band_range_lbl]
|
||||
)
|
||||
|
||||
self._owner.activate_low_band_filter_btn.toggled.connect(
|
||||
partial(self._owner.activate_if_toggled,
|
||||
self._owner.activate_low_band_filter_btn,
|
||||
self._owner.lower_band_spinbox,
|
||||
self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_confidence)
|
||||
)
|
||||
|
||||
self._owner.activate_up_band_filter_btn.toggled.connect(
|
||||
partial(self._owner.activate_if_toggled,
|
||||
self._owner.activate_up_band_filter_btn,
|
||||
self._owner.upper_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_confidence)
|
||||
)
|
||||
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
self._owner.include_undef_bands,
|
||||
self._owner.activate_low_band_filter_btn,
|
||||
self._owner.activate_up_band_filter_btn
|
||||
],
|
||||
radio_1=self._owner.activate_low_band_filter_btn,
|
||||
ruled_by_radio_1=[
|
||||
self._owner.lower_band_spinbox,
|
||||
self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_confidence
|
||||
],
|
||||
radio_2=self._owner.activate_up_band_filter_btn,
|
||||
ruled_by_radio_2=[
|
||||
self._owner.upper_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_confidence
|
||||
]
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the filter screen."""
|
||||
self._reset_fb_filters(Ftype.BAND)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the band filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
undef_band = is_undef_band(self._owner.db.loc[signal_name])
|
||||
if undef_band:
|
||||
if self._owner.include_undef_bands.isChecked():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
signal_bands = (
|
||||
safe_cast(self._owner.db.at[signal_name, Signal.INF_BAND], int),
|
||||
safe_cast(self._owner.db.at[signal_name, Signal.SUP_BAND], int)
|
||||
)
|
||||
|
||||
lower_limit_ok = True
|
||||
upper_limit_ok = True
|
||||
if self._owner.activate_low_band_filter_btn.isChecked():
|
||||
if not signal_bands[1] >= filters_limit(self._owner.lower_band_spinbox,
|
||||
self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_confidence, -1):
|
||||
lower_limit_ok = False
|
||||
if self._owner.activate_up_band_filter_btn.isChecked():
|
||||
if not signal_bands[0] < filters_limit(self._owner.upper_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_confidence):
|
||||
upper_limit_ok = False
|
||||
return lower_limit_ok and upper_limit_ok
|
||||
|
||||
def refresh(self):
|
||||
"""Extend _BaseFilter.refresh."""
|
||||
super().refresh()
|
||||
self._set_band_filter_label(
|
||||
self._owner.activate_low_freq_filter_btn,
|
||||
self._owner.lower_freq_spinbox,
|
||||
self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_confidence,
|
||||
self._owner.activate_up_freq_filter_btn,
|
||||
self._owner.upper_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_confidence,
|
||||
self._owner.freq_range_lbl
|
||||
)
|
||||
|
||||
|
||||
class CatFilter(_BaseFilter):
|
||||
"""Category filter class."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_cat_filter_btn
|
||||
self.reset_btn = self._owner.reset_cat_filters_btn
|
||||
# Order matters!
|
||||
self._cat_filter_btns = [
|
||||
self._owner.military_btn,
|
||||
self._owner.radar_btn,
|
||||
self._owner.active_btn,
|
||||
self._owner.inactive_btn,
|
||||
self._owner.ham_btn,
|
||||
self._owner.commercial_btn,
|
||||
self._owner.aviation_btn,
|
||||
self._owner.marine_btn,
|
||||
self._owner.analogue_btn,
|
||||
self._owner.digital_btn,
|
||||
self._owner.trunked_btn,
|
||||
self._owner.utility_btn,
|
||||
self._owner.sat_btn,
|
||||
self._owner.navigation_btn,
|
||||
self._owner.interfering_btn,
|
||||
self._owner.number_stations_btn,
|
||||
self._owner.time_signal_btn
|
||||
]
|
||||
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
*self._cat_filter_btns,
|
||||
self._owner.cat_at_least_one,
|
||||
self._owner.cat_all
|
||||
]
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the category filter screen."""
|
||||
uncheck_and_emit(self.apply_remove_btn)
|
||||
for f in self._cat_filter_btns:
|
||||
if f.isChecked():
|
||||
f.setChecked(False)
|
||||
self._owner.cat_at_least_one.setChecked(True)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the category filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
cat_code = self._owner.db.at[signal_name, Signal.CATEGORY_CODE]
|
||||
cat_checked = 0
|
||||
positive_cases = 0
|
||||
for index, cat in enumerate(self._cat_filter_btns):
|
||||
if cat.isChecked():
|
||||
cat_checked += 1
|
||||
if cat_code[index] == '1':
|
||||
positive_cases += 1
|
||||
if self._owner.cat_at_least_one.isChecked():
|
||||
return positive_cases > 0
|
||||
else:
|
||||
return cat_checked == positive_cases and cat_checked > 0
|
||||
|
||||
|
||||
class ModeFilter(_BaseFilter):
|
||||
"""Mode filter class."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_mode_filter_btn
|
||||
self.reset_btn = self._owner.reset_mode_filters_btn
|
||||
self._set_mode_tree_widget()
|
||||
self._owner.mode_tree_widget.itemSelectionChanged.connect(
|
||||
self._manage_mode_selections
|
||||
)
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
self._owner.mode_tree_widget,
|
||||
self._owner.include_unknown_modes_btn
|
||||
]
|
||||
)
|
||||
|
||||
def _manage_mode_selections(self):
|
||||
"""Rules the selection of childs items of the 'Mode' QTreeWidget.
|
||||
|
||||
If a parent is selected all its children will be selected as well.
|
||||
"""
|
||||
selected_items = self._owner.mode_tree_widget.selectedItems()
|
||||
parents = Constants.MODES.keys()
|
||||
for parent in parents:
|
||||
for item in selected_items:
|
||||
if parent == item.text(0):
|
||||
for i in range(len(Constants.MODES[parent])):
|
||||
item.child(i).setSelected(True)
|
||||
|
||||
def _set_mode_tree_widget(self):
|
||||
"""Construct the QTreeWidget for the 'Mode' screen."""
|
||||
for parent, children in Constants.MODES.items():
|
||||
iparent = QTreeWidgetItem([parent])
|
||||
self._owner.mode_tree_widget.addTopLevelItem(iparent)
|
||||
for child in children:
|
||||
ichild = QTreeWidgetItem([child])
|
||||
iparent.addChild(ichild)
|
||||
self._owner.mode_tree_widget.expandAll()
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the mode filter screen."""
|
||||
uncheck_and_emit(self.apply_remove_btn)
|
||||
parents = Constants.MODES.keys()
|
||||
selected_children = []
|
||||
for item in self._owner.mode_tree_widget.selectedItems():
|
||||
if item.text(0) in parents:
|
||||
item.setSelected(False)
|
||||
else:
|
||||
selected_children.append(item)
|
||||
for children in selected_children:
|
||||
children.setSelected(False)
|
||||
if self._owner.include_unknown_modes_btn.isChecked():
|
||||
self._owner.include_unknown_modes_btn.setChecked(False)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the mode filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
signal_mode = self._owner.db.at[signal_name, Signal.MODE]
|
||||
if signal_mode == Constants.UNKNOWN:
|
||||
if self._owner.include_unknown_modes_btn.isChecked():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
selected_items = [item for item in self._owner.mode_tree_widget.selectedItems()]
|
||||
selected_items_text = [i.text(0) for i in selected_items]
|
||||
parents = [
|
||||
item for item in selected_items_text
|
||||
if item in Constants.MODES.keys()
|
||||
]
|
||||
ok = []
|
||||
for item in selected_items:
|
||||
if item.text(0) in parents:
|
||||
ok.append(item.text(0) in signal_mode)
|
||||
elif not item.parent().isSelected():
|
||||
ok.append(item.text(0) == signal_mode)
|
||||
return any(ok)
|
||||
|
||||
|
||||
class ModulationFilter(_BaseFilter):
|
||||
"""Modulation filter class."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_modulation_filter_btn
|
||||
self.reset_btn = self._owner.reset_modulation_filters_btn
|
||||
self._owner.search_bar_modulation.textEdited.connect(self._show_matching_modulations)
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
self._owner.search_bar_modulation,
|
||||
self._owner.modulation_list
|
||||
]
|
||||
)
|
||||
self._owner.modulation_list.itemClicked.connect(self._remove_if_unselected_modulation)
|
||||
|
||||
@pyqtSlot(QListWidgetItem)
|
||||
def _remove_if_unselected_modulation(self, item):
|
||||
"""If an item is unselected from the modulations list, hide the item."""
|
||||
if not item.isSelected():
|
||||
self._show_matching_modulations(self.search_bar_modulation.text())
|
||||
|
||||
@pyqtSlot(str)
|
||||
def _show_matching_modulations(self, text):
|
||||
"""Show the modulations which matches 'text'.
|
||||
|
||||
The match criterion is defined in 'show_matching_strings'."""
|
||||
show_matching_strings(self._owner.modulation_list, text)
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the modulation filter screen."""
|
||||
uncheck_and_emit(self.apply_remove_btn)
|
||||
self._owner.search_bar_modulation.setText('')
|
||||
show_matching_strings(
|
||||
self._owner.modulation_list,
|
||||
self._owner.search_bar_modulation.text()
|
||||
)
|
||||
for i in range(self._owner.modulation_list.count()):
|
||||
if self._owner.modulation_list.item(i).isSelected():
|
||||
self._owner.modulation_list.item(i).setSelected(False)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the modulation filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
signal_modulation = get_field_entries(
|
||||
self._owner.db.at[signal_name, Signal.MODULATION]
|
||||
)
|
||||
for item in self._owner.modulation_list.selectedItems():
|
||||
if item.text() in signal_modulation:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class LocFilter(_BaseFilter):
|
||||
"""Location filter class."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_location_filter_btn
|
||||
self.reset_btn = self._owner.reset_location_filters_btn
|
||||
self._owner.search_bar_location.textEdited.connect(
|
||||
self._show_matching_locations
|
||||
)
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
self._owner.search_bar_location,
|
||||
self._owner.locations_list
|
||||
]
|
||||
)
|
||||
self._owner.locations_list.itemClicked.connect(self._remove_if_unselected_location)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def _show_matching_locations(self, text):
|
||||
"""Show the locations which matches 'text'.
|
||||
|
||||
The match criterion is defined in 'show_matching_strings'."""
|
||||
show_matching_strings(self._owner.locations_list, text)
|
||||
|
||||
@pyqtSlot(QListWidgetItem)
|
||||
def _remove_if_unselected_location(self, item):
|
||||
"""If an item is unselected from the locations list, hide the item."""
|
||||
if not item.isSelected():
|
||||
self._show_matching_locations(self._owner.search_bar_location.text())
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the location filter screen."""
|
||||
uncheck_and_emit(self.apply_remove_btn)
|
||||
self._owner.search_bar_location.setText('')
|
||||
show_matching_strings(
|
||||
self._owner.locations_list,
|
||||
self._owner.search_bar_location.text()
|
||||
)
|
||||
for i in range(self._owner.locations_list.count()):
|
||||
if self._owner.locations_list.item(i).isSelected():
|
||||
self._owner.locations_list.item(i).setSelected(False)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the location filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
signal_locations = get_field_entries(
|
||||
self._owner.db.at[signal_name, Signal.LOCATION]
|
||||
)
|
||||
for item in self._owner.locations_list.selectedItems():
|
||||
if item.text() in signal_locations:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ACFFilter(_BaseFilter):
|
||||
"""Autocorrelation function filter class."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__(owner)
|
||||
self.apply_remove_btn = self._owner.apply_remove_acf_filter_btn
|
||||
self.reset_btn = self._owner.reset_acf_filters_btn
|
||||
self.apply_remove_btn.set_texts(Constants.APPLY, Constants.REMOVE)
|
||||
self.apply_remove_btn.set_slave_filters(
|
||||
simple_ones=[
|
||||
self._owner.include_undef_acf,
|
||||
self._owner.acf_spinbox,
|
||||
self._owner.acf_confidence
|
||||
]
|
||||
)
|
||||
self._owner.acf_info_btn.clicked.connect(lambda: webbrowser.open(Constants.ACF_DOCS))
|
||||
|
||||
connect_events_to_func(
|
||||
events_to_connect=[self._owner.acf_spinbox.valueChanged,
|
||||
self._owner.acf_confidence.valueChanged],
|
||||
fun_to_connect=self._set_acf_interval_label,
|
||||
fun_args=None
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def _set_acf_interval_label(self):
|
||||
"""Display the actual acf interval for the search."""
|
||||
tolerance = self._owner.acf_spinbox.value() * self._owner.acf_confidence.value() / 100
|
||||
if tolerance > 0:
|
||||
val = round(self._owner.acf_spinbox.value() - tolerance, Constants.MAX_DIGITS)
|
||||
to_display = f"Selected range:\n\n{val}" + Constants.RANGE_SEPARATOR \
|
||||
+ f"{round(self._owner.acf_spinbox.value() + tolerance, Constants.MAX_DIGITS)} ms"
|
||||
else:
|
||||
to_display = f"Selected value:\n\n{self._owner.acf_spinbox.value()} ms"
|
||||
self._owner.acf_range_lbl.setText(to_display)
|
||||
self._owner.acf_range_lbl.setStyleSheet(f"color: {self._owner.active_color}")
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset the acf filter screen."""
|
||||
uncheck_and_emit(self.apply_remove_btn)
|
||||
if self._owner.include_undef_acf.isChecked():
|
||||
self._owner.include_undef_acf.setChecked(False)
|
||||
self._owner.acf_spinbox.setValue(50)
|
||||
self._owner.acf_confidence.setValue(0)
|
||||
|
||||
def _ok(self, signal_name):
|
||||
"""Evalaute if the signal matches the acf filters."""
|
||||
if not self.apply_remove_btn.isChecked():
|
||||
return True
|
||||
signal_acf = self._owner.db.at[signal_name, Signal.ACF]
|
||||
if signal_acf == Constants.UNKNOWN:
|
||||
if self._owner.include_undef_acf.isChecked():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
signal_acf = safe_cast(signal_acf.rstrip("ms"), float)
|
||||
tolerance = self._owner.acf_spinbox.value() * self._owner.acf_confidence.value() / 100
|
||||
upper_limit = self._owner.acf_spinbox.value() + tolerance
|
||||
lower_limit = self._owner.acf_spinbox.value() - tolerance
|
||||
if signal_acf <= upper_limit and signal_acf >= lower_limit:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def refresh(self):
|
||||
"""Extend _BaseFilter.refresh."""
|
||||
super().refresh()
|
||||
self._set_acf_interval_label()
|
||||
|
||||
|
||||
class Filters(QObject):
|
||||
"""Global filter class.
|
||||
|
||||
Provides the information about all the filters. Its only public attribute
|
||||
is filters, which is a namedtuple containing instances of all the filters.
|
||||
The only exposed methods are reset(), ok(signal_name) and refresh().
|
||||
The class also connects the apply and reset buttons to the relevant functions."""
|
||||
|
||||
_FiltersTuple = namedtuple(
|
||||
"_FiltersTuple",
|
||||
[
|
||||
"freq_filter",
|
||||
"band_filter",
|
||||
"cat_filter",
|
||||
"mode_filter",
|
||||
"modulation_filter",
|
||||
"location_filter",
|
||||
"acf_filter",
|
||||
]
|
||||
)
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__()
|
||||
self.filters = self._FiltersTuple(
|
||||
FreqFilter(owner),
|
||||
BandFilter(owner),
|
||||
CatFilter(owner),
|
||||
ModeFilter(owner),
|
||||
ModulationFilter(owner),
|
||||
LocFilter(owner),
|
||||
ACFFilter(owner),
|
||||
)
|
||||
self._owner = owner
|
||||
self._owner.reset_filters_btn.clicked.connect(self.reset)
|
||||
|
||||
# Connect Apply and Reset buttons clicks to functions.
|
||||
for f in self.filters:
|
||||
f.apply_remove_btn.clicked.connect(self._display_signals)
|
||||
f.reset_btn.clicked.connect(f.reset)
|
||||
|
||||
@pyqtSlot()
|
||||
def _display_signals(self):
|
||||
self._owner.display_signals()
|
||||
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
"""Reset all the filters."""
|
||||
for f in self.filters:
|
||||
f.reset()
|
||||
|
||||
def ok(self, signal_name):
|
||||
"""Check whether all the filters are passed."""
|
||||
return all(f._ok(signal_name) for f in self.filters)
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh the relevant widgets when changing theme."""
|
||||
for f in self.filters:
|
||||
f.refresh()
|
||||
297
src/spaceweathermanager.py
Normal file
297
src/spaceweathermanager.py
Normal file
@@ -0,0 +1,297 @@
|
||||
import webbrowser
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
from constants import Constants, Messages
|
||||
from switchable_label import SwitchableLabelsIterable
|
||||
from weatherdata import SpaceWeatherData
|
||||
from utilities import safe_cast, pop_up
|
||||
|
||||
|
||||
class SpaceWeatherManager(QObject):
|
||||
"""Class to manage the spaceweather screen."""
|
||||
|
||||
def __init__(self, owner):
|
||||
super().__init__()
|
||||
self._owner = owner
|
||||
self._owner.info_now_btn.clicked.connect(
|
||||
lambda: webbrowser.open(Constants.SPACE_WEATHER_INFO)
|
||||
)
|
||||
self._owner.update_now_bar.clicked.connect(self._start_update_space_weather)
|
||||
self._owner.update_now_bar.set_idle()
|
||||
self._owner.space_weather_data = SpaceWeatherData()
|
||||
self._owner.space_weather_data.update_complete.connect(self._update_space_weather)
|
||||
|
||||
self.space_weather_labels = (
|
||||
self._owner.space_weather_lbl_0,
|
||||
self._owner.space_weather_lbl_1,
|
||||
self._owner.space_weather_lbl_2,
|
||||
self._owner.space_weather_lbl_3,
|
||||
self._owner.space_weather_lbl_4,
|
||||
self._owner.space_weather_lbl_5,
|
||||
self._owner.space_weather_lbl_6,
|
||||
self._owner.space_weather_lbl_7,
|
||||
self._owner.space_weather_lbl_8
|
||||
)
|
||||
|
||||
for lab in self.space_weather_labels:
|
||||
lab.set_default_stylesheet()
|
||||
|
||||
self._owner.space_weather_label_container.labels = self.space_weather_labels
|
||||
self._owner.space_weather_label_name_container.labels = [
|
||||
self._owner.eme_lbl,
|
||||
self._owner.ms_lbl,
|
||||
self._owner.muf_lbl,
|
||||
self._owner.hi_lbl,
|
||||
self._owner.eu50_lbl,
|
||||
self._owner.eu70_lbl,
|
||||
self._owner.eu144_lbl,
|
||||
self._owner.na_lbl,
|
||||
self._owner.aurora_lbl
|
||||
]
|
||||
|
||||
self._switchable_r_labels = SwitchableLabelsIterable(
|
||||
self._owner.r0_now_lbl,
|
||||
self._owner.r1_now_lbl,
|
||||
self._owner.r2_now_lbl,
|
||||
self._owner.r3_now_lbl,
|
||||
self._owner.r4_now_lbl,
|
||||
self._owner.r5_now_lbl
|
||||
)
|
||||
|
||||
self._switchable_s_labels = SwitchableLabelsIterable(
|
||||
self._owner.s0_now_lbl,
|
||||
self._owner.s1_now_lbl,
|
||||
self._owner.s2_now_lbl,
|
||||
self._owner.s3_now_lbl,
|
||||
self._owner.s4_now_lbl,
|
||||
self._owner.s5_now_lbl
|
||||
)
|
||||
|
||||
self._switchable_g_now_labels = SwitchableLabelsIterable(
|
||||
self._owner.g0_now_lbl,
|
||||
self._owner.g1_now_lbl,
|
||||
self._owner.g2_now_lbl,
|
||||
self._owner.g3_now_lbl,
|
||||
self._owner.g4_now_lbl,
|
||||
self._owner.g5_now_lbl
|
||||
)
|
||||
|
||||
self._switchable_g_today_labels = SwitchableLabelsIterable(
|
||||
self._owner.g0_today_lbl,
|
||||
self._owner.g1_today_lbl,
|
||||
self._owner.g2_today_lbl,
|
||||
self._owner.g3_today_lbl,
|
||||
self._owner.g4_today_lbl,
|
||||
self._owner.g5_today_lbl
|
||||
)
|
||||
|
||||
self._k_storm_labels = SwitchableLabelsIterable(
|
||||
self._owner.k_ex_sev_storm_lbl,
|
||||
self._owner.k_very_sev_storm_lbl,
|
||||
self._owner.k_sev_storm_lbl,
|
||||
self._owner.k_maj_storm_lbl,
|
||||
self._owner.k_min_storm_lbl,
|
||||
self._owner.k_active_lbl,
|
||||
self._owner.k_unsettled_lbl,
|
||||
self._owner.k_quiet_lbl,
|
||||
self._owner.k_very_quiet_lbl,
|
||||
self._owner.k_inactive_lbl
|
||||
)
|
||||
|
||||
self._a_storm_labels = SwitchableLabelsIterable(
|
||||
self._owner.a_sev_storm_lbl,
|
||||
self._owner.a_maj_storm_lbl,
|
||||
self._owner.a_min_storm_lbl,
|
||||
self._owner.a_active_lbl,
|
||||
self._owner.a_unsettled_lbl,
|
||||
self._owner.a_quiet_lbl
|
||||
)
|
||||
|
||||
# Used by ThemeManager.
|
||||
self.refreshable_labels = SwitchableLabelsIterable(
|
||||
*self._switchable_r_labels,
|
||||
*self._switchable_s_labels,
|
||||
*self._switchable_g_now_labels,
|
||||
*self._switchable_g_today_labels,
|
||||
*self._k_storm_labels,
|
||||
*self._a_storm_labels,
|
||||
self._owner.expected_noise_lbl
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def _start_update_space_weather(self):
|
||||
"""Start the update of the space weather screen.
|
||||
|
||||
Start the corresponding thread.
|
||||
"""
|
||||
if not self._owner.space_weather_data.is_updating:
|
||||
self._owner.update_now_bar.set_updating()
|
||||
self._owner.space_weather_data.update()
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _update_space_weather(self, status_ok):
|
||||
"""Update the space weather screen after a successful download.
|
||||
|
||||
If the download was not successful throw a warning. In any case remove
|
||||
the downloaded data.
|
||||
"""
|
||||
self._owner.update_now_bar.set_idle()
|
||||
if status_ok:
|
||||
xray_long = safe_cast(self._owner.space_weather_data.xray[-1][7], float)
|
||||
|
||||
def format_text(letter, power):
|
||||
return letter + f"{xray_long * 10**power:.1f}"
|
||||
|
||||
if xray_long < 1e-8 and xray_long != -1.00e+05:
|
||||
self._owner.peak_flux_lbl.setText(format_text("<A", 8))
|
||||
elif xray_long >= 1e-8 and xray_long < 1e-7:
|
||||
self._owner.peak_flux_lbl.setText(format_text("A", 8))
|
||||
elif xray_long >= 1e-7 and xray_long < 1e-6:
|
||||
self._owner.peak_flux_lbl.setText(format_text("B", 7))
|
||||
elif xray_long >= 1e-6 and xray_long < 1e-5:
|
||||
self._owner.peak_flux_lbl.setText(format_text("C", 6))
|
||||
elif xray_long >= 1e-5 and xray_long < 1e-4:
|
||||
self._owner.peak_flux_lbl.setText(format_text("M", 5))
|
||||
elif xray_long >= 1e-4:
|
||||
self._owner.peak_flux_lbl.setText(format_text("X", 4))
|
||||
elif xray_long == -1.00e+05:
|
||||
self._owner.peak_flux_lbl.setText("No Data")
|
||||
|
||||
if xray_long < 1e-5 and xray_long != -1.00e+05:
|
||||
self._switchable_r_labels.switch_on(self._owner.r0_now_lbl)
|
||||
elif xray_long >= 1e-5 and xray_long < 5e-5:
|
||||
self._switchable_r_labels.switch_on(self._owner.r1_now_lbl)
|
||||
elif xray_long >= 5e-5 and xray_long < 1e-4:
|
||||
self._switchable_r_labels.switch_on(self._owner.r2_now_lbl)
|
||||
elif xray_long >= 1e-4 and xray_long < 1e-3:
|
||||
self._switchable_r_labels.switch_on(self._owner.r3_now_lbl)
|
||||
elif xray_long >= 1e-3 and xray_long < 2e-3:
|
||||
self._switchable_r_labels.switch_on(self._owner.r4_now_lbl)
|
||||
elif xray_long >= 2e-3:
|
||||
self._switchable_r_labels.switch_on(self._owner.r5_now_lbl)
|
||||
elif xray_long == -1.00e+05:
|
||||
self._switchable_r_labels.switch_off_all()
|
||||
|
||||
pro10 = safe_cast(self._owner.space_weather_data.prot_el[-1][8], float)
|
||||
if pro10 < 10 and pro10 != -1.00e+05:
|
||||
self._switchable_s_labels.switch_on(self._owner.s0_now_lbl)
|
||||
elif pro10 >= 10 and pro10 < 100:
|
||||
self._switchable_s_labels.switch_on(self._owner.s1_now_lbl)
|
||||
elif pro10 >= 100 and pro10 < 1000:
|
||||
self._switchable_s_labels.switch_on(self._owner.s2_now_lbl)
|
||||
elif pro10 >= 1000 and pro10 < 10000:
|
||||
self._switchable_s_labels.switch_on(self._owner.s3_now_lbl)
|
||||
elif pro10 >= 10000 and pro10 < 100000:
|
||||
self._switchable_s_labels.switch_on(self._owner.s4_now_lbl)
|
||||
elif pro10 >= 100000:
|
||||
self._switchable_s_labels.switch_on(self._owner.s5_now_lbl)
|
||||
elif pro10 == -1.00e+05:
|
||||
self._switchable_s_labels.switch_off_all()
|
||||
|
||||
k_index = safe_cast(
|
||||
self._owner.space_weather_data.ak_index[8][11].replace('.', ''), int
|
||||
)
|
||||
self._owner.k_index_lbl.setText(str(k_index))
|
||||
a_index = safe_cast(
|
||||
self._owner.space_weather_data.ak_index[7][7].replace('.', ''), int
|
||||
)
|
||||
self._owner.a_index_lbl.setText(str(a_index))
|
||||
|
||||
if k_index == 0:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g0_now_lbl)
|
||||
self._k_storm_labels.switch_on(self.k_inactive_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S0 - S1 (<-120 dBm) ")
|
||||
elif k_index == 1:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g0_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_very_quiet_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S0 - S1 (<-120 dBm) ")
|
||||
elif k_index == 2:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g0_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_quiet_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S1 - S2 (-115 dBm) ")
|
||||
elif k_index == 3:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g0_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_unsettled_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S2 - S3 (-110 dBm) ")
|
||||
elif k_index == 4:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g0_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_active_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S3 - S4 (-100 dBm) ")
|
||||
elif k_index == 5:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g1_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_min_storm_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S4 - S6 (-90 dBm) ")
|
||||
elif k_index == 6:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g2_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_maj_storm_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S6 - S9 (-80 dBm) ")
|
||||
elif k_index == 7:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g3_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_sev_storm_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S9 - S20 (>-60 dBm) ")
|
||||
elif k_index == 8:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g4_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_very_sev_storm_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S20 - S30 (>-60 dBm) ")
|
||||
elif k_index == 9:
|
||||
self._switchable_g_now_labels.switch_on(self._owner.g5_now_lbl)
|
||||
self._k_storm_labels.switch_on(self._owner.k_ex_sev_storm_lbl)
|
||||
self._owner.expected_noise_lbl.setText(" S30+ (>>-60 dBm) ")
|
||||
self._owner.expected_noise_lbl.switch_on()
|
||||
|
||||
if a_index >= 0 and a_index < 8:
|
||||
self._a_storm_labels.switch_on(self._owner.a_quiet_lbl)
|
||||
elif a_index >= 8 and a_index < 16:
|
||||
self._a_storm_labels.switch_on(self._owner.a_unsettled_lbl)
|
||||
elif a_index >= 16 and a_index < 30:
|
||||
self._a_storm_labels.switch_on(self._owner.a_active_lbl)
|
||||
elif a_index >= 30 and a_index < 50:
|
||||
self._a_storm_labels.switch_on(self._owner.a_min_storm_lbl)
|
||||
elif a_index >= 50 and a_index < 100:
|
||||
self._a_storm_labels.switch_on(self._owner.a_maj_storm_lbl)
|
||||
elif a_index >= 100 and a_index < 400:
|
||||
self._a_storm_labels.switch_on(self._owner.a_sev_storm_lbl)
|
||||
|
||||
index = self._owner.space_weather_data.geo_storm[6].index("was") + 1
|
||||
k_index_24_hmax = safe_cast(
|
||||
self._owner.space_weather_data.geo_storm[6][index], int
|
||||
)
|
||||
if k_index_24_hmax == 0:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g0_today_lbl)
|
||||
elif k_index_24_hmax == 1:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g0_today_lbl)
|
||||
elif k_index_24_hmax == 2:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g0_today_lbl)
|
||||
elif k_index_24_hmax == 3:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g0_today_lbl)
|
||||
elif k_index_24_hmax == 4:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g0_today_lbl)
|
||||
elif k_index_24_hmax == 5:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g1_today_lbl)
|
||||
elif k_index_24_hmax == 6:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g2_today_lbl)
|
||||
elif k_index_24_hmax == 7:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g3_today_lbl)
|
||||
elif k_index_24_hmax == 8:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g4_today_lbl)
|
||||
elif k_index_24_hmax == 9:
|
||||
self._switchable_g_today_labels.switch_on(self._owner.g5_today_lbl)
|
||||
|
||||
val = safe_cast(
|
||||
self._owner.space_weather_data.ak_index[7][2].replace('.', ''), int
|
||||
)
|
||||
self._owner.sfi_lbl.setText(f"{val}")
|
||||
val = safe_cast(
|
||||
[x[4] for x in self._owner.space_weather_data.sgas
|
||||
if "SSN" in x][0], int
|
||||
)
|
||||
self._owner.sn_lbl.setText(f"{val:d}")
|
||||
|
||||
for label, pixmap in zip(self.space_weather_labels,
|
||||
self._owner.space_weather_data.images):
|
||||
label.pixmap = pixmap
|
||||
label.make_transparent()
|
||||
label.apply_pixmap()
|
||||
elif not self._owner.closing:
|
||||
pop_up(self._owner, title=Messages.BAD_DOWNLOAD,
|
||||
text=Messages.BAD_DOWNLOAD_MSG).show()
|
||||
self._owner.space_weather_data.remove_data()
|
||||
@@ -1,12 +1,10 @@
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
import os
|
||||
import re
|
||||
from PyQt5.QtWidgets import QAction, QActionGroup
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtGui import QPixmap
|
||||
from constants import Constants
|
||||
from switchable_label import SwitchableLabelsIterable
|
||||
from utilities import pop_up
|
||||
|
||||
|
||||
@@ -46,7 +44,7 @@ class _ColorsHandler:
|
||||
|
||||
Can handle strings representing multiple colors."""
|
||||
|
||||
MAX_COLORS = 2
|
||||
_MAX_COLORS = 2
|
||||
|
||||
def __init__(self, line):
|
||||
"""Define the color from the string 'line'.
|
||||
@@ -76,7 +74,7 @@ class _ColorsHandler:
|
||||
return bool(re.match(pattern, col)) and len(col) == 7
|
||||
|
||||
if not self.is_simple_string:
|
||||
if len(self.color_list) <= self.MAX_COLORS:
|
||||
if len(self.color_list) <= self._MAX_COLORS:
|
||||
return all(match_ok(c) for c in self.color_list)
|
||||
else:
|
||||
return False
|
||||
@@ -120,57 +118,16 @@ class ThemeManager:
|
||||
self._theme_path = ""
|
||||
self._current_theme = ""
|
||||
|
||||
self._space_weather_labels = SwitchableLabelsIterable(
|
||||
*list(
|
||||
chain(
|
||||
self._owner.switchable_r_labels,
|
||||
self._owner.switchable_s_labels,
|
||||
self._owner.switchable_g_now_labels,
|
||||
self._owner.switchable_g_today_labels,
|
||||
self._owner.k_storm_labels,
|
||||
self._owner.a_storm_labels,
|
||||
[self._owner.expected_noise_lbl]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"switch_on_colors",
|
||||
ThemeConstants.DEFAULT_ON_COLORS
|
||||
)
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"switch_off_colors", ThemeConstants.DEFAULT_OFF_COLORS
|
||||
)
|
||||
|
||||
self._theme_names = {}
|
||||
|
||||
def _refresh_range_labels(self):
|
||||
"""Refresh the range-labels."""
|
||||
self._owner.set_acf_interval_label()
|
||||
self._owner.set_band_filter_label(
|
||||
self._owner.activate_low_band_filter_btn,
|
||||
self._owner.lower_band_spinbox,
|
||||
self._owner.lower_band_filter_unit,
|
||||
self._owner.lower_band_confidence,
|
||||
self._owner.activate_up_band_filter_btn,
|
||||
self._owner.upper_band_spinbox,
|
||||
self._owner.upper_band_filter_unit,
|
||||
self._owner.upper_band_confidence,
|
||||
self._owner.band_range_lbl
|
||||
)
|
||||
|
||||
self._owner.set_band_filter_label(
|
||||
self._owner.activate_low_freq_filter_btn,
|
||||
self._owner.lower_freq_spinbox,
|
||||
self._owner.lower_freq_filter_unit,
|
||||
self._owner.lower_freq_confidence,
|
||||
self._owner.activate_up_freq_filter_btn,
|
||||
self._owner.upper_freq_spinbox,
|
||||
self._owner.upper_freq_filter_unit,
|
||||
self._owner.upper_freq_confidence,
|
||||
self._owner.freq_range_lbl
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def _apply(self, theme_path):
|
||||
"""Apply the selected theme.
|
||||
@@ -185,12 +142,12 @@ class ThemeManager:
|
||||
item=self._owner.signals_list.currentItem(),
|
||||
previous_item=None
|
||||
)
|
||||
self._refresh_range_labels()
|
||||
self._owner.audio_widget.refresh_btns_colors(
|
||||
self._owner.filters.refresh()
|
||||
self._owner.audio_widget.refresh(
|
||||
self._owner.active_color,
|
||||
self._owner.inactive_color
|
||||
)
|
||||
self._space_weather_labels.refresh()
|
||||
self._owner.spaceweather_screen.refreshable_labels.refresh()
|
||||
else:
|
||||
pop_up(self._owner, title=ThemeConstants.THEME_NOT_FOUND,
|
||||
text=ThemeConstants.MISSING_THEME).show()
|
||||
@@ -297,20 +254,20 @@ class ThemeManager:
|
||||
inactive_color_ok = True
|
||||
if color.quality == Constants.TEXT_COLOR:
|
||||
text_color_ok = True
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"text_color",
|
||||
color.color_str
|
||||
)
|
||||
for color in color_handler.double_color_list:
|
||||
if color.quality == Constants.LABEL_ON_COLOR:
|
||||
switch_on_color_ok = True
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"switch_on_colors",
|
||||
color.color_list
|
||||
)
|
||||
if color.quality == Constants.LABEL_OFF_COLOR:
|
||||
switch_off_color_ok = True
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"switch_off_colors",
|
||||
color.color_list
|
||||
)
|
||||
@@ -320,17 +277,17 @@ class ThemeManager:
|
||||
self._owner.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR
|
||||
|
||||
if not (switch_on_color_ok and switch_off_color_ok):
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"switch_on_colors",
|
||||
ThemeConstants.DEFAULT_ON_COLORS
|
||||
)
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"switch_off_colors",
|
||||
ThemeConstants.DEFAULT_OFF_COLORS
|
||||
)
|
||||
|
||||
if not text_color_ok:
|
||||
self._space_weather_labels.set(
|
||||
self._owner.spaceweather_screen.refreshable_labels.set(
|
||||
"text_color",
|
||||
ThemeConstants.DEFAULT_TEXT_COLOR
|
||||
)
|
||||
|
||||
@@ -23,6 +23,29 @@ def uncheck_and_emit(button):
|
||||
button.clicked.emit()
|
||||
|
||||
|
||||
def show_matching_strings(list_elements, text):
|
||||
"""Show all elements of QListWidget that matches (even partially) a target text.
|
||||
|
||||
Arguments:
|
||||
list_elements -- the QListWidget
|
||||
text -- the target text."""
|
||||
for index in range(list_elements.count()):
|
||||
item = list_elements.item(index)
|
||||
if text.lower() in item.text().lower() or item.isSelected():
|
||||
item.setHidden(False)
|
||||
else:
|
||||
item.setHidden(True)
|
||||
|
||||
|
||||
def get_field_entries(db_entry, separator=Constants.FIELD_SEPARATOR):
|
||||
"""Take a database entry and optionally a separator string.
|
||||
|
||||
Return a list obtained by splitting the signal field with separator."""
|
||||
return [
|
||||
x.strip() for x in db_entry.split(separator)
|
||||
]
|
||||
|
||||
|
||||
def pop_up(cls, title, text,
|
||||
informative_text=None,
|
||||
connection=None,
|
||||
|
||||
Reference in New Issue
Block a user