Qubes OS - Qubes tools - Dark mode
Preview
Introduction
See the first reply for simpler methods.
Description
Add an option in the view menu of Qube Manager to enable/disable dark mode.
Dark mode can also be set in the Qube Manager config file:
[user@dom0 ~]$ cat "$HOME/.config/The Qubes Project/qubes-qube-manager.conf"
[...]
[view]
darkmode=true
This script is a one-file format (single .sh
file).
Usage
Copy the script to dom0
, make it executable, then launch it.
qubes-os.org/doc/how-to-copy-from-dom0/#copying-to-dom0
Caution:
The code you run in dom0 MUST be understood.
file_name=qubes_tools_dark_mode.sh
qvm-run --pass-io sys-usb "
cat /home/user/$file_name" > $HOME/$file_name
chmod +x $HOME/$file_name
$HOME/$file_name
Make sure to have only space
characters for python code.
If you have tab
characters, Qube Manager & Tools will fail to start.
Theme colors
You can extract the CSS file of your theme with the gresource
utility.
If gresource
isn’t available, you need to install the package that contain it.
Fedora
[user@disp42 ~]$ sudo dnf install glib2-devel
Debian
[user@disp42 ~]$ sudo apt install libglib2.0-bin
e.g.
List the files of your gtk.gresource
theme.
[user@disp42 ~]$ gresource list /usr/share/themes/Arc-Dark/gtk-3.0/gtk.gresource
[...]
/org/gnome/arc-theme/gtk-main.css
Once the file path is known, extract it.
[user@disp42 ~]$ gresource extract /usr/share/themes/Arc-Dark/gtk-3.0/gtk.gresource /org/gnome/arc-theme/gtk-main.css > gtk-arc-dark.css
You may want to use an online CSS formatter to make it easier to read.
You can now find the color values you want to use.
CSS file
Colors are based on the Arc-Dark theme.
CSS content
#!/usr/bin/bash
set -eu -o pipefail
css_content='
QMenu {
border: 1px solid #252A32;
}
::separator {
background-color: #475970;
height: 1px;
width: 1px;
}
:disabled {
color: gray;
}
::tab:!selected {
background-color: #2F343F;
}
::tab:!selected:hover {
background-color: #505666;
}
* {
color: #D3DAE3;
background-color: #383C4A;
alternate-background-color: #404552;
selection-background-color: #5294E2;
}'
Save the css file in the ‘Qubes Project’ config directory.
It can be an other directory (e.g. $HOME/Documents
).
qubes_cfg_dir="$HOME/.config/The Qubes Project/"
css_file=dark-stylesheet.css
echo "$css_content" > "$qubes_cfg_dir/$css_file"
Qubes Tools
Location of the Qubes Tools files.
python_version=$(python3 --version | grep -Eo '[0-9]+\.[0-9]+')
lib_python_dir=/usr/lib/python$python_version/site-packages/
qm_dir=$lib_python_dir/qubesmanager/
Helper function to backup a file (to be able to reverse the changes).
create_backup_file ()
create_backup_file ()
{
if [ ! -f $1.bak ]
then
sudo cp $1 $1.bak
fi
}
Helper function to add our code to the Qubes files.
add_data_after_pattern ()
add_data_after_pattern ()
{
handle_data ()
{
if [[ $# -ne 1 ]]
then
echo "$3" | sudo tee /tmp/qm_content.tmp > /dev/null
sudo sed -i -E "/$2/ r /tmp/qm_content.tmp" $1
$FUNCNAME $1 "${@:4}"
fi
}
local qtool=$qm_dir/$1
create_backup_file $qtool
handle_data $qtool "${@:2}"
}
Enable dark mode (or not) according to the setting made in Qube Manager.
add_dark_mode ()
add_dark_mode ()
{
local qm_darkmode="
if not parent:
manager_settings = QtCore.QSettings(
'The Qubes Project', 'qubes-qube-manager')
else:
manager_settings = QtCore.QSettings(self)
if manager_settings.value('view/darkmode',
defaultValue='false') != 'false':
qubes_cfg_dir = '$qubes_cfg_dir'
with open(qubes_cfg_dir + '$css_file') as css_file:
self.setStyleSheet(css_file.read())"
for qtool_file in $@
do
add_data_after_pattern $qtool_file 'setupUi' "$qm_darkmode"
done
}
Add our code to the Qubes Tools files.
(template_manager.py
is actually the code for Template Switcher).
For 4.2, remove global_settings.py
.
add_dark_mode \
backup.py \
bootfromdevice.py \
clone_vm.py \
create_new_vm.py \
global_settings.py \
log_dialog.py \
restore.py \
settings.py \
template_manager.py
sudo sed -i 's/^from PyQt5 import /&QtCore, /' \
$qm_dir/bootfromdevice.py \
$qm_dir/log_dialog.py
Template Manager (4.2)
Add our code to the Template Manager tool.
add_dark_mode qvm_template_gui.py
sudo sed -i -E -e '/^import PyQt5\.QtWidgets/ a from PyQt5 import QtCore' \
-e 's/self, actions/&, parent=None/' \
-e 's/TemplateInstallConfirmDialog\(actions/&, parent/' \
-e 's/TemplateInstallProgressDialog\(actions/&, parent/' \
-e 's/do_install\(self/&, parent=None/' \
$qm_dir/qvm_template_gui.py
Global Config, Policy Editor, Update & Create New Qube (4.2)
Make the new GUI tools use Arc-Dark theme values.
qui_dark_css=$lib_python_dir/qui/styles/qubes-colors-dark.css
create_backup_file $qui_dark_css
sudo sed -i -E -e '/top-background / s/#[^;]+/#2F343F/' \
-e '/top-background-2/ s/#[^;]+/#383C4A/' \
-e '/bottom-background/ s/#[^;]+/#404552/' \
-e '/background-frame/ s/#[^;]+/#252A32/' \
$qui_dark_css
Application menu (4.2)
Make the menu use Arc-Dark theme values.
(need logout or kill & restart the new menu to take effect)
qmenu_dark_css=$lib_python_dir/qubes_menu/qubes-menu-dark.css
create_backup_file $qmenu_dark_css
sudo sed -i -E -e '/left-background/ s/#[^;]+/#383C4A/' \
-e '/right-background/ s/#[^;]+/#404552/' \
-e '/outer-background/ s/#[^;]+/#2F343F/' \
$qmenu_dark_css
Qube Manager
ui_qubemanager.py
Widget to be added in the view menu.
qm_darkmode_widget
qm_darkmode_widget="
self.action_dark_mode = QtWidgets.QAction(VmManagerWindow)
self.action_dark_mode.setCheckable(True)
self.action_dark_mode.setChecked(False)
self.action_dark_mode.setObjectName('action_dark_mode')"
Text displayed in the view menu.
qm_darkmode_text
qm_darkmode_text="
self.action_dark_mode.setText(_translate('VmManagerWindow', 'Dark mode'))"
Add our code to ui_qubemanager.py
.
add_data_after_pattern ui_qubemanager.py \
'"action_compact_view"' "$qm_darkmode_widget" \
'Compact view' "$qm_darkmode_text"
qube_manager.py
Add the dark mode widget to the existing view menu.
qm_darkmode_menu
qm_darkmode_menu="
self.menu_view.addAction(self.action_dark_mode)"
Restore the preference of light or dark mode.
qm_darkmode_restore
qm_darkmode_restore="
if self.manager_settings.value('view/darkmode',
defaultValue='false') != 'false':
self.action_dark_mode.setChecked(True)"
Enable or disable dark mode when we click on the menu item.
Save the preference in the config file.
qm_set_darkmode
qm_set_darkmode="
@pyqtSlot(bool)
def on_action_dark_mode_toggled(self, checked):
if checked:
qubes_cfg_dir = '$qubes_cfg_dir'
with open(qubes_cfg_dir + '$css_file') as css_file:
self.setStyleSheet(css_file.read())
else:
self.setStyleSheet('')
if self.settings_loaded:
self.manager_settings.setValue('view/darkmode', checked)"
Add our code to qube_manager.py
.
add_data_after_pattern qube_manager.py \
'compact_view\.setChecked' "$qm_darkmode_restore" \
'Action\(self\.action_compact_view' "$qm_darkmode_menu" \
'self\.close' "$qm_set_darkmode"
Restore backup
A script to restore the backup files.
In case the Dark Mode doesn’t behave as expected.
restore_backup_file ()
#!/bin/bash
python_version=$(python3 --version | grep -Eo '[0-9]+\.[0-9]+')
lib_python_dir=/usr/lib/python$python_version/site-packages/
qm_dir=$lib_python_dir/qubesmanager/
restore_backup_file ()
{
for qtool in $@
do
if [ -f $qtool.bak ]
then
sudo cp $qtool.bak $qtool
else
echo "${qtool##*/}: no backup file"
fi
done
}
restore_backup_file \
$qm_dir/backup.py \
$qm_dir/bootfromdevice.py \
$qm_dir/clone_vm.py \
$qm_dir/create_new_vm.py \
$qm_dir/global_settings.py \
$qm_dir/log_dialog.py \
$qm_dir/restore.py \
$qm_dir/settings.py \
$qm_dir/template_manager.py \
$qm_dir/qube_manager.py \
$qm_dir/ui_qubemanager.py
# 4.2
qui_dark_css=$lib_python_dir/qui/styles/qubes-colors-dark.css
qmenu_dark_css=$lib_python_dir/qubes_menu/qubes-menu-dark.css
restore_backup_file \
$qm_dir/qvm_template_gui.py \
$qui_dark_css \
$qmenu_dark_css
Remarks
The dark mode will be erased on Qube Manager update.
When it happens, delete the old backup files before executing this script.
(Otherwise, you will keep the old version as a backup instead of the new one)