Implement themes

This commit is contained in:
alessandro90
2019-03-16 10:54:56 +01:00
parent 9cd87a12fb
commit d4c13f09a7
25 changed files with 1298 additions and 740 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ wav_converter.py
*.txt *.txt
icons_imgs icons_imgs
TestData TestData
themes/.current_theme

View File

@@ -3,9 +3,9 @@ import sys
from pydub import AudioSegment from pydub import AudioSegment
from pygame import mixer from pygame import mixer
from PyQt5.QtCore import QTimer, QTimer, pyqtSlot, QObject from PyQt5.QtCore import QTimer, QTimer, pyqtSlot, QObject
import qtawesome as qta
from utilities import Constants from utilities import Constants
import qtawesome as qta
class AudioPlayer(QObject): # Maybe useless inheriting from QObject class AudioPlayer(QObject): # Maybe useless inheriting from QObject
@@ -17,7 +17,7 @@ class AudioPlayer(QObject): # Maybe useless inheriting from QObject
__time_step = 500 # Milliseconds. __time_step = 500 # Milliseconds.
def __init__(self, play, pause, stop, volume, audio_progress): def __init__(self, play, pause, stop, volume, audio_progress, active_color, inactive_color):
super().__init__() super().__init__()
self.__paused = False self.__paused = False
self.__first_call = True self.__first_call = True
@@ -33,18 +33,21 @@ class AudioPlayer(QObject): # Maybe useless inheriting from QObject
self.__pause.clicked.connect(self.__pause_audio) self.__pause.clicked.connect(self.__pause_audio)
self.__stop.clicked.connect(self.__stop_audio) self.__stop.clicked.connect(self.__stop_audio)
self.__volume.valueChanged.connect(self.__set_volume) self.__volume.valueChanged.connect(self.__set_volume)
self.__play.setIcon(qta.icon('fa5.play-circle',
color = "#4facf1",
color_disabled = '#7a7a7a'))
self.__play.setIconSize(self.__play.size()) self.__play.setIconSize(self.__play.size())
self.__pause.setIcon(qta.icon('fa5.pause-circle',
color = "#4facf1",
color_disabled = '#7a7a7a'))
self.__pause.setIconSize(self.__pause.size()) self.__pause.setIconSize(self.__pause.size())
self.__stop.setIcon(qta.icon('fa5.stop-circle',
color = "#4facf1",
color_disabled = '#7a7a7a'))
self.__stop.setIconSize(self.__stop.size()) self.__stop.setIconSize(self.__stop.size())
self.refresh_btns_colors(active_color, inactive_color)
def refresh_btns_colors(self, active_color, inactive_color):
self.__play.setIcon(qta.icon('fa5.play-circle',
color = active_color,
color_disabled = inactive_color))
self.__pause.setIcon(qta.icon('fa5.pause-circle',
color = active_color,
color_disabled = inactive_color))
self.__stop.setIcon(qta.icon('fa5.stop-circle',
color = active_color,
color_disabled = inactive_color))
@pyqtSlot() @pyqtSlot()
def __set_volume(self): def __set_volume(self):

View File

@@ -14,33 +14,7 @@
<string>Download database</string> <string>Download database</string>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QWidget { <string notr="true"/>
background-color: #464646
}
QLabel {
color: #ffffff;
}
QProgressBar {
border: 2px #7a7a7a;
border-radius: 5px;
background-color: #7a7a7a;
}
QProgressBar::chunk {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #1d5eff, stop:0.5 #4177ff, stop:1 #1d5eff);
border-radius: 5px;
}
QMessageBox {
color: #ffffff;
}
QPushButton {
color: #ffffff;
}</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint"> <property name="sizeConstraint">
@@ -73,6 +47,9 @@ Please wait</string>
<property name="value"> <property name="value">
<number>-1</number> <number>-1</number>
</property> </property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -83,13 +60,7 @@ Please wait</string>
</font> </font>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton { <string notr="true"/>
background-color: rgb(52,52,52);
color: #FFFFFF;
border: 1px solid gray;
border-radius: 8px;
}
</string>
</property> </property>
<property name="text"> <property name="text">
<string>Cancel</string> <string>Cancel</string>

202
main.py
View File

