Qube Manager Dark Mode (built-in method)

Qube Manager & Tools: Dark Mode


Add an option in the view menu to enable/disable dark mode.

The script must be launch in a dom0 terminal.

Make sure to have only space character for python part.
If you have tab character, qube manager will fail to start.

Dark mode can be set in the qube-manager config file:
$HOME/.config/The Qubes Project/qubes-qube-manager.conf

[view]
darkmode=true

Stylesheet

Colors are based on Arc-Dark theme.
You can extract the css file from your theme with gresource.

qubes_cfg_dir="$HOME/.config//The Qubes Project/"
dark_stylesheet=dark-stylesheet.css
echo '
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;
}' > "$qubes_cfg_dir/$dark_stylesheet"

Function

Terms to feed to your favorite search engine:

  • bash sed
  • bash <()
  • bash function $#
  • bash function $@
  • bash if [[ ]]
  • bash recursive function

Bash command <() doesn’t handle sudo.
Just like file redirection (text > file) doesn’t.

A backup file is made for each file!

python_version=$(python3 --version | grep -Eo '[0-9]+\.[0-9]+')
qm_dir=/usr/lib/python$python_version/site-packages/qubesmanager/
add_data_after_pattern ()
{
    handle_data ()
    {
        if [[ $# -ne 1 ]]
        then
            sed -i -E "/$2/ r"<(echo "$3") $1
            handle_data $1 "${@:4}"
        fi
    }
    local qtool=$qm_dir/$1
    local qtmp=$HOME/$1

    if [ ! -f $qtool.bak ]; then
        sudo cp $qtool $qtool.bak
    fi
    sudo cp $qtool $qtmp
    sudo chown user:user $qtmp

    handle_data $qtmp "${@:2}"

    sudo mv $qtmp $qtool
    sudo chown root:root $qtool
}

Qubes tools

If setting is set to dark mode, use our css stylesheet.

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'
            stylesheet = open(qubes_cfg_dir + '$dark_stylesheet', 'r')
            self.setStyleSheet(stylesheet.read())
            stylesheet.close()"
    for qtool_file in $@
    do
        add_data_after_pattern $qtool_file 'setupUi' "$qm_darkmode"
    done
}
add_dark_mode \
    global_settings.py \
    settings.py \
    template_manager.py \
    create_new_vm.py \
    clone_vm.py \
    backup.py \
    restore.py \
    log_dialog.py

sudo sed -i 's/^from PyQt5 import /&QtCore, /' $qm_dir/log_dialog.py

Qube manager

Add a toggle option in the view menu.

qm_set_darkmode="
    @pyqtSlot(bool)
    def on_action_dark_mode_toggled(self, checked):
        if checked:
            qubes_cfg_dir = '$qubes_cfg_dir'
            stylesheet = open(qubes_cfg_dir + '$dark_stylesheet', 'r')
            self.setStyleSheet(stylesheet.read())
            stylesheet.close()
        else:
            self.setStyleSheet('')
        if self.settings_loaded:
            self.manager_settings.setValue('view/darkmode', checked)"

qm_menu () {
    echo "        self.menu_view.addAction(self.$1)"
}
qm_darkmode_menu=$(qm_menu action_dark_mode)

qm_restore () {
    echo "
        if self.manager_settings.value('$1',
                                       defaultValue='false') != 'false':
            self.action_dark_mode.setChecked(True)"
}
qm_darkmode_restore=$(qm_restore 'view/darkmode')

add_data_after_pattern qube_manager.py \
    'self\.close' "$qm_set_darkmode" \
    'compact_view\.setChecked' "$qm_darkmode_restore" \
    'Action\(self\.action_compact_view' "$qm_darkmode_menu"

Create the Qt widget to handle dark mode. (Qt is the gui framework)

qm_widget () {
    echo "
        self.$1 = QtWidgets.QAction(VmManagerWindow)
        self.$1.setCheckable(True)
        self.$1.setChecked($2)
        self.$1.setObjectName('$1')"
}
qm_darkmode_widget=$(qm_widget action_dark_mode 'False')

qm_text () {
    echo "        self.$1.$2(_translate('VmManagerWindow', '$3'))"
}
qm_darkmode_text=$(qm_text action_dark_mode setText 'Dark mode')

add_data_after_pattern ui_qubemanager.py \
    '"action_compact_view"' "$qm_darkmode_widget" \
    'Compact view' "$qm_darkmode_text"

Note

qube manager version: 4.1.25-1
Dark mode will be erase if qube manager get updated.

1 Like

Two, more simple solutions.

wiki.archlinux.org/title/qt#Qt_Style_Sheets
Qt application can use Style Sheets, which are CSS files.

These 2 methods add the -stylesheet option to the Qubes applications.

method 1

Add these two lines:

    sys.argv.append('-stylesheet')
    sys.argv.append('/path/to/dark-stylesheet.css')

in /usr/lib/python3.8/site-packages/qubesmanager/utils.py:

  • below the line: def run_asynchronous
  • below the line: def run_synchronous

in /usr/lib/python3.8/site-packages/qubesmanager/create_new_vm.py

  • below the line: def main

The functions are at the bottom of files.

method 2

Modify desktop files for Qube Manager and Qubes Tools.

/usr/share/applications/

Example for Qube Manager:

[Desktop Entry]
...
Exec=qubes-qube-manager -stylesheet '/path/to/dark-stylesheet.css'
...

The desktop files are for the apps menu > Qubes Tools shorcuts.

For the Qubes Tray icon > Open Qube Manager
Add the -stylesheet option in run_manager function at line 350 (need logout).

/usr/lib/python3.8/site-packages/qui/tray/domains.py

def run_manager(_item):
    subprocess.Popen(['qubes-qube-manager', '-stylesheet',
        '/path/to/dark-stylesheet.css'])

note

To disable the dark mode, rename or move the CSS file.

Dark mode works out of the box for me, but I run KDE