Better folder organization

This commit is contained in:
alessandro90
2019-07-05 19:32:05 +02:00
parent 9e92bf0265
commit 41fdf7f660
92 changed files with 4 additions and 2 deletions

2
src/.flake8 Normal file
View File

@@ -0,0 +1,2 @@
[flake8]
ignore = E221, E501, W605, W504

1641
src/artemis.py Normal file

File diff suppressed because it is too large Load Diff

9549
src/artemis.ui Normal file

File diff suppressed because it is too large Load Diff

144
src/audio_player.py Normal file
View File

@@ -0,0 +1,144 @@
import os
from pygame import mixer
from PyQt5.QtCore import QTimer, pyqtSlot, QObject
from constants import Constants
import qtawesome as qta
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.
Everything else is managed internally."""
_TIME_STEP = 500 # Milliseconds.
def __init__(self, play,
pause,
stop,
volume,
audio_progress,
active_color,
inactive_color):
"""Initialize the player."""
super().__init__()
self._paused = False
self._first_call = True
self._play = play
self._pause = pause
self._stop = stop
self._volume = volume
self._audio_progress = audio_progress
self._audio_file = None
self._timer = QTimer()
self._timer.timeout.connect(self._update_bar)
self._play.clicked.connect(self._play_audio)
self._pause.clicked.connect(self._pause_audio)
self._stop.clicked.connect(self._stop_audio)
self._volume.valueChanged.connect(self._set_volume)
self._play.setIconSize(self._play.size())
self._pause.setIconSize(self._pause.size())
self._stop.setIconSize(self._stop.size())
self.refresh_btns_colors(active_color, inactive_color)
def refresh_btns_colors(self, active_color, inactive_color):
"""Repaint the buttons of the widgetd after the theme has changed."""
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()
def _set_volume(self):
"""Set the volume of the audio samples."""
if mixer.get_init():
mixer.music.set_volume(
self._volume.value() / self._volume.maximum()
)
def _reset_audio_widget(self):
"""Reset the widget. Stop all playing samples."""
if mixer.get_init():
if mixer.music.get_busy():
mixer.music.stop()
self._timer.stop()
mixer.quit()
self._audio_progress.reset()
self._enable_buttons(False, False, False)
self._paused = False
@pyqtSlot()
def _update_bar(self):
"""Update the progress bar."""
pos = mixer.music.get_pos()
if pos == -1:
self._timer.stop()
self._audio_progress.reset()
self._enable_buttons(True, False, False)
else:
self._audio_progress.setValue(pos)
def _set_max_progress_bar(self):
"""Set the maximum value of the progress bar."""
self._audio_progress.setMaximum(
mixer.Sound(self._audio_file).get_length() * 1000
)
def set_audio_player(self, fname=""):
"""Set the current audio sample."""
self._first_call = True
self._reset_audio_widget()
full_name = os.path.join(
Constants.DATA_FOLDER,
Constants.AUDIO_FOLDER,
fname + '.ogg'
)
if os.path.exists(full_name):
self._play.setEnabled(True)
self._audio_file = full_name
@pyqtSlot()
def _play_audio(self):
"""Play the audio sample."""
if not self._paused:
if self._first_call:
self._first_call = False
mixer.init(48000, -16, 1, 1024)
mixer.music.load(self._audio_file)
self._set_volume()
self._set_max_progress_bar()
mixer.music.play()
else:
mixer.music.unpause()
self._paused = False
self._timer.start(self._TIME_STEP)
self._enable_buttons(False, True, True)
@pyqtSlot()
def _stop_audio(self):
"""Stop the audio sample."""
mixer.music.stop()
self._audio_progress.reset()
self._timer.stop()
self._enable_buttons(True, False, False)
@pyqtSlot()
def _pause_audio(self):
"""Pause the audio sample."""
mixer.music.pause()
self._timer.stop()
self._paused = True
self._enable_buttons(True, False, False)
def _enable_buttons(self, play_en, pause_en, stop_en):
"""Set the three buttons status."""
self._play.setEnabled(play_en)
self._pause.setEnabled(pause_en)
self._stop.setEnabled(stop_en)

View File

@@ -0,0 +1,35 @@
from PyQt5.QtWidgets import QProgressBar
from PyQt5.QtCore import Qt, pyqtSignal
from constants import Constants
class ClickableProgressBar(QProgressBar):
"""Subclass QProgressBar. Clickable progress bar class."""
clicked = pyqtSignal()
def __init__(self, parent=None):
"""Initialize the instance."""
self._text = ''
super().__init__(parent)
def text(self):
"""Return the text displayed on the bar."""
return self._text
def set_idle(self):
"""Set the bar to a non-downloading status."""
self._text = Constants.CLICK_TO_UPDATE_STR
self.setMaximum(self.minimum() + 1)
def set_updating(self):
"""Set the bar to a downloading status."""
self._text = Constants.UPDATING_STR
self.setMaximum(self.minimum())
def mousePressEvent(self, event):
"""Override QWidget.mousePressEvent. Detect a click on the bar."""
if event.button() == Qt.LeftButton:
self.clicked.emit()
else:
super().mousePressEvent(event)

186
src/constants.py Normal file
View File

@@ -0,0 +1,186 @@
from collections import namedtuple
from enum import Enum, auto
import os.path
class Ftype:
"""Container class to differentiate between frequency and band.
Used in reset_fb_filters.
"""
FREQ = "freq"
BAND = "band"
class GfdType(Enum):
"""Enum class to differentiate the possible GFD search criterias."""
FREQ = auto()
LOC = auto()
class ChecksumWhat(Enum):
"""Enum class to distinguish the object you want to verify the checksum."""
FOLDER = auto()
DB = auto()
class Messages:
"""Container class for messages to be displayed."""
DB_UP_TO_DATE = "Already up to date"
DB_UP_TO_DATE_MSG = "No newer version to download."
DB_NEW_VER = "New version available"
DB_NEW_VER_MSG = "A new version of the database is available for download."
NO_DB_AVAIL = "No database detected."
NO_DB = "No database"
DOWNLOAD_NOW_QUESTION = "Do you want to download it now?"
DOWNLOAD_ANYWAY_QUESTION = "Do you want to download it anyway?"
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."
SLOW_CONN = "Slow internet connection"
SLOW_CONN_MSG = "Your internet connection is unstable or too slow."
class Signal:
"""Container class for the signal property names."""
NAME = "name"
INF_FREQ = "inf_freq"
SUP_FREQ = "sup_freq"
MODE = "mode"
INF_BAND = "inf_band"
SUP_BAND = "sup_band"
LOCATION = "location"
URL = "url"
DESCRIPTION = "description"
MODULATION = "modulation"
CATEGORY_CODE = "category_code"
ACF = "acf"
WIKI_CLICKED = "url_clicked"
class Database:
"""Container class for the database-related constants."""
LINK_LOC = "https://aresvalley.com/Storage/Artemis/Database/data.zip"
LINK_REF = "https://aresvalley.com/Storage/Artemis/Database/data.zip.log"
NAME = "db.csv"
NAMES = (Signal.NAME,
Signal.INF_FREQ,
Signal.SUP_FREQ,
Signal.MODE,
Signal.INF_BAND,
Signal.SUP_BAND,
Signal.LOCATION,
Signal.URL,
Signal.DESCRIPTION,
Signal.MODULATION,
Signal.CATEGORY_CODE,
Signal.ACF)
DELIMITER = "*"
STRINGS = (Signal.INF_FREQ,
Signal.SUP_FREQ,
Signal.MODE,
Signal.INF_BAND,
Signal.SUP_BAND,
Signal.CATEGORY_CODE)
class ForecastColors:
"""Container class for the forecast labels colors."""
WARNING_COLOR = "#F95423"
KP9_COLOR = "#FFCCCB"
KP8_COLOR = "#FFCC9A"
KP7_COLOR = "#FFFECD"
KP6_COLOR = "#CDFFCC"
KP5_COLOR = "#BEE3FE"
_Band = namedtuple("Band", ["lower", "upper"])
class Constants:
"""Container class for several constants of the software."""
CLICK_TO_UPDATE_STR = "Click to update"
SIGIDWIKI = "https://www.sigidwiki.com/wiki/Signal_Identification_Guide"
ADD_SIGNAL_LINK = "https://www.sigidwiki.com/index.php/Special:FormEdit/Signal/?preload=Signal_Identification_Wiki:Signal_form_preload_text"
FORUM_LINK = "https://aresvalley.com/community/"
ARESVALLEY_LINK = "https://aresvalley.com/"
RTL_SDL_LINK = "https://www.rtl-sdr.com/"
UPDATING_STR = "Updating..."
ACF_DOCS = "https://aresvalley.com/documentation/"
FORECAST_PROBABILITIES = "https://services.swpc.noaa.gov/text/sgarf.txt"
SPACE_WEATHER_XRAY = "https://services.swpc.noaa.gov/text/goes-xray-flux-primary.txt"
SPACE_WEATHER_PROT_EL = "https://services.swpc.noaa.gov/text/goes-particle-flux-primary.txt"
SPACE_WEATHER_AK_INDEX = "https://services.swpc.noaa.gov/text/wwv.txt"
SPACE_WEATHER_SGAS = "https://services.swpc.noaa.gov/text/sgas.txt"
SPACE_WEATHER_GEO_STORM = "https://services.swpc.noaa.gov/text/3-day-forecast.txt"
SPACE_WEATHER_INFO = "https://www.swpc.noaa.gov/sites/default/files/images/NOAAscales.pdf"
SPACE_WEATHER_IMGS = ["http://www.mmmonvhf.de/eme/eme.png",
"http://www.mmmonvhf.de/ms/ms.png",
"http://www.mmmonvhf.de/es/es.png",
"http://www.mmmonvhf.de/solar/solar.png",
"http://amunters.home.xs4all.nl/eskip50status.gif",
"http://amunters.home.xs4all.nl/eskip70status.gif",
"http://amunters.home.xs4all.nl/eskipstatus.gif",
"https://amunters.home.xs4all.nl/eskipstatusNA.gif",
"https://amunters.home.xs4all.nl/aurorastatus.gif"]
SEARCH_LABEL_IMG = "search_icon.png"
VOLUME_LABEL_IMG = "volume.png"
DATA_FOLDER = "Data"
SPECTRA_FOLDER = "Spectra"
SPECTRA_EXT = ".png"
AUDIO_FOLDER = "Audio"
ACTIVE = "active"
INACTIVE = "inactive"
LABEL_ON_COLOR = "on"
LABEL_OFF_COLOR = "off"
TEXT_COLOR = "text"
_ELF = _Band(0, 30) # Formally it is (3, 30) Hz.
_SLF = _Band(30, 300)
_ULF = _Band(300, 3000)
_VLF = _Band(3000, 30000)
_LF = _Band(30 * 10**3, 300 * 10**3)
_MF = _Band(300 * 10 ** 3, 3000 * 10**3)
_HF = _Band(3 * 10**6, 30 * 10**6)
_VHF = _Band(30 * 10**6, 300 * 10**6)
_UHF = _Band(300 * 10**6, 3000 * 10**6)
_SHF = _Band(3 * 10**9, 30 * 10**9)
_EHF = _Band(30 * 10**9, 300 * 10**9)
BANDS = (_ELF, _SLF, _ULF, _VLF, _LF, _MF, _HF, _VHF, _UHF, _SHF, _EHF)
MAX_DIGITS = 3
RANGE_SEPARATOR = ' ÷ '
GFD_SITE = "http://qrg.globaltuners.com/"
CONVERSION_FACTORS = {"Hz": 1,
"kHz": 1000,
"MHz": 1000000,
"GHz": 1000000000}
MODES = {"FM": ("NFM", "WFM"),
"AM": (),
"CW": (),
"SK": ("FSK", "PSK", "MSK"),
"SB": ("LSB", "USB", "DSB"),
"Chirp Spread Spectrum": (),
"FHSS-TDM": (),
"RAW": (),
"SC-FDMA": ()}
APPLY = "Apply"
REMOVE = "Remove"
UNKNOWN = "N/A"
EXTRACTING_MSG = "Extracting..."
EXTRACTING_CODE = -1
ZERO_INITIAL_SPEED = -1
ZERO_FINAL_SPEED = -2
NOT_AVAILABLE = "spectrumnotavailable.png"
NOT_SELECTED = "nosignalselected.png"
FIELD_SEPARATOR = ";"
DEFAULT_IMGS_FOLDER = os.path.join(":", "pics", "default_pics")
DEFAULT_NOT_SELECTED = os.path.join(DEFAULT_IMGS_FOLDER, NOT_SELECTED)
DEFAULT_NOT_AVAILABLE = os.path.join(DEFAULT_IMGS_FOLDER, NOT_AVAILABLE)

9
src/default_imgs.qrc Normal file
View File

@@ -0,0 +1,9 @@
<RCC>
<qresource prefix="icon">
<file>default_pics/Artemis3.500px.png</file>
</qresource>
<qresource prefix="pics">
<file>default_pics/nosignalselected.png</file>
<file>default_pics/spectrumnotavailable.png</file>
</qresource>
</RCC>

6034
src/default_imgs_rc.py Normal file

File diff suppressed because it is too large Load Diff

59
src/double_text_button.py Normal file
View File

@@ -0,0 +1,59 @@
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtCore import pyqtSlot
class DoubleTextButton(QPushButton):
"""Subclass QPushButton.
A click will deactivate/activate a series of 'slave' widgets depending
on the 'checked' status of the button."""
def __init__(self, parent=None):
"""Extends QPushButton.__init__."""
super().__init__(parent)
self.clicked.connect(self._manage_click)
def set_texts(self, text_a, text_b):
"""Set the two texts to be displayed."""
self._text_a = text_a
self._text_b = text_b
def set_slave_filters(self, simple_ones=None,
radio_1=None,
ruled_by_radio_1=None,
radio_2=None,
ruled_by_radio_2=None):
"""Set all the 'slave' widgets.
Keyword arguments:
simple_ones -- a list of widgets.
radio_1 -- a radio button.
ruled_by_radio_1 -- a list of widgets whose status depend upon radio_1.
radio_2 -- a radio button.
ruled_by_radio_2 -- a list of widgets whose status depend upon radio_2."""
self._simple_ones = simple_ones
self._ruled_by_radio_1 = ruled_by_radio_1
self._radio_1 = radio_1
self._ruled_by_radio_2 = ruled_by_radio_2
self._radio_2 = radio_2
@pyqtSlot()
def _manage_click(self):
"""Set the status of all the 'slave widgets' based on the status of the instance."""
if self.isChecked():
self.setText(self._text_b)
enable = False
else:
self.setText(self._text_a)
enable = True
for f in self._simple_ones:
f.setEnabled(enable)
radio_btns = self._radio_1, self._radio_2
ruled_widgets = self._ruled_by_radio_1, self._ruled_by_radio_2
for radio_btn, ruled_by in zip(radio_btns, ruled_widgets):
if ruled_by:
for f in ruled_by:
if radio_btn.isChecked():
f.setEnabled(enable)
else:
f.setEnabled(False)

117
src/download_db_window.ui Normal file
View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>160</height>
</rect>
</property>
<property name="windowTitle">
<string>Download database</string>
</property>
<property name="windowIcon">
<iconset resource="default_imgs.qrc">
<normaloff>:/icon/default_pics/Artemis3.500px.png</normaloff>:/icon/default_pics/Artemis3.500px.png</iconset>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Downloading database
Please wait...
</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="status_lbl">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>status</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="speed_lbl">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Speed</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>-1</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_btn">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="default_imgs.qrc"/>
</resources>
<connections/>
</ui>

117
src/download_window.py Normal file
View File

@@ -0,0 +1,117 @@
from PyQt5 import uic
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QWidget
from threads import DownloadThread, ThreadStatus
from utilities import pop_up, resource_path
from constants import Constants, Messages
Ui_Download_window, _ = uic.loadUiType(
resource_path("download_db_window.ui")
)
class DownloadWindow(QWidget, Ui_Download_window):
"""Subclass QWidget and Ui_Download_window. It is the window displayed during the database download."""
complete = pyqtSignal()
closed = pyqtSignal()
def __init__(self):
"""Initialize the window."""
super().__init__()
self.setupUi(self)
self.setWindowFlags(
# Qt.Window |
Qt.CustomizeWindowHint |
Qt.WindowTitleHint |
Qt.WindowCloseButtonHint |
Qt.WindowStaysOnTopHint
)
self._no_internet_msg = pop_up(self, title=Messages.NO_CONNECTION,
text=Messages.NO_CONNECTION_MSG,
connection=self.close)
self._bad_db_download_msg = pop_up(self, title=Messages.BAD_DOWNLOAD,
text=Messages.BAD_DOWNLOAD_MSG,
connection=self.close)
self._slow_conn_msg = pop_up(self, title=Messages.SLOW_CONN,
text=Messages.SLOW_CONN_MSG,
connection=self.close)
self._download_thread = DownloadThread()
self._download_thread.finished.connect(self._wait_close)
self._download_thread.progress.connect(self._display_progress)
self._download_thread.speed_progress.connect(self._display_speed)
self.closed.connect(self._download_thread.set_exit)
self.cancel_btn.clicked.connect(self._terminate_process)
def start_download(self):
"""Start the download thread."""
self._download_thread.start()
def _download_format_str(self, n):
"""Return a well-formatted string with the downloaded MB."""
return f"Downloaded: {n} MB"
@pyqtSlot(float)
def _display_speed(self, speed):
"""Display the download speed."""
ret = "Speed: "
if speed == Constants.ZERO_INITIAL_SPEED:
ret += "Calculating..."
elif speed == 0.0:
ret += "VERY SLOW"
elif speed == Constants.ZERO_FINAL_SPEED:
ret = ""
else:
ret += f"{speed} MB/s"
self.speed_lbl.setText(ret)
@pyqtSlot(int)
def _display_progress(self, progress):
"""Display the downloaded MB."""
if progress != Constants.EXTRACTING_CODE:
self.status_lbl.setText(self._download_format_str(progress))
elif progress == Constants.EXTRACTING_CODE:
self.status_lbl.setText(Constants.EXTRACTING_MSG)
def show(self):
"""Extends QWidget.show. Set downloaded MB and speed to zero."""
self._display_progress(0)
self._display_speed(Constants.ZERO_INITIAL_SPEED)
super().show()
def _stop_thread(self):
"""Ask the download thread to stop."""
if self._download_thread.isRunning():
self.closed.emit()
self._download_thread.wait()
@pyqtSlot()
def _terminate_process(self):
"""Terminate the download thread and close."""
self._stop_thread()
self.close()
@pyqtSlot()
def _wait_close(self):
"""Decide the action based on the download thread status and close."""
if self._download_thread.status is ThreadStatus.OK:
self.complete.emit()
self.close()
elif self._download_thread.status is ThreadStatus.NO_CONNECTION_ERR:
self._no_internet_msg.show()
elif self._download_thread.status is ThreadStatus.BAD_DOWNLOAD_ERR:
self._bad_db_download_msg.show()
elif self._download_thread.status is ThreadStatus.SLOW_CONN_ERR:
self._slow_conn_msg.show()
else:
self.close()
def reject(self):
"""Extends QWidget.reject. Terminate the download thread."""
self._stop_thread()
super().reject()

View File

@@ -0,0 +1,38 @@
from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import Qt
class FixedAspectRatioLabel(QLabel):
"""Subclass QLabel. A resizable label class."""
def __init__(self, parent=None):
"""Initialize the instance. Set the pixmap to None."""
super().__init__(parent)
self.pixmap = None
def set_default_stylesheet(self):
"""Set the initial stylesheet of the label."""
self.setStyleSheet("""border-width: 1px;
border-style: solid;
border-color: black;""")
def make_transparent(self):
"""Make the label transparent.
Remove text and border."""
self.setText('')
self.setStyleSheet("border-width: 0px;")
def apply_pixmap(self):
"""Apply a scaled pixmap without modifying the dimension of the original one."""
if self.pixmap:
self.setPixmap(
self.pixmap.scaled(
self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation
)
)
def rescale(self, size):
"""Rescale the widget and the displayed pixmap to the given size."""
self.resize(size)
self.apply_pixmap()

View File

@@ -0,0 +1,26 @@
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import QSize
class FixedAspectRatioWidget(QWidget):
"""Subclass QWidget. Keep all the internal labels to a fixed aspect ratio."""
SPACE = 10
def __init__(self, parent=None):
"""Initialize the instance."""
super().__init__(parent)
self.labels = []
def resizeEvent(self, event):
"""Override QWidget.resizeEvent. Rescale all the internal widgets."""
h, w = self.height(), self.width()
h_lbl = h / 9 - self.SPACE
w_lbl = 5 * h_lbl
w_pad = w - 10
if w_lbl > w_pad:
w_lbl = w_pad
h_lbl = w_pad / 5
for label in self.labels:
label.rescale(QSize(w_lbl, h_lbl))

158
src/switchable_label.py Normal file
View File

@@ -0,0 +1,158 @@
from PyQt5.QtWidgets import QLabel
from constants import ForecastColors
class _BaseSwitchableLabel(QLabel):
"""Subclass QLabel. Base class for the switchable labels."""
def __init__(self, parent=None):
"""Set is_on to False and level to 0."""
super().__init__(parent)
self.is_on = False
self.level = 0
def switch_on(self):
"""Set is_on to True."""
self.is_on = True
def switch_off(self):
"""Set is_on to False."""
self.is_on = False
class SwitchableLabel(_BaseSwitchableLabel):
"""Subclass _BaseSwitchableLabel."""
def __init__(self, parent=None):
"""Define text and colors attributes."""
super().__init__(parent)
self.switch_on_colors = ()
self.switch_off_colors = ()
self.text_color = ''
def switch_on(self):
"""Extend _BaseSwitchableLabel.switch_on.
Apply the active state colors."""
super().switch_on()
self._apply_colors(*self.switch_on_colors)
def switch_off(self):
"""Extend _BaseSwitchableLabel.switch_off.
Apply the inactive state colors."""
super().switch_off()
self._apply_colors(*self.switch_off_colors)
def _apply_colors(self, start, end):
"""Set text and background color of the label."""
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 SingleColorSwitchableLabel(_BaseSwitchableLabel):
"""Subclass _BaseSwitchableLabel."""
THRESHOLD = 30
def __init__(self, parent=None):
"""Set default active color."""
super().__init__(parent)
self.active_color = ForecastColors.WARNING_COLOR
def switch_on(self):
"""Extend _BaseSwitchableLabel.switch_on.
Apply the active state color if level >= THRESHOLD."""
if self.level >= self.THRESHOLD:
super().switch_on()
self.setStyleSheet(f"color: {self.active_color}")
def switch_off(self):
"""Extend _BaseSwitchableLabel.switch_off.
Apply an empty stylesheet."""
super().switch_off()
self.setStyleSheet("")
class MultiColorSwitchableLabel(_BaseSwitchableLabel):
"""Subclass _BaseSwitchableLabel."""
LEVEL_COLORS = {
9: ForecastColors.KP9_COLOR,
8: ForecastColors.KP8_COLOR,
7: ForecastColors.KP7_COLOR,
6: ForecastColors.KP6_COLOR,
5: ForecastColors.KP5_COLOR
}
MIN_LEVEL = list(LEVEL_COLORS.keys())[-1]
MAX_LEVEL = list(LEVEL_COLORS.keys())[0]
def __init__(self, parent=None):
"""Initialize the instance."""
super().__init__(parent)
def switch_on(self):
"""Extend _BaseSwitchableLabel.switch_on.
Apply the active state color based on LEVEL_COLORS."""
if self.MIN_LEVEL <= self.level <= self.MAX_LEVEL:
super().switch_on()
self.setStyleSheet(
f"""color: {self.LEVEL_COLORS[self.level]};
text-decoration: underline;"""
)
def switch_off(self):
"""Extend _BaseSwitchableLabel.switch_off.
Apply an empty stylesheet."""
super().switch_off()
self.setStyleSheet("")
class SwitchableLabelsIterable:
"""Iterable class of _BaseSwitchableLabel."""
def __init__(self, *labels):
"""Set the labels to iterate through."""
self.labels = labels
def __iter__(self):
"""Define the iterator."""
for lab in self.labels:
yield lab
def switch_on(self, label):
"""Switch on the label 'label'. Switch off all the other labels."""
for lab in self.labels:
if lab is label:
lab.switch_on()
else:
lab.switch_off()
def switch_off_all(self):
"""Switch off all the labels."""
for lab in self.labels:
lab.switch_off()
def set(self, attr, value):
"""Set the attribute 'attr' equal to 'value' for all the labels."""
for lab in self.labels:
setattr(lab, attr, value)
def refresh(self):
"""Refresh the state of all the labels.
Used after the applied theme has changed."""
for lab in self.labels:
if lab.is_on:
lab.switch_on()
else:
lab.switch_off()

631
src/themes/acqua/acqua.qss Normal file
View File

@@ -0,0 +1,631 @@
/*
Aqua Style Sheet for QT Applications
Author: Jaime A. Quiroga P.
Company: GTRONICK
Last updated: 22/01/2019, 07:55.
Available at: https://github.com/GTRONICK/QSS/blob/master/Aqua.qss
*/
/* QMainWindow {
background-color:#ececec;
} */
QWidget{
background-color:#ececec;
}
QTextEdit {
}
QPlainTextEdit {
border-style: solid;
border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
}
QToolButton {
border-style: solid;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
border-right-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(217, 217, 217), stop:1 rgb(227, 227, 227));
border-left-color: qlineargradient(spread:pad, x1:0, y1:0.5, x2:1, y2:0.5, stop:0 rgb(227, 227, 227), stop:1 rgb(217, 217, 217));
border-bottom-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgb(215, 215, 215), stop:1 rgb(222, 222, 222));
border-width: 1px;
border-radius: 5px;
color: rgb(0,0,0);
padding: 2px;
background-color: rgb(255,255,255);
}
QToolButton:hover{
border-style: solid;
border-width: 1px;
border-radius: 5px;
color: rgb(0,0,0);
padding: 2px;
background-color: rgb(255,255,255);
}
QToolButton:pressed{
border-style: solid;
border-width: 1px;
border-radius: 5px;
color: rgb(0,0,0);
padding: 2px;
background-color: rgb(142,142,142);
}
QPushButton{
border-width: 1px;
border-radius: 5px;
color: rgb(0,0,0);
padding: 2px;
border: 1px solid transparent;
background-color: transparent;
}
QPushButton:hover{
border-style: solid;
border-width: 1px;
border-radius: 5px;
color: rgb(0,0,0);
padding: 2px;
border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
}
QPushButton:pressed{
border-style: solid;
border-width: 1px;
border-radius: 5px;
color: rgb(0,0,0);
padding: 2px;
}
QPushButton:checked{
border-style: solid;
border-width: 1px;
border-radius: 5px;
color: #ffffff;
padding: 2px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
}
QPushButton:disabled{
border-style: solid;
border-width: 1px;
border-radius: 5px;
color: #808086;
padding: 2px;
background-color: transparent;
}
QLineEdit {
border-width: 1px; border-radius: 4px;
border-style: solid;
border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
}
QLabel {
color: #000000;
}
QLCDNumber {
color: rgb(0, 113, 255, 255);
}
QProgressBar {
text-align: center;
color: rgb(240, 240, 240);
border-width: 1px;
border-radius: 10px;
border-color: rgb(230, 230, 230);
border-style: solid;
background-color:rgb(207,207,207);
}
QProgressBar::chunk {
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
border-radius: 10px;
}
QMenuBar {
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(207, 209, 207, 255), stop:1 rgba(230, 229, 230, 255));
}
QMenuBar::item {
color: #000000;
spacing: 3px;
padding: 1px 4px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(207, 209, 207, 255), stop:1 rgba(230, 229, 230, 255));
}
QMenuBar::item:selected {
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
color: #FFFFFF;
}
QMenu::item:selected {
border-style: solid;
border-top-color: transparent;
border-right-color: transparent;
border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
border-bottom-color: transparent;
border-left-width: 2px;
color: #000000;
padding-left:15px;
padding-top:4px;
padding-bottom:4px;
padding-right:7px;
}
QMenu::item {
border-style: solid;
border-top-color: transparent;
border-right-color: transparent;
border-left-color: transparent;
border-bottom-color: transparent;
border-bottom-width: 1px;
color: #000000;
padding-left:17px;
padding-top:4px;
padding-bottom:4px;
padding-right:7px;
}
QTabWidget {
color:rgb(0,0,0);
background-color:#000000;
}
QTreeView {
background-color: transparent;
selection-background-color: transparent;
border: 0px;
}
QTreeView::item {
background-color: transparent;
}
QTreeView::item:hover {
border-right: 2px solid #4545e5;
color: rgba(0, 113, 255, 255)
}
QTreeView::item:selected {
color: rgba(0, 113, 255, 255)
}
QTreeView::item:active{
background: transparent;
}
QTreeView::item:disabled{
color: #808086;
}
QTreeView::item:selected:disabled{
color: #808086;
}
QListWidget {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #80CBC4;
}
QListView {
background-color: transparent;
outline: 0;
border: 0px solid transparent;
}
QListView::item:hover {
color: rgba(0, 113, 255, 255);
background: transparent;
}
QListView::item:selected {
color: rgba(0, 113, 255, 255);
background: transparent;
}
QListView::item:disabled {
color: #808086;
background: transparent;
}
QListView::item:disabled:selected {
color: rgba(0, 113, 255, 255);
background: transparent;
}
QTabWidget::pane {
background-color:rgb(226,226,226);
border-style: solid;
border-radius: 6px;
}
QTabBar::tab:first {
border-style: solid;
border-left-width:1px;
border-right-width:0px;
border-top-width:1px;
border-bottom-width:1px;
border-top-color: rgb(209,209,209);
border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
border-bottom-color: rgb(229,229,229);
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
color: #000000;
padding: 3px;
margin-left:0px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(247, 247, 247, 255), stop:1 rgba(255, 255, 255, 255));
}
QTabBar::tab:last {
border-style: solid;
border-width:1px;
border-top-color: rgb(209,209,209);
border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
border-right-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
border-bottom-color: rgb(229,229,229);
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
color: #000000;
padding: 3px;
margin-left:0px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(247, 247, 247, 255), stop:1 rgba(255, 255, 255, 255));
}
QTabBar::tab {
border-style: solid;
border-top-width:1px;
border-bottom-width:1px;
border-left-width:1px;
border-top-color: rgb(209,209,209);
border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
border-bottom-color: rgb(229,229,229);
color: #000000;
padding: 3px;
margin-left:0px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(247, 247, 247, 255), stop:1 rgba(255, 255, 255, 255));
}
QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
border-style: solid;
border-left-width:1px;
border-right-color: transparent;
border-top-color: rgb(209,209,209);
border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
border-bottom-color: rgb(229,229,229);
color: #FFFFFF;
padding: 3px;
margin-left:0px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
}
QTabBar::tab:selected, QTabBar::tab:first:selected, QTabBar::tab:hover {
border-style: solid;
border-left-width:1px;
border-bottom-width:1px;
border-top-width:1px;
border-right-color: transparent;
border-top-color: rgb(209,209,209);
border-left-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(209, 209, 209, 209), stop:1 rgba(229, 229, 229, 229));
border-bottom-color: rgb(229,229,229);
color: #FFFFFF;
padding: 3px;
margin-left:0px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
}
QRadioButton {
color: 000000;
padding: 1px;
}
QRadioButton::indicator:checked {
height: 10px;
width: 10px;
border-style:solid;
border-radius:5px;
border-width: 1px;
border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
color: #a9b7c6;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
}
QRadioButton::indicator:!checked {
height: 10px;
width: 10px;
border-style:solid;
border-radius:5px;
border-width: 1px;
border-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(0, 113, 255, 255), stop:1 rgba(91, 171, 252, 255));
color: #a9b7c6;
background-color: transparent;
}
QStatusBar {
color:#027f7f;
}
QSpinBox {
background-color: transparent;
border-width: 0px;
}
QSpinBox:disabled {
color: #808086;
border-width: 0px;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/acqua/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/acqua/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/acqua/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/acqua/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 16px;
image: url("./themes/acqua/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/acqua/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/acqua/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/acqua/icons/down-arrow_off.png");
}
QComboBox {
border: 0px solid transparent;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable {
selection-background-color: transparent;
selection-color: #FFFFFF;
background-color: transparent;
}
QComboBox:disabled {
color: #808086;
}
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on {
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/acqua/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/acqua/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/acqua/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: #ececec;
}
QScrollArea {
color: #FFFFFF;
background-color:#000000;
}
QSlider::groove:horizontal {
height: 5px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
}
QSlider::groove:vertical {
width: 5px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
}
QSlider::handle:horizontal {
background: rgb(253,253,253);
border-style: solid;
border-width: 1px;
border-color: rgb(207,207,207);
width: 12px;
margin: -5px 0;
border-radius: 7px;
}
QSlider::handle:vertical {
background: rgb(253,253,253);
border-style: solid;
border-width: 1px;
border-color: rgb(207,207,207);
height: 12px;
margin: 0 -5px;
border-radius: 7px;
}
QSlider::add-page:horizontal {
background: rgb(181,181,181);
}
QSlider::add-page:vertical {
background: rgb(181,181,181);
}
QSlider::sub-page:horizontal {
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
}
QSlider::sub-page:vertical {
background-color: qlineargradient(spread:pad, y1:0.5, x1:1, y2:0.5, x2:0, stop:0 rgba(49, 147, 250, 255), stop:1 rgba(34, 142, 255, 255));
}
QScrollBar:horizontal {
max-height: 20px;
border: 1px transparent grey;
margin: 0px 20px 0px 20px;
}
QScrollBar:vertical {
max-width: 20px;
border: 1px transparent grey;
margin: 20px 0px 20px 0px;
}
QScrollBar::handle:horizontal {
background: rgb(253,253,253);
border-style: solid;
border-width: 1px;
border-color: rgb(207,207,207);
border-radius: 7px;
min-width: 25px;
}
QScrollBar::handle:horizontal:hover {
background: rgb(253,253,253);
border-style: solid;
border-width: 1px;
border-color: rgb(147, 200, 200);
border-radius: 7px;
min-width: 25px;
}
QScrollBar::handle:vertical {
background: rgb(253,253,253);
border-style: solid;
border-width: 1px;
border-color: rgb(207,207,207);
border-radius: 7px;
min-height: 25px;
}
QScrollBar::handle:vertical:hover {
background: rgb(253,253,253);
border-style: solid;
border-width: 1px;
border-color: rgb(147, 200, 200);
border-radius: 7px;
min-height: 25px;
}
QScrollBar::add-line:horizontal {
border: 2px transparent grey;
border-top-right-radius: 7px;
border-bottom-right-radius: 7px;
background: rgba(34, 142, 255, 255);
width: 20px;
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::add-line:horizontal:pressed {
border: 2px transparent grey;
border-top-right-radius: 7px;
border-bottom-right-radius: 7px;
background: rgb(181,181,181);
width: 20px;
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::add-line:vertical {
border: 2px transparent grey;
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
background: rgba(34, 142, 255, 255);
height: 20px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::add-line:vertical:pressed {
border: 2px transparent grey;
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
background: rgb(181,181,181);
height: 20px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal {
border: 2px transparent grey;
border-top-left-radius: 7px;
border-bottom-left-radius: 7px;
background: rgba(34, 142, 255, 255);
width: 20px;
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal:pressed {
border: 2px transparent grey;
border-top-left-radius: 7px;
border-bottom-left-radius: 7px;
background: rgb(181,181,181);
width: 20px;
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
border: 2px transparent grey;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
background: rgba(34, 142, 255, 255);
height: 20px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical:pressed {
border: 2px transparent grey;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
background: rgb(181,181,181);
height: 20px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::left-arrow:horizontal {
border: 1px transparent grey;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
width: 6px;
height: 6px;
background: white;
}
QScrollBar::right-arrow:horizontal {
border: 1px transparent grey;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
width: 6px;
height: 6px;
background: white;
}
QScrollBar::up-arrow:vertical {
border: 1px transparent grey;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
width: 6px;
height: 6px;
background: white;
}
QScrollBar::down-arrow:vertical {
border: 1px transparent grey;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
width: 6px;
height: 6px;
background: white;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
background: none;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}

View File

@@ -0,0 +1,5 @@
active=#228eff
inactive=#808086
off=#3a7bd5, #3a6073
on=#00d2ff, #928dab
text=#ffffff

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: 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,5 @@
active= #ff9900
inactive= #616161
on=#fc4a1a, #f7b733
off= #000000, #434343
text=#ffffff

View File

@@ -0,0 +1,413 @@
/*
Dark Console Style Sheet for QT Applications
Author: Jaime A. Quiroga P.
Company: GTRONICK
Last updated: 24/05/2018, 17:12.
Available at: https://github.com/GTRONICK/QSS/blob/master/ConsoleStyle.qss
*/
QWidget {
background-color:rgb(0, 0, 0);
color: rgb(240, 240, 240);
border-color: rgb(58, 58, 58);
}
QPlainTextEdit {
background-color:rgb(0, 0, 0);
color: rgb(200, 200, 200);
selection-background-color: rgb(255, 153, 0);
selection-color: rgb(0, 0, 0);
}
QFrame[frameShape="4"],
QFrame[frameShape="5"]
{
border: none;
background: #FFFFFF;
max-width: 1px;
}
QSlider::sub-page:horizontal {
background-color: #ff9900;
}
QSlider::sub-page:vertical {
background-color: #ff9900;
}
QComboBox {
border: 0px solid transparent;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable {
selection-background-color: transparent;
selection-color: #FFFFFF;
background-color: transparent;
}
QComboBox:disabled {
color: #616161;
}
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
color: #ffffff;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on {
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/console_style/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/console_style/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/console_style/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: #232629;
}
QListWidget {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #ff9900;
color: #ffffff;
}
QListView {
background-color: transparent;
color: #ffffff;
outline: 0;
border: 0px solid transparent;
}
QListView::item:hover {
color: rgb(200, 200, 200);
background: transparent;
}
QListView::item:selected {
color: #ff9900;
background: transparent;
}
QListView::item:disabled {
color: #616161;
background: transparent;
}
QListView::item:disabled:selected {
color: #ff9900;
background: transparent;
}
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: #616161;
min-width: 16px;
border-radius: 5px;
}
QScrollBar::handle:vertical {
background: #616161;
min-height: 16px;
border-radius: 5px;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal,
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
QLineEdit {
border-style: outset;
border-radius: 10px;
border-width: 1px;
border-color: #ff9900;
background-color: transparent;
color: #ffffff;
}
QTreeView {
background-color: transparent;
selection-background-color: transparent;
border: 0px;
}
QTreeView::item {
background-color: transparent;
color: #ffffff;
}
QTreeView::item:hover {
border-right: 2px solid #ff9900;
color: rgb(200, 200, 200);
}
QTreeView::item:selected {
color: #ff9900;
}
QTreeView::item:active{
background: transparent;
}
QTreeView::item:disabled{
color: rgb(200, 200, 200);
}
QTreeView::item:selected:disabled{
color: #ff9900;
}
QTabWidget::pane {
border-top: 1px solid #000000;
}
QSpinBox {
background-color: transparent;
color: #ffffff;
border-width: 0px;
}
QSpinBox:disabled {
color: #616161;
border-width: 0px;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/console_style/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/console_style/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/console_style/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/console_style/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 16px;
image: url("./themes/console_style/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/console_style/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/console_style/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/console_style/icons/down-arrow_off.png");
}
QTabBar::tab {
background-color:rgb(0, 0, 0);
border-style: outset;
border-width: 1px;
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-top-width: 0px;
border-style: solid;
color: rgb(255, 153, 0);
padding: 4px;
}
QTabBar::tab:selected, QTabBar::tab:hover {
color: rgb(255, 255, 255);
background-color:rgb(0, 0, 0);
border-color:rgb(42, 42, 42);
margin-left: 0px;
margin-right: 0px;
border-bottom-right-radius:4px;
border-bottom-left-radius:4px;
}
QTabBar::tab:last:selected {
background-color:rgb(0, 0, 0);
border-color:rgb(42, 42, 42);
margin-left: 0px;
margin-right: 0px;
border-bottom-right-radius:4px;
border-bottom-left-radius:4px;
}
QTabBar::tab:!selected {
margin-bottom: 4px;
border-bottom-right-radius:4px;
border-bottom-left-radius:4px;
}
QPushButton{
/* border-style: outset;
border-width: 2px; */
/* border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-style: solid; */
color: rgb(255, 255, 255);
padding: 6px;
/* background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255)); */
background-color: transparent;
}
QPushButton:hover{
/* border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
border-bottom-color: rgb(115, 115, 115);
border-bottom-width: 1px;
border-style: solid; */
color: rgb(200, 200, 200);
padding: 6px;
/* background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(107, 107, 107, 255), stop:1 rgba(157, 157, 157, 255)); */
background-color: transparent;
}
QPushButton:pressed{
/* border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(62, 62, 62, 255), stop:1 rgba(22, 22, 22, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-style: solid; */
color: rgb(255, 255, 255);
padding: 6px;
/* background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255)); */
background-color: transparent;
}
QPushButton:disabled{
/* border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-style: solid; */
color: #616161;
padding: 6px;
/* background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(57, 57, 57, 255), stop:1 rgba(77, 77, 77, 255)); */
background-color: transparent;
}
QPushButton:checked {
color: #ff9900;
}
QProgressBar {
text-align: center;
color: rgb(255, 255, 255);
background-color: #616161;
border-width: 1px;
border-radius: 10px;
border-color: #616161;
border-style: inset;
}
QProgressBar::chunk {
background-color: #ff9900;
border-radius: 10px;
}
QMenuBar {
background:rgb(0, 0, 0);
color: rgb(255, 153, 0);
}
QMenuBar::item {
spacing: 3px;
padding: 1px 4px;
background: transparent;
}
QMenuBar::item:selected {
background:rgb(115, 115, 115);
}
QMenu {
border-width: 2px;
border-radius: 10px;
border-color: rgb(255, 153, 0);
border-style: outset;
}
QMenu::item {
spacing: 3px;
padding: 3px 15px;
}
QMenu::item:selected {
spacing: 3px;
padding: 3px 15px;
background:rgb(115, 115, 115);
color:rgb(255, 255, 255);
border-width: 1px;
border-radius: 10px;
border-color: rgb(58, 58, 58);
border-style: inset;
}

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: 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,5 @@
active=#4545e5
inactive=#546E7A
off=#283048,#859398
on=#4776e6, #8e54e9
text=#ffffff

470
src/themes/dark/dark.qss Normal file
View File

@@ -0,0 +1,470 @@
/*************************************
Main Window and Splitters
**************************************/
QWidget:window {
background-color: #232629;
}
QSplitter::handle {
background-color: transparent;
}
/*************************************
Main menu (Bar)
**************************************/
QMenuBar {
background-color: transparent;
color: #AFBDC4;
}
QMenuBar::item {
background-color: transparent;
}
QMenuBar::item:disabled {
color: gray;
}
QMenuBar::item:selected {
color: #FFFFFF;
border-bottom: 2px solid #4545e5;
}
QMenuBar::item:pressed {
color: #FFFFFF;
border-bottom: 2px solid #4545e5;
}
QToolBar {
background-color: transparent;
border: 1px solid transparent;
}
QToolBar:handle {
background-color: transparent;
border-left: 2px dotted #80CBC4;
color: transparent;
}
QToolBar::separator {
border: 0;
}
QMenu {
background-color: #263238;
color: #AFBDC4;
}
QMenu::item:selected {
color: #FFFFFF;
}
QMenu::item:pressed {
color: #FFFFFF;
}
QMenu::separator {
background-color: transparent;
height: 1px;
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
/*************************************
TabBar
**************************************/
QTabBar {
background: transparent;
}
QTabWidget::pane {
border: 0px solid transparent;
background: transparent;
}
QTabBar::tab {
background-color: transparent;
border: 0px solid transparent;
border-radius: 10px;
color: #AFBDC4;
padding-left: 10px;
padding-right: 10px;
padding-top: 3px;
padding-bottom: 3px;
}
QTabBar::tab:hover {
background: #4545e5;
color: #FFFFFF;
}
QTabBar::tab:selected {
background: #4545e5;
color: #FFFFFF;
}
QStackedWidget {
background: #232629;
}
QSlider::sub-page:horizontal {
background-color: #4545e5;
}
QSlider::sub-page:vertical {
background-color: #4545e5;
}
/*************************************
Progressbar
**************************************/
QProgressBar
{
border: 2px solid grey;
border-radius: 5px;
text-align: center;
}
QProgressBar::chunk
{
background-color: #88cc00;
width: 2.15px;
margin: 0.5px;
}
/*************************************
Labels and Rich Text boxes
**************************************/
QLabel {
background-color: transparent;
color: #CFD8DC;
}
QDialog {
background-color: transparent;
color: #949a9c;
}
QTextBrowser {
background-color: transparent;
color: #949a9c;
}
/*************************************
Search Bar
**************************************/
QLineEdit {
border: 2px solid #4545e5 ;
border-radius: 10px;
background-color: transparent;
color: #CFD8DC;
}
QLineEdit:hover {
border-width: 1px;
border-radius: 10px;
border-style: solid;
border-color: #4545e5 ;
}
QLineEdit:focus {
border-width: 1px;
border-radius: 10px;
border-style: solid;
border-color: #4545e5 ;
}
/*************************************
Scroll bars
**************************************/
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: #374146;
min-width: 16px;
border-radius: 5px;
}
QScrollBar::handle:vertical {
background: #374146;
min-height: 16px;
border-radius: 5px;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal,
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
/*************************************
List
**************************************/
QListWidget {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #80CBC4;
color: #AFBDC4;
}
QListView {
background-color: transparent;
color: #AFBDC4;
outline: 0;
border: 0px solid transparent;
}
QListView::item:hover {
color: #FFFFFF;
background: transparent;
}
QListView::item:selected {
color: #4545e5;
background: transparent;
}
QListView::item:disabled {
color: #546E7A;
background: transparent;
}
QListView::item:disabled:selected {
color: #88cc00;
background: transparent;
}
/*************************************
Buttons
**************************************/
QPushButton {
background-color: transparent;
color: #AFBDC4;
border: 1px solid transparent;
padding: 4px 22px;
}
QPushButton:hover {
border: 2px dashed #4545e5;
border-radius: 13px;
color: #FFFFFF;
}
QPushButton:pressed {
color: #FFFFFF;
}
QPushButton:disabled {
color:#546E7A;
}
QPushButton:checked {
color: #4545e5;
}
/*************************************
ComboBox
**************************************/
QComboBox {
border: 0px solid transparent;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable {
selection-background-color: transparent;
color: #AFBDC4;
selection-color: #FFFFFF;
background-color: transparent;
}
QComboBox:disabled {
color: #546E7A;
}
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
color: #AFBDC4;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on {
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/dark/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/dark/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/dark/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: #232629;
}
/*************************************
RadioButton
**************************************/
QRadioButton{
color: #AFBDC4;
}
QRadioButton:disabled{
color: #546E7A;
}
QRadioButton::indicator{
width: 50px;
height: 50px;
}
QRadioButton::indicator::unchecked {
image: url("./themes/dark/icons/off.png");
}
QRadioButton::indicator:unchecked:hover {
image: url("./themes/dark/icons/off_press.png");
}
QRadioButton::indicator:unchecked:pressed {
image: url("./themes/dark/icons/off_press.png");
}
QRadioButton::indicator::checked {
image: url("./themes/dark/icons/on.png");
}
QRadioButton::indicator:checked:hover {
image: url("./themes/dark/icons/on_press.png");
}
QRadioButton::indicator:checked:pressed {
image: url("./themes/dark/icons/on_press.png");
}
/*************************************
SpinBox
**************************************/
QSpinBox {
background-color: transparent;
color: #AFBDC4;
border-width: 0px;
}
QSpinBox:disabled {
color: #546E7A;
border-width: 0px;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/dark/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/dark/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/dark/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/dark/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 16px;
image: url("./themes/dark/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/dark/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/dark/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/dark/icons/down-arrow_off.png");
}
/*************************************
TreeViewMenu (Mode)
**************************************/
QTreeView {
background-color: transparent;
selection-background-color: transparent;
border: 0px;
}
QTreeView::item {
background-color: transparent;
color: #AFBDC4;
}
QTreeView::item:hover {
border-right: 2px solid #4545e5;
color: #FFFFFF;
}
QTreeView::item:selected {
color: #4545e5;
}
QTreeView::item:active{
background: transparent;
}
QTreeView::item:disabled{
color: #546E7A;
}
QTreeView::item:selected:disabled{
color: #4545e5;
}

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,5 @@
active= #00ff00
inactive= #9f9f9f
on=#fdfc47, #24fe41
off=#f2f2f2,#eaeaea
text=#000000

View File

@@ -0,0 +1,407 @@
/*
ElegantDark Style Sheet for QT Applications
Author: Jaime A. Quiroga P.
Company: GTRONICK
Last updated: 17/04/2018
Available at: https://github.com/GTRONICK/QSS/blob/master/ElegantDark.qss
*/
QMainWindow {
background-color:rgb(82, 82, 82);
}
QWidget{
background-color: rgb(82, 82, 82)
}
QTextEdit {
background-color:rgb(42, 42, 42);
color: rgb(0, 255, 0);
}
QSplitter::handle {
background-color: transparent;
}
QSlider::sub-page:horizontal {
background-color: #00ff00;
}
QSlider::sub-page:vertical {
background-color: #00ff00;
}
QTreeView {
background-color: transparent;
selection-background-color: transparent;
border: 0px;
color: #AFBDC4;
}
QTreeView::item {
background-color: transparent;
}
QTreeView::item:hover {
color: #FFFFFF;
}
QTreeView::item:selected {
color: #00ff00;
}
QTreeView::item:active{
background: transparent;
}
QTreeView::item:disabled{
color: #000000;
}
QTreeView::item:selected:disabled{
color: #00ff00;
}
QSpinBox {
background-color: transparent;
color: #AFBDC4;
border-width: 0px;
}
QSpinBox:disabled {
color: #000000;
border-width: 0px;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/elegant_dark/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/elegant_dark/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/elegant_dark/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/elegant_dark/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 16px;
image: url("./themes/elegant_dark/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/elegant_dark/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/elegant_dark/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/elegant_dark/icons/down-arrow_off.png");
}
QPushButton{
border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-style: solid;
color: rgb(255, 255, 255);
padding: 2px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
}
QPushButton:hover{
border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(180, 180, 180, 255), stop:1 rgba(110, 110, 110, 255));
border-bottom-color: rgb(115, 115, 115);
border-bottom-width: 1px;
border-style: solid;
color: rgb(255, 255, 255);
padding: 2px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(107, 107, 107, 255), stop:1 rgba(157, 157, 157, 255));
}
QPushButton:pressed{
border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(62, 62, 62, 255), stop:1 rgba(22, 22, 22, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-style: solid;
color: rgb(255, 255, 255);
padding: 2px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
}
QPushButton:disabled{
border-style: outset;
border-width: 2px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
border-style: solid;
color: rgb(0, 0, 0);
padding: 2px;
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(57, 57, 57, 255), stop:1 rgba(77, 77, 77, 255));
}
QPushButton:checked {
color: #00ff00;
}
QLineEdit{
border-width: 1px; border-radius: 4px;
border-color: rgb(58, 58, 58);
border-style: inset;
padding: 0 8px;
color: rgb(255, 255, 255);
background:rgb(100, 100, 100);
selection-background-color: rgb(187, 187, 187);
selection-color: rgb(60, 63, 65);
}
QLabel{
color:rgb(255,255,255);
}
QRadioButton{
color: #FFFFFF;
}
QComboBox {
border: 0px solid transparent;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable {
selection-background-color: transparent;
color: #AFBDC4;
selection-color: #FFFFFF;
background-color: transparent;
}
QComboBox:disabled {
color: #000000;
}
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
color: #AFBDC4;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on {
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/elegant_dark/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/elegant_dark/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/elegant_dark/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: rgb(100,100,100);
}
QProgressBar {
text-align: center;
color: rgb(240, 240, 240);
border-width: 1px;
border-radius: 10px;
border-color: rgb(58, 58, 58);
border-style: inset;
background-color:rgb(77,77,77);
}
QProgressBar::chunk {
background-color: #00ff00;
border-radius: 5px;
}
QMenuBar {
background:rgb(82, 82, 82);
}
QMenuBar::item {
color:rgb(223,219,210);
spacing: 3px;
padding: 1px 4px;
background: transparent;
}
QMenuBar::item:selected {
background:rgb(115, 115, 115);
}
QMenu::item:selected {
color:rgb(255,255,255);
border-width:2px;
border-style:solid;
padding-left:18px;
padding-right:8px;
padding-top:2px;
padding-bottom:3px;
background:qlineargradient(spread:pad, x1:0.5, y1:0.7, x2:0.5, y2:0.3, stop:0 rgba(87, 97, 106, 255), stop:1 rgba(93, 103, 113, 255));
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(62, 62, 62, 255));
border-bottom-color: rgb(58, 58, 58);
border-bottom-width: 1px;
}
QMenu::item {
color:rgb(223,219,210);
background-color:rgb(78,78,78);
padding-left:20px;
padding-top:4px;
padding-bottom:4px;
padding-right:10px;
}
QMenu{
background-color:rgb(78,78,78);
}
QTabWidget {
color:rgb(0,0,0);
background-color:rgb(247,246,246);
}
QTabWidget::pane {
border-color: rgb(77,77,77);
background-color:rgb(101,101,101);
border-style: solid;
border-width: 1px;
border-radius: 6px;
}
QTabBar::tab {
padding:2px;
color:rgb(250,250,250);
background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(77, 77, 77, 255), stop:1 rgba(97, 97, 97, 255));
border-style: solid;
border-width: 2px;
border-top-right-radius:4px;
border-top-left-radius:4px;
border-top-color: qlineargradient(spread:pad, x1:0.5, y1:0.6, x2:0.5, y2:0.4, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(95, 92, 93, 255));
border-right-color: qlineargradient(spread:pad, x1:0.4, y1:0.5, x2:0.6, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(95, 92, 93, 255));
border-left-color: qlineargradient(spread:pad, x1:0.6, y1:0.5, x2:0.4, y2:0.5, stop:0 rgba(115, 115, 115, 255), stop:1 rgba(95, 92, 93, 255));
border-bottom-color: rgb(101,101,101);
}
QTabBar::tab:selected, QTabBar::tab:last:selected, QTabBar::tab:hover {
background-color:rgb(101,101,101);
margin-left: 0px;
margin-right: 1px;
}
QTabBar::tab:!selected {
margin-top: 1px;
margin-right: 1px;
}
QStatusBar {
color:rgb(240,240,240);
}
QTextBrowser {
background-color: transparent;
}
QListWidget {
background-color: transparent;
border: 0px solid transparent;
}
QListView {
background-color: transparent;
color: #AFBDC4;
outline: 0;
border: 0px solid transparent;
}
QListView::item:hover {
color: #FFFFFF;
background: transparent;
}
QListView::item:selected {
color: #00ff00;
background: transparent;
}
QListView::item:disabled {
color: #9f9f9f;
background: transparent;
}
QListView::item:disabled:selected {
color: #00ff00;
background: transparent;
}
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: rgb(101,101,101);
min-width: 16px;
border-radius: 5px;
}
QScrollBar::handle:vertical {
background: rgb(101,101,101);
min-height: 16px;
border-radius: 5px;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal,
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}

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: 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,4 @@
active=#88cc00
inactive=#546E7A
off=#304352,#d7d2cc
on=#3ca55c,#b5ac49

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,489 @@
/* Palette
Background: #29353B
Sec. Menu bkg: #263238
Label: #AFBDC4
Label Selected/hover: #FFFFFF
Label Pressed: #FFFFFF
Selection: #88cc00
ScrollBars: #374146
Signal detail Labels: #CFD8DC
Signal detail Dialogs: #949a9c
Disabled: #546E7A
/*************************************
Main Window and Splitters
**************************************/
QWidget:window {
background-color: #29353B;
}
QSplitter::handle {
background-color: transparent;
}
QSlider::sub-page:horizontal {
background-color: #88cc00;
}
QSlider::sub-page:vertical {
background-color: #88cc00;
}
/*************************************
Main menu (Bar)
**************************************/
QMenuBar {
background-color: transparent;
color: #AFBDC4;
}
QMenuBar::item {
background-color: transparent;
}
QMenuBar::item:disabled {
color: gray;
}
QMenuBar::item:selected {
color: #FFFFFF;
border-bottom: 2px solid #88cc00;
}
QMenuBar::item:pressed {
color: #FFFFFF;
border-bottom: 2px solid #88cc00;
}
QToolBar {
background-color: transparent;
border: 1px solid transparent;
}
QToolBar:handle {
background-color: transparent;
border-left: 2px dotted #80CBC4;
color: transparent;
}
QToolBar::separator {
border: 0;
}
QMenu {
background-color: #263238;
color: #AFBDC4;
}
QMenu::item:selected {
color: #FFFFFF;
}
QMenu::item:pressed {
color: #FFFFFF;
}
QMenu::separator {
background-color: transparent;
height: 1px;
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
/*************************************
TabBar
**************************************/
QTabBar {
background: transparent;
}
QTabWidget::pane {
background: transparent;
}
QTabBar::tab {
background: transparent;
border: 0px solid transparent;
border-bottom: 2px solid transparent;
color: #AFBDC4;
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: #FFFFFF;
}
QTabBar::tab:selected {
background-color: transparent;
border: 0px solid transparent;
border-top: none;
border-bottom: 2px solid #88cc00;
color: #FFFFFF;
}
QStackedWidget {
background: #29353B;
}
/*************************************
Progressbar
**************************************/
QProgressBar
{
border: 2px solid grey;
border-radius: 5px;
text-align: center;
}
QProgressBar::chunk
{
background-color: #88cc00;
width: 2.15px;
margin: 0.5px;
}
/*************************************
Labels and Rich Text boxes
**************************************/
QLabel {
background-color: transparent;
color: #CFD8DC;
}
QDialog {
background-color: transparent;
color: #949a9c;
}
QTextBrowser {
background-color: transparent;
color: #949a9c;
}
/*************************************
Search Bar
**************************************/
QLineEdit {
background-color: transparent;
selection-background-color: #669900;
color: #669900;
border-width: 1px;
border-style: solid;
border-color: transparent transparent #669900 transparent;
}
QLineEdit:hover {
border-width: 2px;
border-color: transparent transparent #88cc00 transparent;
}
QLineEdit:focus {
border-width: 2px;
border-color: transparent transparent #88cc00 transparent;
}
/*************************************
Scroll bars
**************************************/
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: #374146;
min-width: 16px;
border-radius: 5px;
}
QScrollBar::handle:vertical {
background: #374146;
min-height: 16px;
border-radius: 5px;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal,
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
/*************************************
List
**************************************/
QListWidget {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #80CBC4;
color: #AFBDC4;
}
QListView {
background-color: transparent;
color: #AFBDC4;
outline: 0;
border: 0px solid transparent;
}
QListView::item:hover {
color: #FFFFFF;
background: transparent;
}
QListView::item:selected {
color: #88cc00;
background: transparent;
}
QListView::item:disabled {
color: #546E7A;
background: transparent;
}
QListView::item:disabled:selected {
color: #88cc00;
background: transparent;
}
/*************************************
Buttons
**************************************/
QPushButton {
background-color: transparent;
color: #AFBDC4;
border: 1px solid transparent;
padding: 4px 22px;
}
QPushButton:hover {
border-left: 2px solid #88cc00;
border-right: 2px solid #88cc00;
color: #FFFFFF;
}
QPushButton:pressed {
color: #FFFFFF;
}
QPushButton:disabled {
color:#546E7A;
}
QPushButton:checked {
color: #88cc00;
}
/*************************************
ComboBox
**************************************/
QComboBox {
border: 0px solid transparent;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable {
selection-background-color: transparent;
color: #AFBDC4;
selection-color: #FFFFFF;
background-color: transparent;
}
QComboBox:disabled {
color: #546E7A;
}
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
color: #AFBDC4;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on {
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/material_design_dark/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/material_design_dark/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/material_design_dark/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: #29353B;
}
/*************************************
RadioButton
**************************************/
QRadioButton{
color: #AFBDC4;
}
QRadioButton:disabled{
color: #546E7A;
}
QRadioButton::indicator{
width: 50px;
height: 50px;
}
QRadioButton::indicator::unchecked {
image: url("./themes/material_design_dark/icons/off.png");
}
QRadioButton::indicator:unchecked:hover {
image: url("./themes/material_design_dark/icons/off_press.png");
}
QRadioButton::indicator:unchecked:pressed {
image: url("./themes/material_design_dark/icons/off_press.png");
}
QRadioButton::indicator::checked {
image: url("./themes/material_design_dark/icons/on.png");
}
QRadioButton::indicator:checked:hover {
image: url("./themes/material_design_dark/icons/on_press.png");
}
QRadioButton::indicator:checked:pressed {
image: url("./themes/material_design_dark/icons/on_press.png");
}
/*************************************
SpinBox
**************************************/
QSpinBox {
background-color: transparent;
color: #AFBDC4;
border-width: 0px;
}
QSpinBox:disabled {
color: #546E7A;
border-width: 0px;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/material_design_dark/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/material_design_dark/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/material_design_dark/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/material_design_dark/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 16px;
image: url("./themes/material_design_dark/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/material_design_dark/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/material_design_dark/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/material_design_dark/icons/down-arrow_off.png");
}
/*************************************
TreeViewMenu (Mode)
**************************************/
QTreeView {
background-color: transparent;
selection-background-color: transparent;
border: 0px;
}
QTreeView::item {
background-color: transparent;
color: #AFBDC4;
}
QTreeView::item:hover {
border-right: 2px solid #88cc00;
color: #FFFFFF;
}
QTreeView::item:selected {
color: #88cc00;
}
QTreeView::item:active{
background: transparent;
}
QTreeView::item:disabled{
color: #546E7A;
}
QTreeView::item:selected:disabled{
color: #88cc00;
}

View File

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

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,489 @@
/* Palette
Background: #F5F5F5
Sec. Menu bkg: #DCDCDC
Label: #29353B
Label Selected/hover: #000000
Label Pressed: #000000
Selection: #6ECE12
ScrollBars: #DCDCDC
Labels: #29353B
Dialogs: #29353B
Disabled: #b3b3cc
/*************************************
Main Window and Splitters
**************************************/
QWidget:window {
background-color: #F5F5F5;
}
QSplitter::handle {
background-color: transparent;
}
QSlider::sub-page:horizontal {
background-color: #6ECE12;
}
QSlider::sub-page:vertical {
background-color: #6ECE12;
}
/*************************************
Main menu (Bar)
**************************************/
QMenuBar {
background-color: transparent;
color: #29353B;
}
QMenuBar::item {
background-color: transparent;
}
QMenuBar::item:disabled {
color: gray;
}
QMenuBar::item:selected {
color: #000000;
border-bottom: 2px solid #6ECE12;
}
QMenuBar::item:pressed {
color: #000000;
border-bottom: 2px solid #6ECE12;
}
QToolBar {
background-color: transparent;
border: 1px solid transparent;
}
QToolBar:handle {
background-color: transparent;
border-left: 2px dotted #80CBC4;
color: transparent;
}
QToolBar::separator {
border: 0;
}
QMenu {
background-color: #DCDCDC;
color: #29353B;
}
QMenu::item:selected {
color: #000000;
}
QMenu::item:pressed {
color: #000000;
}
QMenu::separator {
background-color: transparent;
height: 1px;
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
margin-bottom: 5px;
}
/*************************************
TabBar
**************************************/
QTabBar {
background: transparent;
}
QTabWidget::pane {
background: transparent;
}
QTabBar::tab {
background: transparent;
border: 0px solid transparent;
border-bottom: 2px solid transparent;
color: #29353B;
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 #6ECE12;
color: #000000;
}
QTabBar::tab:selected {
background-color: transparent;
border: 0px solid transparent;
border-top: none;
border-bottom: 2px solid #6ECE12;
color: #000000;
}
QStackedWidget {
background: #F5F5F5;
}
/*************************************
Progressbar
**************************************/
QProgressBar
{
border: 2px solid grey;
border-radius: 5px;
text-align: center;
}
QProgressBar::chunk
{
background-color: #6ECE12;
width: 2.15px;
margin: 0.5px;
}
/*************************************
Labels and Rich Text boxes
**************************************/
QLabel {
background-color: transparent;
color: #29353B;
}
QDialog {
background-color: transparent;
color: #29353B;
}
QTextBrowser {
background-color: transparent;
color: #29353B;
}
/*************************************
Search Bar
**************************************/
QLineEdit {
background-color: transparent;
selection-background-color: #669900;
color: #669900;
border-width: 1px;
border-style: solid;
border-color: transparent transparent #669900 transparent;
}
QLineEdit:hover {
border-width: 2px;
border-color: transparent transparent #6ECE12 transparent;
}
QLineEdit:focus {
border-width: 2px;
border-color: transparent transparent #6ECE12 transparent;
}
/*************************************
Scroll bars
**************************************/
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: #DCDCDC;
min-width: 16px;
border-radius: 5px;
}
QScrollBar::handle:vertical {
background: #DCDCDC;
min-height: 16px;
border-radius: 5px;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal,
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
border: none;
background: none;
}
/*************************************
List
**************************************/
QListWidget {
background-color: transparent;
border: 0px solid transparent;
border-bottom: 2px solid #80CBC4;
color: #29353B;
}
QListView {
background-color: transparent;
color: #29353B;
outline: 0;
border: 0px solid transparent;
}
QListView::item:hover {
color: #000000;
background: transparent;
}
QListView::item:selected {
color: #6ECE12;
background: transparent;
}
QListView::item:disabled {
color: #b3b3cc;
background: transparent;
}
QListView::item:disabled:selected {
color: #6ECE12;
background: transparent;
}
/*************************************
Buttons
**************************************/
QPushButton {
background-color: transparent;
color: #29353B;
border: 1px solid transparent;
padding: 4px 22px;
}
QPushButton:hover {
border-left: 2px solid #6ECE12;
border-right: 2px solid #6ECE12;
color: #000000;
}
QPushButton:pressed {
color: #000000;
}
QPushButton:disabled {
color:#b3b3cc;
}
QPushButton:checked {
color: #6ECE12;
}
/*************************************
ComboBox
**************************************/
QComboBox {
border: 0px solid transparent;
border-radius: 2px;
padding: 1px 6px 1px 6px;
min-width: 2em;
}
QComboBox:!editable {
selection-background-color: transparent;
color: #29353B;
selection-color: #000000;
background-color: transparent;
}
QComboBox:disabled {
color: #b3b3cc;
}
QComboBox:!editable:on, QComboBox::drop-down:editable:on {
color: #29353B;
background-color: transparent;
selection-background-color: transparent;
}
QComboBox:on {
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/material_design_light/icons/down-arrow.png");
}
QComboBox::down-arrow:disabled {
image: url("./themes/material_design_light/icons/down-arrow_off.png");
}
QComboBox::down-arrow:hover {
image: url("./themes/material_design_light/icons/down-arrow_hover.png");
}
QComboBox::down-arrow:on {
top: 1px;
left: 1px;
}
QComboBox QAbstractItemView {
background-color: #F5F5F5;
}
/*************************************
RadioButton
**************************************/
QRadioButton{
color: #29353B;
}
QRadioButton:disabled{
color: #b3b3cc;
}
QRadioButton::indicator{
width: 50px;
height: 50px;
}
QRadioButton::indicator::unchecked {
image: url("./themes/material_design_light/icons/off.png");
}
QRadioButton::indicator:unchecked:hover {
image: url("./themes/material_design_light/icons/off_press.png");
}
QRadioButton::indicator:unchecked:pressed {
image: url("./themes/material_design_light/icons/off_press.png");
}
QRadioButton::indicator::checked {
image: url("./themes/material_design_light/icons/on.png");
}
QRadioButton::indicator:checked:hover {
image: url("./themes/material_design_light/icons/on_press.png");
}
QRadioButton::indicator:checked:pressed {
image: url("./themes/material_design_light/icons/on_press.png");
}
/*************************************
SpinBox
**************************************/
QSpinBox {
background-color: transparent;
color: #29353B;
border-width: 0px;
}
QSpinBox:disabled {
color: #b3b3cc;
border-width: 0px;
}
QSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width: 16px;
image: url("./themes/material_design_light/icons/up-arrow.png");
border-width: 0px;
}
QSpinBox::up-button:hover {
image: url("./themes/material_design_light/icons/up-arrow_hover.png");
}
QSpinBox::up-button:pressed {
image: url("./themes/material_design_light/icons/up-arrow.png");
}
QSpinBox::up-button:disabled {
image: url("./themes/material_design_light/icons/up-arrow_off.png");
}
QSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 16px;
image: url("./themes/material_design_light/icons/down-arrow.png");
border-width: 0px;
border-top-width: 0;
}
QSpinBox::down-button:hover {
image: url("./themes/material_design_light/icons/down-arrow_hover.png");
}
QSpinBox::down-button:pressed {
image: url("./themes/material_design_light/icons/down-arrow.png");
}
QSpinBox::down-button:disabled {
image: url("./themes/material_design_light/icons/down-arrow_off.png");
}
/*************************************
TreeViewMenu (Mode)
**************************************/
QTreeView {
background-color: transparent;
selection-background-color: transparent;
border: 0px;
}
QTreeView::item {
background-color: transparent;
color: #29353B;
}
QTreeView::item:hover {
border-right: 2px solid #6ECE12;
color: #000000;
}
QTreeView::item:selected {
color: #6ECE12;
}
QTreeView::item:active{
background: transparent;
}
QTreeView::item:disabled{
color: #b3b3cc;
}
QTreeView::item:selected:disabled{
color: #6ECE12;
}

368
src/themesmanager.py Normal file
View File

@@ -0,0 +1,368 @@
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
class ThemeConstants:
"""Container class for all the theme-related constants."""
FOLDER = "themes"
EXTENSION = ".qss"
ICONS_FOLDER = "icons"
DEFAULT = "dark"
CURRENT = ".current_theme"
COLORS = "colors.txt"
COLOR_SEPARATOR = "="
DEFAULT_ACTIVE_COLOR = "#000000"
DEFAULT_INACTIVE_COLOR = "#9f9f9f"
DEFAULT_OFF_COLORS = "#000000", "#434343"
DEFAULT_ON_COLORS = "#4b79a1", "#283e51"
DEFAULT_TEXT_COLOR = "#ffffff"
THEME_NOT_FOUND = "Theme not found"
MISSING_THEME = "Missing theme in '" + FOLDER + "' folder."
MISSING_THEME_FOLDER = "'" + FOLDER + "'" + " folder not found.\nOnly the basic theme is available."
THEME_FOLDER_NOT_FOUND = "'" + FOLDER + "'" + " folder not found"
DEFAULT_ICONS_PATH = os.path.join(FOLDER, DEFAULT, ICONS_FOLDER)
DEFAULT_SEARCH_LABEL_PATH = os.path.join(DEFAULT_ICONS_PATH, Constants.SEARCH_LABEL_IMG)
DEFAULT_VOLUME_LABEL_PATH = os.path.join(DEFAULT_ICONS_PATH, Constants.VOLUME_LABEL_IMG)
CURRENT_THEME_FILE = os.path.join(FOLDER, CURRENT)
DEFAULT_THEME_PATH = os.path.join(FOLDER, DEFAULT)
class _ColorsHandler:
"""Manage the theme's secondary colors.
Contains a _Color inner class."""
class _Color:
"""Characterize a color from a string.
Can handle strings representing multiple colors."""
MAX_COLORS = 2
def __init__(self, line):
"""Define the color from the string 'line'.
All relevant features are defined:
- if the format is valid;
- if 'line' represent a single color or a list of colors.
- the 'quality' of the color."""
quality, color_str = line.split(ThemeConstants.COLOR_SEPARATOR)
color_str = color_str.strip()
self.quality = quality.lower().strip()
self.color_str = ''
self.color_list = []
if ',' in color_str:
self.is_simple_string = False
self.color_list = [c.strip() for c in color_str.split(',')]
else:
self.is_simple_string = True
self.color_str = color_str
self.is_valid = self._color_is_valid()
def _color_is_valid(self):
"""Return if the color (or the list of colors) has a valid html format."""
pattern = "#([a-zA-Z0-9]){6}"
def match_ok(col):
return bool(re.match(pattern, col)) and len(col) == 7
if not self.is_simple_string:
if len(self.color_list) <= self.MAX_COLORS:
return all(match_ok(c) for c in self.color_list)
else:
return False
else:
return match_ok(self.color_str)
def __init__(self, simple_color_list, double_color_list):
"""Initialize the lists of valid _Color objects."""
self.simple_color_list = simple_color_list
self.double_color_list = double_color_list
@classmethod
def from_file(cls, colors_str):
"""Return a _ColorsHandler object with two lists of valid _Color objects.
If the file is empty or there are no valid colors return None."""
if colors_str:
simple_color_list = []
double_color_list = []
for line in colors_str.splitlines():
color = cls._Color(line)
if color.is_valid:
if color.is_simple_string:
simple_color_list.append(color)
else:
double_color_list.append(color)
if simple_color_list or double_color_list:
return cls(simple_color_list, double_color_list)
return None
class ThemeManager:
"""Manage all the operations releted to the themes."""
def __init__(self, owner):
"""Initialize the ThemeManager instance."""
self._owner = owner
self._owner.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR
self._owner.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR
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(
"switch_on_colors",
ThemeConstants.DEFAULT_ON_COLORS
)
self._space_weather_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.
Refresh all relevant widgets.
Display a QMessageBox if the theme is not found."""
self._theme_path = theme_path
if os.path.exists(theme_path):
if self._theme_path != self._current_theme:
self._change()
self._owner.display_specs(
item=self._owner.signals_list.currentItem(),
previous_item=None
)
self._refresh_range_labels()
self._owner.audio_widget.refresh_btns_colors(
self._owner.active_color,
self._owner.inactive_color
)
self._space_weather_labels.refresh()
else:
pop_up(self._owner, title=ThemeConstants.THEME_NOT_FOUND,
text=ThemeConstants.MISSING_THEME).show()
def _pretty_name(self, bad_name):
"""Return a well-formatted theme name."""
return ' '.join(
map(
lambda s: s.capitalize(),
bad_name.split('_')
)
)
def _detect_themes(self):
"""Detect all available themes.
Connect all the actions to change the theme.
Display a QMessageBox if the theme folder is not found."""
themes = []
ag = QActionGroup(self._owner, exclusive=True)
if os.path.exists(ThemeConstants.FOLDER):
for theme_folder in sorted(os.listdir(ThemeConstants.FOLDER)):
relative_folder = os.path.join(ThemeConstants.FOLDER, theme_folder)
if os.path.isdir(os.path.abspath(relative_folder)):
relative_folder = os.path.join(ThemeConstants.FOLDER, theme_folder)
themes.append(relative_folder)
for theme_path in themes:
theme_name = '&' + self._pretty_name(os.path.basename(theme_path))
new_theme = ag.addAction(
QAction(
theme_name,
self._owner, checkable=True
)
)
self._owner.menu_themes.addAction(new_theme)
self._theme_names[theme_name.lstrip('&')] = new_theme
new_theme.triggered.connect(partial(self._apply, theme_path))
else:
pop_up(self._owner, title=ThemeConstants.THEME_FOLDER_NOT_FOUND,
text=ThemeConstants.MISSING_THEME_FOLDER).show()
def _change(self):
"""Change the current theme.
Apply the stylesheet and set active and inactive colors.
Set all the new images needed.
Save the new current theme on file."""
theme_name = os.path.basename(self._theme_path) + ThemeConstants.EXTENSION
try:
with open(os.path.join(self._theme_path, theme_name), "r") as stylesheet:
style = stylesheet.read()
self._owner.setStyleSheet(style)
self._owner.download_window.setStyleSheet(style)
except FileNotFoundError:
pop_up(self._owner, title=ThemeConstants.THEME_NOT_FOUND,
text=ThemeConstants.MISSING_THEME).show()
else:
icons_path = os.path.join(self._theme_path, ThemeConstants.ICONS_FOLDER)
path_to_search_label = os.path.join(icons_path, Constants.SEARCH_LABEL_IMG)
if os.path.exists(path_to_search_label):
path = path_to_search_label
else:
path = ThemeConstants.DEFAULT_SEARCH_LABEL_PATH
self._owner.search_label.setPixmap(QPixmap(path))
self._owner.modulation_search_label.setPixmap(QPixmap(path))
self._owner.location_search_label.setPixmap(QPixmap(path))
self._owner.search_label.setScaledContents(True)
self._owner.modulation_search_label.setScaledContents(True)
self._owner.location_search_label.setScaledContents(True)
path_to_volume_label = os.path.join(icons_path, Constants.VOLUME_LABEL_IMG)
if os.path.exists(path_to_volume_label):
path = path_to_volume_label
else:
path = ThemeConstants.DEFAULT_VOLUME_LABEL_PATH
self._owner.volume_label.setPixmap(QPixmap(path))
self._owner.volume_label.setScaledContents(True)
path_to_colors = os.path.join(self._theme_path, ThemeConstants.COLORS)
active_color_ok = False
inactive_color_ok = False
switch_on_color_ok = False
switch_off_color_ok = False
text_color_ok = False
if os.path.exists(path_to_colors):
with open(path_to_colors, "r") as colors_file:
color_handler = _ColorsHandler.from_file(colors_file.read())
if color_handler is not None:
for color in color_handler.simple_color_list:
if color.quality == Constants.ACTIVE:
self._owner.active_color = color.color_str
active_color_ok = True
if color.quality == Constants.INACTIVE:
self._owner.inactive_color = color.color_str
inactive_color_ok = True
if color.quality == Constants.TEXT_COLOR:
text_color_ok = True
self._space_weather_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(
"switch_on_colors",
color.color_list
)
if color.quality == Constants.LABEL_OFF_COLOR:
switch_off_color_ok = True
self._space_weather_labels.set(
"switch_off_colors",
color.color_list
)
if not (active_color_ok and inactive_color_ok):
self._owner.active_color = ThemeConstants.DEFAULT_ACTIVE_COLOR
self._owner.inactive_color = ThemeConstants.DEFAULT_INACTIVE_COLOR
if not (switch_on_color_ok and switch_off_color_ok):
self._space_weather_labels.set(
"switch_on_colors",
ThemeConstants.DEFAULT_ON_COLORS
)
self._space_weather_labels.set(
"switch_off_colors",
ThemeConstants.DEFAULT_OFF_COLORS
)
if not text_color_ok:
self._space_weather_labels.set(
"text_color",
ThemeConstants.DEFAULT_TEXT_COLOR
)
self._current_theme = self._theme_path
try:
with open(ThemeConstants.CURRENT_THEME_FILE, "w") as current_theme:
current_theme.write(self._theme_path)
except Exception:
pass
def start(self):
"""Start the theme manager."""
self._detect_themes()
if os.path.exists(ThemeConstants.CURRENT_THEME_FILE):
with open(ThemeConstants.CURRENT_THEME_FILE, "r") as current_theme_path:
theme_path = current_theme_path.read()
theme_name = self._pretty_name(os.path.basename(theme_path))
try:
self._theme_names[theme_name].setChecked(True)
except Exception:
pop_up(self._owner, title=ThemeConstants.THEME_NOT_FOUND,
text=ThemeConstants.MISSING_THEME).show()
else:
self._apply(theme_path)
else:
try:
self._theme_names[
self._pretty_name(ThemeConstants.DEFAULT)
].setChecked(True)
except Exception:
pop_up(self._owner, title=ThemeConstants.THEME_NOT_FOUND,
text=ThemeConstants.MISSING_THEME).show()
else:
self._apply(ThemeConstants.DEFAULT_THEME_PATH)