@@ -1,5 +1,6 @@
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
@@ -7,12 +8,13 @@ import sys
from pandas import read_csv from pandas import read_csv
from PyQt5.QtWidgets import (QMainWindow, from PyQt5.QtWidgets import (QMainWindow,
QApplication, QApplication,
QAction,
QMessageBox, QMessageBox,
qApp, qApp,
QDesktopWidget, QDesktopWidget,
QListWidgetItem, QListWidgetItem,
QTreeView, QTreeView,
QTreeWidgetItem) QTreeWidgetItem,)
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from PyQt5 import uic from PyQt5 import uic
from PyQt5.QtCore import (QFileInfo, from PyQt5.QtCore import (QFileInfo,
@@ -24,7 +26,12 @@ from audio_player import AudioPlayer
from double_text_button import DoubleTextButton from double_text_button import DoubleTextButton
from download_window import DownloadWindow from download_window import DownloadWindow
from utilities import Constants, reset_apply_remove_btn
from utilities import (Constants,
reset_apply_remove_btn,
throwable_message,
is_valid_html_color,)
qt_creator_file = "main_window.ui" qt_creator_file = "main_window.ui"
Ui_MainWindow, _ = uic.loadUiType(qt_creator_file) Ui_MainWindow, _ = uic.loadUiType(qt_creator_file)
@@ -42,6 +49,8 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.current_signal_name = '' self.current_signal_name = ''
self.signal_names = [] self.signal_names = []
self.total_signals = 0 self.total_signals = 0
self.active_color = Constants.ACTIVE_COLOR
self.inactive_color = Constants.INACTIVE_COLOR
# Manage frequency filters. # Manage frequency filters.
self.frequency_filters_btns = ( self.frequency_filters_btns = (
@@ -301,6 +310,13 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.reset_location_filters_btn.clicked.connect(self.reset_location_filters) self.reset_location_filters_btn.clicked.connect(self.reset_location_filters)
self.locations_list.itemClicked.connect(self.remove_if_unselected_location) self.locations_list.itemClicked.connect(self.remove_if_unselected_location)
# Find available themes.
self.default_images_folder = os.path.join(Constants.THEMES_FOLDER,
Constants.DEFAULT_THEME,
Constants.ICONS_FOLDER)
# self.find_themes()
# self.set_theme()
# ########################################################################################## # ##########################################################################################
self.load_db() self.load_db()
@@ -309,16 +325,15 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.search_bar.textChanged.connect(self.display_signals) self.search_bar.textChanged.connect(self.display_signals)
self.result_list.addItems(self.signal_names) self.result_list.addItems(self.signal_names)
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.result_list.itemDoubleClicked.connect(lambda: self.main_tab.setCurrentWidget(self.signal_properties_tab))
self.signal_properties_tab
)
)
self.display_signals() self.display_signals()
self.audio_widget = AudioPlayer(self.play, self.audio_widget = AudioPlayer(self.play,
self.pause, self.pause,
self.stop, self.stop,
self.volume, self.volume,
self.audio_progress) self.audio_progress,
self.active_color,
self.inactive_color)
BandLabel = namedtuple("BandLabel", ["left", "center", "right"]) BandLabel = namedtuple("BandLabel", ["left", "center", "right"])
self.band_labels = [ self.band_labels = [
@@ -334,8 +349,124 @@ class MyApp(QMainWindow, Ui_MainWindow):
BandLabel(self.shf_left, self.shf, self.shf_right), BandLabel(self.shf_left, self.shf, self.shf_right),
BandLabel(self.ehf_left, self.ehf, self.ehf_right), BandLabel(self.ehf_left, self.ehf, self.ehf_right),
] ]
self.find_themes()
self.set_theme()
self.show() self.show()
def find_themes(self):
themes = []
for theme_folder in os.listdir(Constants.THEMES_FOLDER):
relative_folder = os.path.join(Constants.THEMES_FOLDER, theme_folder)
if os.path.isdir(os.path.abspath(relative_folder)):
relative_folder = os.path.join(Constants.THEMES_FOLDER, theme_folder)
themes.append(relative_folder)
for theme in themes:
theme_name = '&' + ' '.join(
map(lambda s: s.capitalize(),
os.path.basename(theme).split('-')[1].split('_')
)
)
new_theme = QAction(theme_name, self)
self.menu_themes.addAction(new_theme)
@pyqtSlot()
def show_new_theme(theme):
self.change_theme(theme)
self.display_specs(self.result_list.currentItem(), None)
new_theme.triggered.connect(partial(show_new_theme, theme))
@pyqtSlot()
def change_theme(self, theme_path):
try:
with open(os.path.join(
theme_path,
os.path.basename(theme_path).split('-')[1] + Constants.THEME_EXTENSION)
) as stylesheet:
style = stylesheet.read()
self.setStyleSheet(style)
self.download_window.setStyleSheet(style)
except FileNotFoundError:
throwable_message(self, title = "Theme not found",
text = f"Missing theme in {Constants.THEMES_FOLDER} folder.").show()
else:
icons_path = os.path.join(theme_path, Constants.ICONS_FOLDER)
default_icons_path = os.path.join(Constants.THEMES_FOLDER, Constants.DEFAULT_THEME, Constants.ICONS_FOLDER)
if os.path.exists(os.path.join(icons_path, Constants.NOT_SELECTED)) and \
os.path.exists(os.path.join(icons_path, Constants.NOT_AVAILABLE)):
self.default_images_folder = icons_path
else:
self.default_images_folder = default_icons_path
path_to_search_label = os.path.join(icons_path, Constants.SEARCH_LABEL_IMG)
default_search_label = os.path.join(default_icons_path, Constants.SEARCH_LABEL_IMG)
if os.path.exists(path_to_search_label):
self.search_label.setPixmap(QPixmap(path_to_search_label))
self.modulation_search_label.setPixmap(QPixmap(path_to_search_label))
self.location_search_label.setPixmap(QPixmap(path_to_search_label))
else:
self.search_label.setPixmap(QPixmap(default_search_label))
self.modulation_search_label.setPixmap(QPixmap(default_search_label))
self.location_search_label.setPixmap(QPixmap(default_search_label))
self.search_label.setScaledContents(True)
self.modulation_search_label.setScaledContents(True)
self.location_search_label.setScaledContents(True)
path_to_volume_label = os.path.join(icons_path, Constants.VOLUME_LABEL_IMG)
default_volume_label = os.path.join(default_icons_path, Constants.VOLUME_LABEL_IMG)
if os.path.exists(path_to_volume_label):
self.volume_label.setPixmap(QPixmap(path_to_volume_label))
else:
self.volume_label.setPixmap(QPixmap(default_volume_label))
self.volume_label.setScaledContents(True)
path_to_colors = os.path.join(theme_path, Constants.THEME_COLORS)
active_color_ok = False
inactive_color_ok = False
valid_format = False
valid_file = False
if os.path.exists(path_to_colors):
valid_file = True
with open(path_to_colors, "r") as colors_file:
for line in colors_file:
if '=' in line:
valid_format = True
quality, color = line.split("=")
color = color.rstrip()
if quality == "active" and is_valid_html_color(color):
self.active_color = color
active_color_ok = True
if quality == "inactive" and is_valid_html_color(color):
self.inactive_color = color
inactive_color_ok = True
if not all([valid_file, valid_format, active_color_ok, inactive_color_ok]):
self.active_color = Constants.ACTIVE_COLOR
self.inactive_color = Constants.INACTIVE_COLOR
self.audio_widget.refresh_btns_colors(self.active_color, self.inactive_color)
try:
with open(os.path.join(Constants.THEMES_FOLDER,
Constants.CURRENT_THEME), "w") as current_theme:
current_theme.write(theme_path)
except:
pass
def set_theme(self):
current_theme_file = os.path.join(Constants.THEMES_FOLDER, Constants.CURRENT_THEME)
if os.path.exists(current_theme_file):
with open(current_theme_file) as current_theme:
theme = current_theme.read()
if theme != Constants.DEFAULT_THEME:
self.change_theme(theme)
@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():
@@ -440,42 +571,25 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.display_signals() self.display_signals()
def load_db(self): def load_db(self):
names = ["name", names = Constants.DB_NAMES
"inf_freq",
"sup_freq",
"mode",
"inf_band",
"sup_band",
"location",
"url",
"description",
"modulation",
"category_code",
"acf",]
try: try:
self.db = read_csv(os.path.join(Constants.DATA_FOLDER, 'db.csv'), self.db = read_csv(os.path.join(Constants.DATA_FOLDER, Constants.DB_NAME),
sep = '*', sep = '*',
header = None, header = None,
index_col = 0, index_col = 0,
dtype = {'inf_freq': str, dtype = {name : str for name in Constants.DB_STRINGS},
'sup_freq': str,
'mode': str,
'inf_band': str,
'sup_band': str,
'category_code': str,},
names = names,) names = names,)
except FileNotFoundError: except FileNotFoundError:
self.search_bar.setDisabled(True) self.search_bar.setDisabled(True)
box = QMessageBox(self) box = QMessageBox(self)
box.setWindowTitle("No database") box.setWindowTitle(Constants.Messages.NO_DB)
box.setText("No database available.\n" box.setText(Constants.Messages.NO_DB_AVAIL)
"Go to Updates->Update database.")
box.show() box.show()
else: else:
self.signal_names = self.db.index self.signal_names = self.db.index
self.total_signals = len(self.signal_names) self.total_signals = len(self.signal_names)
self.db.fillna(Constants.UNKNOWN, inplace = True) self.db.fillna(Constants.UNKNOWN, inplace = True)
self.db["url_clicked"] = False self.db[Constants.DB_WIKI_CLICKED] = False
self.update_status_tip(self.total_signals) self.update_status_tip(self.total_signals)
@staticmethod @staticmethod
@@ -530,13 +644,13 @@ class MyApp(QMainWindow, Ui_MainWindow):
range_lbl): range_lbl):
activate_low = False activate_low = False
activate_high = False activate_high = False
color = Constants.INACTIVE_COLOR color = self.inactive_color
title = '' title = ''
to_display = '' to_display = ''
if activate_low_btn.isChecked(): if activate_low_btn.isChecked():
to_display += str(lower_spinbox.value()) + ' ' + lower_unit.currentText() to_display += str(lower_spinbox.value()) + ' ' + lower_unit.currentText()
activate_low = True activate_low = True
color = Constants.ACTIVE_COLOR color = self.active_color
if lower_confidence.value() != 0: if lower_confidence.value() != 0:
to_display += ' - ' + str(lower_confidence.value()) + ' %' to_display += ' - ' + str(lower_confidence.value()) + ' %'
else: else:
@@ -545,7 +659,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
if activate_up_btn.isChecked(): if activate_up_btn.isChecked():
to_display += str(upper_spinbox.value()) + ' ' + upper_unit.currentText() to_display += str(upper_spinbox.value()) + ' ' + upper_unit.currentText()
activate_high = True activate_high = True
color = Constants.ACTIVE_COLOR color = self.active_color
if upper_confidence.value() != 0: if upper_confidence.value() != 0:
to_display += ' + ' + str(upper_confidence.value()) + ' %' to_display += ' + ' + str(upper_confidence.value()) + ' %'
else: else:
@@ -591,7 +705,7 @@ class MyApp(QMainWindow, Ui_MainWindow):
def update_status_tip(self, available_signals): def update_status_tip(self, available_signals):
if available_signals < self.total_signals: if available_signals < self.total_signals:
self.statusbar.setStyleSheet(f'color: {Constants.ACTIVE_COLOR}') self.statusbar.setStyleSheet(f'color: {self.active_color}')
else: else:
self.statusbar.setStyleSheet('color: #ffffff') self.statusbar.setStyleSheet('color: #ffffff')
self.statusbar.showMessage(f"{available_signals} out of {self.total_signals} signals displayed.") self.statusbar.showMessage(f"{available_signals} out of {self.total_signals} signals displayed.")
@@ -821,21 +935,21 @@ class MyApp(QMainWindow, Ui_MainWindow):
self.description_text.setText(current_signal.at["description"]) self.description_text.setText(current_signal.at["description"])
for cat, cat_lab in zip(category_code, self.category_labels): for cat, cat_lab in zip(category_code, self.category_labels):
if cat == '0': if cat == '0':
cat_lab.setStyleSheet(f"color: {Constants.INACTIVE_COLOR};") cat_lab.setStyleSheet(f"color: {self.inactive_color};")
elif cat == '1': elif cat == '1':
cat_lab.setStyleSheet(f"color: {Constants.ACTIVE_COLOR};") cat_lab.setStyleSheet(f"color: {self.active_color};")
self.set_band_range(current_signal) self.set_band_range(current_signal)
self.audio_widget.set_audio_player(self.current_signal_name) self.audio_widget.set_audio_player(self.current_signal_name)
else: else:
self.url_button.setEnabled(False) self.url_button.setEnabled(False)
self.url_button.setStyleSheet(f"color: {self.url_button.colors.inactive};") self.url_button.setStyleSheet(f"color: {self.url_button.colors.inactive};")
self.current_signal_name = '' self.current_signal_name = ''
self.name_lab.setText("No signal") self.name_lab.setText("No Signal")
self.name_lab.setAlignment(Qt.AlignHCenter) self.name_lab.setAlignment(Qt.AlignHCenter)
for lab in self.property_labels: for lab in self.property_labels:
lab.setText(Constants.UNKNOWN) lab.setText(Constants.UNKNOWN)
for lab in self.category_labels: for lab in self.category_labels:
lab.setStyleSheet(f"color: {Constants.INACTIVE_COLOR};") lab.setStyleSheet(f"color: {self.inactive_color};")
self.set_band_range() self.set_band_range()
self.audio_widget.set_audio_player() self.audio_widget.set_audio_player()
@@ -882,23 +996,21 @@ class MyApp(QMainWindow, Ui_MainWindow):
return 10**9 return 10**9
def display_spectrogram(self): def display_spectrogram(self):
default_pic = os.path.join(Constants.ICONS_FOLDER, "nosignalselected.png") default_pic = os.path.join(self.default_images_folder, Constants.NOT_SELECTED)
item = self.result_list.currentItem() item = self.result_list.currentItem()
if item: if item:
spectrogram_name = item.text() spectrogram_name = item.text()
path_spectr = os.path.join(Constants.DATA_FOLDER, path_spectr = os.path.join(Constants.DATA_FOLDER,
Constants.SPECTRA_FOLDER, Constants.SPECTRA_FOLDER,
spectrogram_name + ".png") spectrogram_name + Constants.SPECTRA_EXT)
if not QFileInfo(path_spectr).exists(): if not QFileInfo(path_spectr).exists():
path_spectr = os.path.join(Constants.ICONS_FOLDER, path_spectr = os.path.join(self.default_images_folder, Constants.NOT_AVAILABLE)
"spectrumnotavailable.png")
else: else:
path_spectr = default_pic path_spectr = default_pic
self.spectrogram.setPixmap(QPixmap(path_spectr)) self.spectrogram.setPixmap(QPixmap(path_spectr))
@staticmethod def activate_band_category(self, band_label, activate = True):
def activate_band_category(band_label, activate = True): color = self.active_color if activate else self.inactive_color
color = Constants.ACTIVE_COLOR if activate else Constants.INACTIVE_COLOR
for label in band_label: for label in band_label:
label.setStyleSheet(f"color: {color};") label.setStyleSheet(f"color: {color};")

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

