Better folder organization
2
src/.flake8
Normal file
@@ -0,0 +1,2 @@
|
||||
[flake8]
|
||||
ignore = E221, E501, W605, W504
|
||||
1641
src/artemis.py
Normal file
9549
src/artemis.ui
Normal file
144
src/audio_player.py
Normal 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)
|
||||
35
src/clickable_progress_bar.py
Normal 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
@@ -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
@@ -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
59
src/double_text_button.py
Normal 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
@@ -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
@@ -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()
|
||||
38
src/fixed_aspect_ratio_label.py
Normal 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()
|
||||
26
src/fixed_aspect_ratio_widget.py
Normal 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
@@ -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
@@ -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;
|
||||
}
|
||||
5
src/themes/acqua/colors.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
active=#228eff
|
||||
inactive=#808086
|
||||
off=#3a7bd5, #3a6073
|
||||
on=#00d2ff, #928dab
|
||||
text=#ffffff
|
||||
BIN
src/themes/acqua/icons/down-arrow.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
src/themes/acqua/icons/down-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/acqua/icons/down-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/acqua/icons/search_icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/themes/acqua/icons/up-arrow.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
src/themes/acqua/icons/up-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/acqua/icons/up-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/acqua/icons/volume.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
5
src/themes/console_style/colors.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
active= #ff9900
|
||||
inactive= #616161
|
||||
on=#fc4a1a, #f7b733
|
||||
off= #000000, #434343
|
||||
text=#ffffff
|
||||
413
src/themes/console_style/console_style.qss
Normal 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;
|
||||
}
|
||||
BIN
src/themes/console_style/icons/down-arrow.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
src/themes/console_style/icons/down-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/console_style/icons/down-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/console_style/icons/search_icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/themes/console_style/icons/up-arrow.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
src/themes/console_style/icons/up-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/console_style/icons/up-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/console_style/icons/volume.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
5
src/themes/dark/colors.txt
Normal 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
@@ -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;
|
||||
}
|
||||
BIN
src/themes/dark/icons/down-arrow.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
src/themes/dark/icons/down-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/dark/icons/down-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/dark/icons/off.png
Normal file
|
After Width: | Height: | Size: 820 B |
BIN
src/themes/dark/icons/off_press.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/themes/dark/icons/on.png
Normal file
|
After Width: | Height: | Size: 916 B |
BIN
src/themes/dark/icons/on_press.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/themes/dark/icons/search_icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/themes/dark/icons/up-arrow.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
src/themes/dark/icons/up-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/dark/icons/up-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/dark/icons/volume.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
5
src/themes/elegant_dark/colors.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
active= #00ff00
|
||||
inactive= #9f9f9f
|
||||
on=#fdfc47, #24fe41
|
||||
off=#f2f2f2,#eaeaea
|
||||
text=#000000
|
||||
407
src/themes/elegant_dark/elegant_dark.qss
Normal 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;
|
||||
}
|
||||
BIN
src/themes/elegant_dark/icons/down-arrow.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
src/themes/elegant_dark/icons/down-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/elegant_dark/icons/down-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/elegant_dark/icons/search_icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/themes/elegant_dark/icons/up-arrow.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
src/themes/elegant_dark/icons/up-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/elegant_dark/icons/up-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/elegant_dark/icons/volume.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
4
src/themes/material_design_dark/colors.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
active=#88cc00
|
||||
inactive=#546E7A
|
||||
off=#304352,#d7d2cc
|
||||
on=#3ca55c,#b5ac49
|
||||
BIN
src/themes/material_design_dark/icons/down-arrow.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
src/themes/material_design_dark/icons/down-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_dark/icons/down-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_dark/icons/off.png
Normal file
|
After Width: | Height: | Size: 820 B |
BIN
src/themes/material_design_dark/icons/off_press.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/themes/material_design_dark/icons/on.png
Normal file
|
After Width: | Height: | Size: 916 B |
BIN
src/themes/material_design_dark/icons/on_press.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/themes/material_design_dark/icons/search_icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/themes/material_design_dark/icons/up-arrow.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
src/themes/material_design_dark/icons/up-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_dark/icons/up-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_dark/icons/volume.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
489
src/themes/material_design_dark/material_design_dark.qss
Normal 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;
|
||||
}
|
||||
4
src/themes/material_design_light/colors.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
active=#6ECE12
|
||||
inactive=#b3b3cc
|
||||
off=#948e99,#2e1437
|
||||
on=#b993d6,#8ca6db
|
||||
BIN
src/themes/material_design_light/icons/down-arrow.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
src/themes/material_design_light/icons/down-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_light/icons/down-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_light/icons/off.png
Normal file
|
After Width: | Height: | Size: 820 B |
BIN
src/themes/material_design_light/icons/off_press.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/themes/material_design_light/icons/on.png
Normal file
|
After Width: | Height: | Size: 916 B |
BIN
src/themes/material_design_light/icons/on_press.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/themes/material_design_light/icons/search_icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/themes/material_design_light/icons/up-arrow.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
src/themes/material_design_light/icons/up-arrow_hover.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_light/icons/up-arrow_off.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/themes/material_design_light/icons/volume.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
489
src/themes/material_design_light/material_design_light.qss
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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 = []
|
||||