271
src/threads.py Normal file
View File

@@ -0,0 +1,271 @@
import asyncio
from enum import Enum, auto
from io import BytesIO
from math import ceil
import os.path
from shutil import rmtree
from time import perf_counter
from zipfile import ZipFile
import aiohttp
import urllib3
from PyQt5.QtCore import QThread, pyqtSignal
from constants import Constants, Database, ChecksumWhat
from utilities import checksum_ok
# Needed for pyinstaller compilation.
import encodings.idna
class ThreadStatus(Enum):
"""Possible thread status."""
OK = auto()
NO_CONNECTION_ERR = auto()
UNKNOWN_ERR = auto()
BAD_DOWNLOAD_ERR = auto()
UNDEFINED = auto()
SLOW_CONN_ERR = auto()
class _SlowConnError(Exception):
pass
class BaseDownloadThread(QThread):
"""Subclass QThread. Base class for the download threads."""
def __init__(self, parent=None):
"""Set the status to 'UNDEFINED'."""
super().__init__(parent)
self.status = ThreadStatus.UNDEFINED
def __del__(self):
"""Force the termination of the thread."""
self.terminate()
self.wait()
class DownloadThread(BaseDownloadThread):
"""Subclass BaseDownloadThread. Download the database, images and audio samples."""
progress = pyqtSignal(int)
speed_progress = pyqtSignal(float)
_CHUNK = 128 * 1024
_MEGA = 1024**2
_DELTAT = 2
def __init__(self):
"""Just call super().__init__."""
self._db = None
self._exit_call = False
super().__init__()
def _pretty_len(self, byte_obj):
"""Return a well-formatted number of downloaded MB."""
mega = len(byte_obj) / self._MEGA
if mega.is_integer():
return int(mega)
else:
return ceil(mega)
def _get_download_speed(self, data, delta):
"""Return the download speed in MB/s."""
return round(
(len(data) / self._MEGA) / delta, 2
)
def set_exit(self):
self._exit_call = True
def run(self):
"""Override QThread.run. Download the database, images and audio samples.
Handle all possible exceptions. Also extract the files
in the local folder."""
self.status = ThreadStatus.UNDEFINED
self._db = None
raw_data = bytes(0)
sub_data = bytes(0)
try:
self._db = urllib3.PoolManager().request(
'GET',
Database.LINK_LOC,
preload_content=False,
timeout=4.0
)
start = perf_counter()
prev_downloaded = 0
while True:
try:
data = self._db.read(self._CHUNK)
except Exception:
raise _SlowConnError
else:
delta = perf_counter() - start
if not data:
break
raw_data += data
sub_data += data
# Emit a progress signal only if at least 1 MB has been downloaded.
if len(raw_data) - prev_downloaded >= self._MEGA:
prev_downloaded = len(raw_data)
self.progress.emit(self._pretty_len(raw_data))
if delta >= self._DELTAT:
self.speed_progress.emit(
self._get_download_speed(sub_data, delta)
)
sub_data = bytes(0)
start = perf_counter()
if self._exit_call:
self._exit_call = False
self._db.release_conn()
return
except Exception as e: # No (or bad) internet connection.
self._db.release_conn()
if isinstance(e, _SlowConnError):
self.status = ThreadStatus.SLOW_CONN_ERR
else:
self.status = ThreadStatus.NO_CONNECTION_ERR
return
if self._db.status != 200:
self.status = ThreadStatus.BAD_DOWNLOAD_ERR
return
try:
is_checksum_ok = checksum_ok(raw_data, ChecksumWhat.FOLDER)
except Exception: # checksum_ok unable to connect to the reference.
self.status = ThreadStatus.NO_CONNECTION_ERR
return
else:
if not is_checksum_ok:
self.status = ThreadStatus.BAD_DOWNLOAD_ERR
return
if os.path.exists(Constants.DATA_FOLDER):
rmtree(Constants.DATA_FOLDER)
try:
self.progress.emit(Constants.EXTRACTING_CODE)
self.speed_progress.emit(Constants.ZERO_FINAL_SPEED)
with ZipFile(BytesIO(raw_data)) as zipped:
zipped.extractall()
except Exception:
self.status = ThreadStatus.UNKNOWN_ERR
else:
self.status = ThreadStatus.OK
class _AsyncDownloader:
"""Mixin class for asynchronous threads."""
async def _download_resource(self, session, link):
"""Return the content of 'link' as bytes."""
resp = await session.get(link)
return await resp.read()
class UpdateSpaceWeatherThread(BaseDownloadThread, _AsyncDownloader):
"""Subclass BaseDownloadThread. Download the space weather data."""
_PROPERTIES = ("xray", "prot_el", "ak_index", "sgas", "geo_storm")
def __init__(self, space_weather_data):
"""Initialize the a local space_weather_data."""
super().__init__()
self._space_weather_data = space_weather_data
async def _download_property(self, session, property_name):
"""Download the data conteining the information of a specific property."""
link = getattr(Constants, "SPACE_WEATHER_" + property_name.upper())
data = await self._download_resource(session, link)
setattr(self._space_weather_data, property_name, str(data, 'utf-8'))
async def _download_image(self, session, n):
"""Download the data corresponding the n-th image displayed in the screen."""
im = await self._download_resource(
session, Constants.SPACE_WEATHER_IMGS[n]
)
self._space_weather_data.images[n].loadFromData(im)
async def _download_resources(self):
"""Download all the data."""
session = aiohttp.ClientSession()
try:
t = []
for p in self._PROPERTIES:
t.append(
asyncio.create_task(self._download_property(session, p))
)
tot_images = range(len(Constants.SPACE_WEATHER_IMGS))
t1 = []
for im_number in tot_images:
t1.append(
asyncio.create_task(
self._download_image(session, im_number)
)
)
await asyncio.gather(*t, *t1)
except Exception:
self.status = ThreadStatus.UNKNOWN_ERR
else:
self.status = ThreadStatus.OK
finally:
await session.close()
def run(self):
"""Override QThread.run. Start the download of the data."""
self.status = ThreadStatus.UNDEFINED
asyncio.run(self._download_resources())
class UpdateForecastThread(BaseDownloadThread, _AsyncDownloader):
"""Subclass BaseDownloadThread. Download the forecast data."""
class _PropertyName(Enum):
"""Enum used to differentiate between the two data needed."""
FORECAST = auto()
PROBABILITIES = auto()
def __init__(self, owner):
"""Set the owner object (a ForecastData instance)."""
super().__init__()
self.owner = owner
async def _download_property(self, session, link, prop_name):
"""Download the data from 'link' and set the corresponding property of the owner."""
resp = await self._download_resource(session, link)
resp = str(resp, 'utf-8')
if prop_name is self._PropertyName.FORECAST:
self.owner.forecast = resp
if prop_name is self._PropertyName.PROBABILITIES:
self.owner.probabilities = resp
async def _download_resources(self):
"""Download all the data needed."""
session = aiohttp.ClientSession()
try:
await asyncio.gather(
asyncio.create_task(
self._download_property(
session,
Constants.SPACE_WEATHER_GEO_STORM,
self._PropertyName.FORECAST
)
),
asyncio.create_task(
self._download_property(
session,
Constants.FORECAST_PROBABILITIES,
self._PropertyName.PROBABILITIES
)
)
)
except Exception:
self.status = ThreadStatus.UNKNOWN_ERR
else:
self.status = ThreadStatus.OK
finally:
await session.close()
def run(self):
"""Override QThread.run. Start the data download."""
self.status = ThreadStatus.UNDEFINED
asyncio.run(self._download_resources())

