See the first reply for simpler methods.
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).
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.
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.
[user@disp42 ~]$ sudo dnf install glib2-devel
[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.
Colors are based on the Arc-Dark theme.
#!/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"
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 ()
{
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 ()
{
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 ()
{
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
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
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
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
Widget to be added in the view menu.
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="
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"
Add the dark mode widget to the existing view menu.
qm_darkmode_menu="
self.menu_view.addAction(self.action_dark_mode)"
Restore the preference of light or dark mode.
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="
@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"
A script to restore the backup files. In case the Dark Mode doesn't behave as expected.
#!/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
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)