239
themes/2-dark/dark.th Normal file
View File

@@ -0,0 +1,239 @@
QMenuBar {
color: rgb(255, 255, 255);
}
QMenuBar::item:selected {
background:#999999;
color: #1d5eff
}
QMenu::item:selected {
background-color: #999999;
color: #1d5eff
}
QMenu {
color: #ffffff;
}
QWidget {
background-color: #464646
}
QLabel {
color: #ffffff;
}
QPushButton {
color: #FFFFFF;
background-color: rgb(52,52,52);
border: 1px solid gray;
border-radius: 5px;
}
QPushButton:!enabled {
color:#9f9f9f;
}
QPushButton:checked {
color: #39eaff;
}
QTabWidget::pane { /* The tab widget frame */
/* border-left: 1px solid gray;*/
border: 0px;
}
QTabWidget::tab-bar {
left: 30px; /* move to the right by 5px */
}
/* Style the tab using the tab sub-control. Note that
it reads QTabBar _not_ QTabWidget */
QTabBar::tab {
background: #7a7a7a;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
min-width: 16ex;
padding: 2px;
color: #FFFFFF
}
QTabBar::tab:selected {
background: #999999;
color: #1d5eff
}
QTabBar::tab:!selected {
margin-top: 3px; /* make non-selected tabs look smaller */
}
QProgressBar {
border: 2px #7a7a7a;
border-radius: 3px;
background-color: #7a7a7a;
}
QProgressBar::chunk {
/*background-color: #1d5eff;*/
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #1d5eff, stop:0.5 #4177ff, stop:1 #1d5eff);
border-radius: 3px;
}
QAbstractScrollArea::corner {
background: none;
border: none;
}
QScrollBar:vertical, QScrollBar:horizontal{
background-color:#343434;
border-radius: 5px;
/*background: none;*/
}
QScrollBar:vertical {
margin-top: 0px;
margin-bottom: 0px;
/*width: 10px;*/
}
QScrollBar:horizontal{
margin-left: 0px;
margin-right: 0px;
/*height: 10px;*/
}
QScrollBar::handle:vertical, QScrollBar::handle:horizontal{
border-radius: 5px;
border-color: none;
border-width: 1px;
background-color: #999999;
}
QScrollBar::add-line:vertical, QScrollBar::add-line:horizontal{
width: 0px;
height: 0px;
}
QScrollBar::sub-line:vertical, QScrollBar::sub-line:horizontal{
width: 0px;
height: 0px;
}
QScrollBar::add-page:vertical{
border-left: 1px solid gray;
background: transparent;
border-radius: 5px;
/*
border: 1px#343434;
background-color: #343434;*/
}
QScrollBar::add-page:horizontal{
border-top: 1px solid gray;
background: transparent;
border-radius: 5px;
/*
border: 1px#343434;
background-color: #343434;*/
}
QScrollBar::sub-page:vertical{
border-left: 1px solid gray;
background: transparent;
border-radius: 5px;
/* border: 1px #343434;
background-color: #343434;*/
}
QScrollBar::sub-page:horizontal{
border-top: 1px solid gray;
background: transparent;
border-radius: 5px;
/* border: 1px #343434;
background-color: #343434;*/
}
QTextEdit{
color: #ffffff;
}
QMessageBox {
color: #ffffff;
}
QToolTip {
color: #000000;
}
QTextBrowser {
background-color: #464646;
color: #ffffff;
border: 0px;
}
QRadioButton {
color: #ffffff;
}
QListWidget {
background-color:rgb(52,52,52);
color: rgb(255, 255, 255);
border: 1px solid gray;
border-radius: 8px;
}
QLineEdit {
background-color: #343434;
color: rgb(255, 255, 255);
border: 1px solid gray;
border-radius: 5px;
}
QComboBox {
background-color: rgb(52,52,52);
color: #ffffff;
border: 1px solid gray;
border-radius: 5px;
}
QComboBox:!enabled {
color: #9f9f9f;
}
QComboBox QAbstractItemView {
border: 1px solid gray;
selection-background-color: #999999;
selection-color: #1d5eff;
color: #ffffff;
}
QSpinBox {
background-color: rgb(52,52,52);
color: #ffffff;
border: 1px solid gray;
border-radius: 5px;
}
QSpinBox:!enabled {
color:#9f9f9f;
}
/* SPECIAL WIDGETS */
QPushButton#url_button {
color: #9f9f9f;
border: 0px;
background-color: #464646;
}
QPushButton#play, QPushButton#pause, QPushButton#stop {
color: #464646;
border: 0px;
background-color: #464646;
}
QLabel#band_range_lbl {
color: #9f9f9f;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,410 @@
/*****************************************************************************
MainWindow
*****************************************************************************/
QWidget:window {
border: 0px solid #2e2f34;
background-color: #2e2f34;
}
/*****************************************************************************
Search bar
*****************************************************************************/
QLineEdit {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #669900;
color: #669900;
}
/*****************************************************************************
Scroll Bars
*****************************************************************************/
QScrollBar:horizontal {
background: transparent; /* Background where slider is not */
height: 10px;
margin: 0;
}
QScrollBar:vertical {
background: transparent; /* Background where slider is not */
width: 10px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: #37474F; /* Slider color */
min-width: 16px;
border-radius: 5px;
}
QScrollBar::handle:vertical {
background: #37474F; /* Slider color */
min-height: 16px;
border-radius: 5px;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal,
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none; /* Removes the dotted background */
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { /* Hides the slider arrows */
border: none;
background: none;
}
/*****************************************************************************
List
*****************************************************************************/
QListWidget {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #80CBC4;
color: #c2cfd6;
}
QListView::item:hover {
color: #669900;
background: transparent;
}
QListView::item:selected {
color: #88cc00;
background: transparent;
}
QListView {
background-color: transparent;
color: #c2cfd6;
outline: 0;
border: 0px solid transparent;
}
/* === QTabBar === */
QTabBar {
background: transparent;
}
QTabWidget::pane {
background: transparent; /* Only at the very bottom of the tabs */
}
QTabBar::tab {
background: transparent;
border: 0px solid transparent;
border-bottom: 2px solid transparent;
color: #546E7A;
padding-left: 10px;
padding-right: 10px;
padding-top: 3px;
padding-bottom: 3px;
}
QTabBar::tab:hover {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #88cc00;
color: #AFBDC4;
}
QTabBar::tab:selected {
background-color: transparent;
border: 0px solid transparent;
border-top: none;
border-bottom: 2px solid #88cc00;
color: #FFFFFF;
}
QStackedWidget {
background: #2e2f34;/* This covers a bunch of things, I was thinking about making it transparent, */
/* but I would have to find all the other elements... but QTabWidget::pane may be it */
}
/* ==================== Dialog ==================== */
QLabel {
background: transparent;
color: #CFD8DC; /* Not sure about this one */
}
QDialog {
background-color: #263238;
color: #546E7A;
outline: 0;
border: 2px solid transparent;
}
/*****************************************************************************
Buttons
*****************************************************************************/
QToolTip {
background-color: #80CBC4;
color: black;
padding: 5px;
border-radius: 0;
opacity: 200;
}
QPushButton {
background-color: transparent;
color: #c2cfd6;
border: 1px solid transparent;
padding: 4px 22px;
}
QPushButton:hover {
border-left: 2px solid #88cc00;
border-right: 2px solid #88cc00;
color: #f0f3f5;
}
QPushButton:pressed {
color: #efffcc;
}
QPushButton:disabled {
color:#546E7A;
}
QPushButton:checked {
color: #88cc00;
}
/*****************************************************************************
Rich Text Box
*****************************************************************************/
QTextBrowser {
background: transparent;
border: 0px solid transparent;
color: #546E7A;
}
/*****************************************************************************
Main Menu (Upper part)
*****************************************************************************/
QTreeView {
background-color: #263238;
}
QMenu {
background-color: #263238; /* File Menu Background color */
color: #546E7A;
}
QMenu::item:selected {
color: #AFBDC4;
}
QMenu::item:pressed {
color: #FFFFFF;
}
QMenu::separator {
height: 1px;
background: transparent; /* Could change this to #546E7A and reduce the margin top and bottom to 1px */
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
/*****************************************************************************
Main Menu (Bar)
*****************************************************************************/
QMenuBar {
background-color: transparent;
color: #546E7A;
}
QMenuBar::item {
background: transparent;
}
QMenuBar::item:disabled {
color: gray;
}
QMenuBar::item:selected {
color: #AFBDC4;
}
QMenuBar::item:pressed {
color: #FFFFFF;
}
QToolBar {
background: transparent;
border: 1px solid transparent;
}
QToolBar:handle {
background: transparent;
border-left: 2px dotted #80CBC4; /* Fix the 4 handle dots so it doesn't look crappy */
color: transparent;
}
QToolBar::separator {
border: 0;
}
/*****************************************************************************
ComboBox
*****************************************************************************/
QComboBox {
border: 0px solid gray;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable, QComboBox::drop-down:editable {
color: #c2cfd6;
selection-color: #80CBC4;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:disabled {
color: #546E7A;
}
/* QComboBox gets the &quot;on&quot; state when the popup is open */
QComboBox:!editable:on,
QComboBox::drop-down:editable:on {
color: #c2cfd6;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on { /* shift the text when the popup opens */
padding-top: 3px;
padding-left: 4px;
}
QComboBox::drop-down {
background-color: transparent;
subcontrol-origin: padding;
subcontrol-position: top right;
width: 20px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
QComboBox::down-arrow:enabled {
image: url("./themes/3-material_design/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/3-material_design/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/3-material_design/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on { /* shift the arrow when popup is open */
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: #2e2f34;
}
/*****************************************************************************
RadioButton
*****************************************************************************/
QRadioButton{
color: #c2cfd6;
}
QRadioButton:disabled{
color: #546E7A;
}
QRadioButton::indicator{
width: 50px;
height: 50px;
}
QRadioButton::indicator::unchecked {
image: url("./themes/3-material_design/icons/off.png");
}
QRadioButton::indicator:unchecked:hover {
image: url("./themes/3-material_design/icons/off_press.png");
}
QRadioButton::indicator:unchecked:pressed {
image: url("./themes/3-material_design/icons/off_press.png");
}
QRadioButton::indicator::checked {
image: url("./themes/3-material_design/icons/on.png");
}
QRadioButton::indicator:checked:hover {
image: url("./themes/3-material_design/icons/on_press.png");
}
QRadioButton::indicator:checked:pressed {
image: url("./themes/3-material_design/icons/on_press.png");
}
/*****************************************************************************
SpinBox
*****************************************************************************/
QSpinBox {
color: #c2cfd6;
border-width: 0px;
background: transparent;
}
QSpinBox:disabled {
color: #546E7A;
border-width: 0px;
background: transparent;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/3-material_design/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/3-material_design/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/3-material_design/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/3-material_design/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right; /* position at bottom right corner */
width: 16px;
image: url("./themes/3-material_design/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/3-material_design/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/3-material_design/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/3-material_design/icons/down-arrow_off.png");
}

View File

@@ -1,37 +1,70 @@
from collections import namedtuple from collections import namedtuple
import hashlib import hashlib
import re
from pandas import read_csv from pandas import read_csv
class _ReadOnlyProperty(object): from PyQt5.QtWidgets import QMessageBox
def __init__(self, value):
self.__value = value # class _ReadOnlyProperty(object):
# def __init__(self, value):
# self.__value = value
def __get__(self, obj, objtype): # def __get__(self, obj, objtype):
return self.__value # return self.__value
def __set__(self, obj, value): # def __set__(self, obj, value):
return NotImplementedError("Cannot change a constant.") # return NotImplementedError("Cannot change a constant.")
# def change_hardcoded_value(self, value): # def __make_read_only(cls):
# self.__value = value # for k, v in cls.__dict__.items():
# if not callable(getattr(cls, k)) and '__' not in k:
# setattr(cls, k, _ReadOnlyProperty(v))
# # def raise_err(self, attr, value):
# # raise NotImplementedError("Cannot add an attribute.")
# # setattr(cls, '__setattr__', raise_err)
# return cls
def __make_read_only(cls): # @__make_read_only
for k, v in cls.__dict__.items(): class Constants(object):
if not callable(getattr(cls, k)) and '__' not in k: class Messages(object):
setattr(cls, k, _ReadOnlyProperty(v)) NO_DB_AVAIL = "No database available.\nGo to Updates->Update database."
# def raise_err(self, attr, value): NO_DB = "No database"
# raise NotImplementedError("Cannot add an attribute.")
# setattr(cls, '__setattr__', raise_err)
return cls
@__make_read_only
class __Constants(object):
DB_LOCATION = "https://aresvalley.com/Storage/Artemis/Database/data.zip" DB_LOCATION = "https://aresvalley.com/Storage/Artemis/Database/data.zip"
REF_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log" REF_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log"
DB_NAME = "db.csv"
DB_NAMES = ("name",
"inf_freq",
"sup_freq",
"mode",
"inf_band",
"sup_band",
"location",
"url",
"description",
"modulation",
"category_code",
"acf",)
DB_WIKI_CLICKED = "url_clicked"
DB_STRINGS = ('inf_freq',
'sup_freq',
'mode',
'inf_band',
'sup_band',
'category_code',)
DATA_FOLDER = "Data" DATA_FOLDER = "Data"
SPECTRA_FOLDER = "Spectra" SPECTRA_FOLDER = "Spectra"
SPECTRA_EXT = ".png"
AUDIO_FOLDER = "Audio" AUDIO_FOLDER = "Audio"
ICONS_FOLDER = "icons_imgs" THEMES_FOLDER = "themes"
THEME_EXTENSION = ".th"
ICONS_FOLDER = "icons"
DEFAULT_THEME = "1-system"
CURRENT_THEME = ".current_theme"
THEME_COLORS = "colors.txt"
NOT_AVAILABLE = "spectrumnotavailable.png"
NOT_SELECTED = "nosignalselected.png"
SEARCH_LABEL_IMG = "search_icon.png"
VOLUME_LABEL_IMG = "volume.png"
__Band = namedtuple("Band", ["lower", "upper"]) __Band = namedtuple("Band", ["lower", "upper"])
__ELF = __Band(0, 30) # Formally it is (3, 30) Hz. __ELF = __Band(0, 30) # Formally it is (3, 30) Hz.
__SLF = __Band(30, 300) __SLF = __Band(30, 300)
@@ -88,7 +121,6 @@ class __Constants(object):
"PSK", "PSK",
"QAM", "QAM",
"TDMA",) "TDMA",)
LOCATIONS = (UNKNOWN, LOCATIONS = (UNKNOWN,
"Australia", "Australia",
"Canada", "Canada",
@@ -121,13 +153,21 @@ class __Constants(object):
"World Wide", "World Wide",
"Worldwide",) "Worldwide",)
Constants = __Constants() # Constants = __Constants()
def reset_apply_remove_btn(button): def reset_apply_remove_btn(button):
if button.isChecked(): if button.isChecked():
button.setChecked(False) button.setChecked(False)
button.clicked.emit() button.clicked.emit()
def throwable_message(cls, title, text, connection = None):
msg = QMessageBox(cls)
msg.setWindowTitle(title)
msg.setText(text)
if connection:
msg.setText(text).finished.connect(connection)
return msg
def checksum_ok(data, what): def checksum_ok(data, what):
code = hashlib.sha256() code = hashlib.sha256()
code.update(data) code.update(data)
@@ -141,4 +181,7 @@ def checksum_ok(data, what):
reference = read_csv(Constants.REF_LOC, delimiter = '*').iat[-1, n] reference = read_csv(Constants.REF_LOC, delimiter = '*').iat[-1, n]
except HTTPError: except HTTPError:
return False return False
return code.hexdigest() == reference return code.hexdigest() == reference
def is_valid_html_color(color):
return bool(re.match("#([a-zA-Z0-9]){6}", color))