155
src/utilities.py Normal file
View File

@@ -0,0 +1,155 @@
from functools import partial
import hashlib
import sys
import os
from pandas import read_csv
from PyQt5.QtWidgets import QMessageBox
from constants import Constants, Signal, Database, ChecksumWhat
def resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller."""
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def uncheck_and_emit(button):
"""Set the button to the unchecked state and emit the clicked signal."""
if button.isChecked():
button.setChecked(False)
button.clicked.emit()
def pop_up(cls, title, text,
informative_text=None,
connection=None,
is_question=False,
default_btn=QMessageBox.Yes):
"""Return a QMessageBox object.
Keyword arguments:
informative_text -- possible informative text to be displayed.
connection -- a callable to connect the message when emitting the finished signal.
is_question -- whether the message contains a question.
default_btn -- the default button for the possible answer to the question."""
msg = QMessageBox(cls)
msg.setWindowTitle(title)
msg.setText(text)
if informative_text:
msg.setInformativeText(informative_text)
if connection:
msg.finished.connect(connection)
if is_question:
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
msg.setDefaultButton(default_btn)
msg.adjustSize()
return msg
def checksum_ok(data, what):
"""Check whether the checksum of the 'data' argument is correct."""
code = hashlib.sha256()
code.update(data)
if what is ChecksumWhat.FOLDER:
n = 0
elif what is ChecksumWhat.DB:
n = 1
else:
raise ValueError("Wrong entry name.")
try:
reference = read_csv(
Database.LINK_REF,
delimiter=Database.DELIMITER
).iat[-1, n]
except Exception:
raise
return code.hexdigest() == reference
def connect_events_to_func(events_to_connect, fun_to_connect, fun_args):
"""Connect all elements of events_to_connect to the callable fun_to_connect.
fun_args is a list of fun_to_connect arguments."""
if fun_args is not None:
for event in events_to_connect:
event.connect(partial(fun_to_connect, *fun_args))
else:
for event in events_to_connect:
event.connect(fun_to_connect)
def filters_limit(spinbox, filter_unit, confidence, sign=1):
"""Return the actual limit of a numerical filter."""
band_filter = spinbox.value() * Constants.CONVERSION_FACTORS[filter_unit.currentText()]
return band_filter + sign * (confidence.value() * band_filter) // 100
def is_undef_freq(current_signal):
"""Return whether the lower or upper frequency of a signal is undefined."""
lower_freq = current_signal.at[Signal.INF_FREQ]
upper_freq = current_signal.at[Signal.SUP_FREQ]
return lower_freq == Constants.UNKNOWN or upper_freq == Constants.UNKNOWN
def is_undef_band(current_signal):
"""Return whether the lower or upper band of a signal is undefined."""
lower_band = current_signal.at[Signal.INF_BAND]
upper_band = current_signal.at[Signal.SUP_BAND]
return lower_band == Constants.UNKNOWN or upper_band == Constants.UNKNOWN
def _change_unit(str_num):
"""Return a scale factor given the number of digits of a numeric string."""
digits = len(str_num)
if digits < 4:
return 1
elif digits < 7:
return 1000
elif digits < 10:
return 10**6
else:
return 10**9
def format_numbers(lower, upper):
"""Return the string which displays the numeric limits of a filter."""
units = {1: 'Hz', 1000: 'kHz', 10**6: 'MHz', 10**9: 'GHz'}
lower_factor = _change_unit(lower)
upper_factor = _change_unit(upper)
pre_lower = lower
pre_upper = upper
lower = safe_cast(lower, int) / lower_factor
upper = safe_cast(upper, int) / upper_factor
if lower.is_integer():
lower = int(lower)
else:
lower = round(lower, 2)
if upper.is_integer():
upper = int(upper)
else:
upper = round(upper, 2)
if pre_lower != pre_upper:
return f"{lower:,} {units[lower_factor]} - {upper:,} {units[upper_factor]}"
else:
return f"{lower:,} {units[lower_factor]}"
def safe_cast(value, cast_type, default=-1):
"""Call 'cast_type(value)' and return the result.
If the operation fails return 'default'.
Should be used to perform 'safe casts'.
Keyword argument:
default -- default value returned if the cast fails.
"""
try:
r = cast_type(value)
except Exception:
r = default
finally:
return r

353
src/weatherdata.py Normal file
View File

@@ -0,0 +1,353 @@
import re
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from threads import (BaseDownloadThread,
UpdateSpaceWeatherThread,
ThreadStatus,
UpdateForecastThread)
from constants import Constants
from switchable_label import MultiColorSwitchableLabel
from utilities import safe_cast
class _BaseWeatherData(QObject):
"""Base class for the weather data. Extends QObject."""
update_complete = pyqtSignal(bool)
def __init__(self):
"""Create a BaseDownloadThread object."""
super().__init__()
self._update_thread = BaseDownloadThread()
@property
def is_updating(self):
"""Return whether the thread is running."""
return self._update_thread.isRunning()
def update(self):
"""Start the thread."""
self._update_thread.start()
def _parse_data(self):
"""Dummy function. Must be overrided by subclasses."""
pass
@pyqtSlot()
def _parse_and_emit_signal(self):
"""Parse the data and emit an 'update_complete' signal.
If the download was not successful, do not parse the data.
The 'update_complete' signal propagates the thread status up to the
calling slot."""
status_ok = False
if self._update_thread.status is ThreadStatus.OK:
status_ok = True
self._parse_data()
self.update_complete.emit(status_ok)
def _double_split(self, string):
"""Given a string, return a list of lists.
First split on each line. Then split each line on whitespaces."""
return [i.split() for i in string.splitlines()]
def shutdown_thread(self):
"""Terminate the download thread."""
self._update_thread.terminate()
self._update_thread.wait()
class SpaceWeatherData(_BaseWeatherData):
"""Space weather class. Extends _BaseWeatherData."""
def __init__(self):
"""Set all attributes and connect the thread to _parse_and_emit_signal."""
super().__init__()
self.xray = ''
self.prot_el = ''
self.ak_index = ''
self.sgas = ''
self.geo_storm = ''
self.images = [
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap()
]
self._update_thread = UpdateSpaceWeatherThread(self)
self._update_thread.finished.connect(self._parse_and_emit_signal)
def _parse_data(self):
"""Override _BaseWeatherData._parse_data.
Set all the data."""
self.xray = self._double_split(self.xray)
self.prot_el = self._double_split(self.prot_el)
self.ak_index = self._double_split(self.ak_index)
self.sgas = self._double_split(self.sgas)
self.geo_storm = self._double_split(self.geo_storm)
def remove_data(self):
"""Remove the reference to all the data."""
self.xray = ''
self.prot_el = ''
self.ak_index = ''
self.sgas = ''
self.geo_storm = ''
self.images = [
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap(),
QPixmap()
]
def _make_labels_table(forecast, probabilities, rows):
"""Organize all the arguments to feed _get_lbl_value."""
def get_first_split(x):
return x.split("/")[0]
def get_second_split(x):
return x.split("/")[1]
def get_third_split(x):
return x.split("/")[2]
solar_row = rows["solar_row"]
event_row = rows["event_row"]
rb_now_row = rows["rb_now_row"]
ga_now_row = rows["ga_now_row"]
kp_index_row = rows["kp_index_row"]
return [
[
[forecast, solar_row, 3, None],
[probabilities, event_row + 1, 2, get_first_split],
[probabilities, event_row + 2, 2, get_first_split],
[probabilities, event_row + 3, 1, get_first_split],
[forecast, rb_now_row, 1, None],
[forecast, rb_now_row + 1, 3, None],
[probabilities, ga_now_row + 2, 1, get_first_split],
[probabilities, ga_now_row + 3, 2, get_first_split],
[probabilities, ga_now_row + 4, 2, get_first_split],
[probabilities, ga_now_row + 6, 1, get_first_split],
[probabilities, ga_now_row + 7, 2, get_first_split],
[probabilities, ga_now_row + 8, 2, get_first_split],
[forecast, kp_index_row + 3, 1, None],
[forecast, kp_index_row + 4, 1, None],
[forecast, kp_index_row + 5, 1, None],
[forecast, kp_index_row + 6, 1, None],
[forecast, kp_index_row + 7, 1, None],
[forecast, kp_index_row + 8, 1, None],
[forecast, kp_index_row + 9, 1, None],
[forecast, kp_index_row + 10, 1, None]
],
[
[forecast, solar_row, 4, None],
[probabilities, event_row + 1, 2, get_second_split],
[probabilities, event_row + 2, 2, get_second_split],
[probabilities, event_row + 3, 1, get_second_split],
[forecast, rb_now_row, 2, None],
[forecast, rb_now_row + 1, 4, None],
[probabilities, ga_now_row + 2, 1, get_second_split],
[probabilities, ga_now_row + 3, 2, get_second_split],
[probabilities, ga_now_row + 4, 2, get_second_split],
[probabilities, ga_now_row + 6, 1, get_second_split],
[probabilities, ga_now_row + 7, 2, get_second_split],
[probabilities, ga_now_row + 8, 2, get_second_split],
[forecast, kp_index_row + 3, 2, None],
[forecast, kp_index_row + 4, 2, None],
[forecast, kp_index_row + 5, 2, None],
[forecast, kp_index_row + 6, 2, None],
[forecast, kp_index_row + 7, 2, None],
[forecast, kp_index_row + 8, 2, None],
[forecast, kp_index_row + 9, 2, None],
[forecast, kp_index_row + 10, 2, None]
],
[
[forecast, solar_row, 5, None],
[probabilities, event_row + 1, 2, get_third_split],
[probabilities, event_row + 2, 2, get_third_split],
[probabilities, event_row + 3, 1, get_third_split],
[forecast, rb_now_row, 3, None],
[forecast, rb_now_row + 1, 5, None],
[probabilities, ga_now_row + 2, 1, get_third_split],
[probabilities, ga_now_row + 3, 2, get_third_split],
[probabilities, ga_now_row + 4, 2, get_third_split],
[probabilities, ga_now_row + 6, 1, get_third_split],
[probabilities, ga_now_row + 7, 2, get_third_split],
[probabilities, ga_now_row + 8, 2, get_third_split],
[forecast, kp_index_row + 3, 3, None],
[forecast, kp_index_row + 4, 3, None],
[forecast, kp_index_row + 5, 3, None],
[forecast, kp_index_row + 6, 3, None],
[forecast, kp_index_row + 7, 3, None],
[forecast, kp_index_row + 8, 3, None],
[forecast, kp_index_row + 9, 3, None],
[forecast, kp_index_row + 10, 3, None]
]
]
def _get_lbl_value(data, row, col, f=None):
"""Return the well-formatted string-value of the label."""
val = data[row][col]
if f is not None:
val = f(val)
val = val.rstrip('%')
if len(val) > 1:
val = val.lstrip('0')
return val
class ForecastData(_BaseWeatherData):
"""3-day forecast class. Extends _BaseWeatherData."""
ROW_KEYWORDS = {
"solar_row": "S1 or greater",
"event_row": "III. Event probabilities",
"rb_now_row": "R1-R2",
"ga_now_row": "Geomagnetic Activity Probabilities",
"kp_index_row": "NOAA Kp index breakdown"
}
LABELS_PER_COLUMN = 20
def __init__(self, owner):
"""Initialize all attributes and connect the thread to _parse_and_emit_signal."""
super().__init__()
self.forecast = []
self.probabilities = []
self._update_thread = UpdateForecastThread(self)
self._update_thread.finished.connect(self._parse_and_emit_signal)
self._today_lbl = owner.today_lbl
self._today_p1_lbl = owner.today_p1_lbl
self._today_p2_lbl = owner.today_p2_lbl
today_lbls = []
today_p1_lbls = []
today_p2_lbls = []
flags = ['', 'p1_', 'p2_']
for flag in flags:
title_lbl = getattr(self, "_today_" + flag + "lbl")
title_lbl.setText("-")
for index in range(self.LABELS_PER_COLUMN):
label = getattr(
owner,
"forecast_today_" + flag + str(index) + "_lbl"
)
label.setText(Constants.UNKNOWN)
if flag == flags[0]:
today_lbls.append(label)
if flag == flags[1]:
today_p1_lbls.append(label)
if flag == flags[2]:
today_p2_lbls.append(label)
self._all_lbls = [today_lbls, today_p1_lbls, today_p2_lbls]
def _parse_data(self):
"""Override _BaseWeatherData._parse_data.
Set all the relevant data."""
# Remove possible '(G\d)' from the kp_index table
self.forecast = re.sub(
'\(G\d\)', lambda obj: '', self.forecast
)
self.forecast = self.forecast.splitlines()
self.probabilities = re.sub(
'\(G\d\)', lambda obj: '', self.probabilities
)
self.probabilities = self.probabilities.splitlines()
def _split_lists(self):
"""Split the elements of forecast and probabilities."""
return [i.split() for i in self.forecast], [i.split() for i in self.probabilities]
def _find_row_with(self, data, text):
"""Given a list of strings, return the index of the first string containing the target text."""
for i, row in enumerate(data):
if text in row:
return i
return None
def _get_rows(self):
"""Get all the rows needed for updating the screen.
Raise an exception if something goes wrong."""
rows = {}
rows["solar_row"] = self._find_row_with(
self.forecast,
self.ROW_KEYWORDS["solar_row"]
)
rows["event_row"] = self._find_row_with(
self.probabilities,
self.ROW_KEYWORDS["event_row"]
)
rows["rb_now_row"] = self._find_row_with(
self.forecast,
self.ROW_KEYWORDS["rb_now_row"]
)
rows["ga_now_row"] = self._find_row_with(
self.probabilities,
self.ROW_KEYWORDS["ga_now_row"]
)
rows["kp_index_row"] = self._find_row_with(
self.forecast,
self.ROW_KEYWORDS["kp_index_row"]
)
if any(row is None for row in rows.values()):
raise Exception('Missing Rows')
else:
return rows
def _set_dates(self, forecast, solar_row):
"""Set the date labels."""
month = forecast[solar_row - 1][0]
today = forecast[solar_row - 1][1]
today_p1 = forecast[solar_row - 1][3]
today_p2 = forecast[solar_row - 1][5]
self._today_lbl.setText(month + ' ' + today)
self._today_p1_lbl.setText(month + ' ' + today_p1)
self._today_p2_lbl.setText(month + ' ' + today_p2)
def _set_labels_values(self, labels_table):
"""Set all the labels values."""
for lbl_list, table in zip(self._all_lbls, labels_table):
for lbl, row in zip(lbl_list, table):
lbl.switch_off()
value = _get_lbl_value(*row)
lbl.level = safe_cast(value, int)
if not isinstance(lbl, MultiColorSwitchableLabel):
value += '%'
lbl.setText(value)
lbl.switch_on()
def update_all_labels(self):
"""Update all the labels values.
If an exception is raised in the process, do nothing."""
try:
rows = self._get_rows()
forecast, probabilities = self._split_lists()
labels_table = _make_labels_table(forecast, probabilities, rows)
self._set_dates(forecast, rows["solar_row"])
self._set_labels_values(labels_table)
except Exception:
pass
def remove_data(self):
"""Remove the reference to the downloaded data."""
self.forecast = []
self.probabilities = []