Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5693b3f
fix compatibility with python-pillow 10.0.0
xuzhen Jul 4, 2023
0564a4e
Merge branch 'M7S:pygi-python3' into pygi-python3
xuzhen Jul 4, 2023
19dba7c
bump version
xuzhen Jul 28, 2023
8257929
Improve the appearance of window title
xuzhen Nov 14, 2023
6b34d40
Automatically use the most appropriate ellipsize mode
xuzhen Nov 16, 2023
6f87d30
Remove unused launcher files
xuzhen Nov 16, 2023
ecfe511
Fix compatibility with python 3.12
xuzhen Nov 21, 2023
882310d
Fix python3 requirement
xuzhen Nov 21, 2023
7c78e61
Use setuptools instead of distutils
xuzhen Dec 2, 2023
cf8b636
Fix install path
xuzhen Dec 4, 2023
320c3df
Fix compatibility with old version of setuptools and pip
xuzhen Dec 4, 2023
6c3abc0
Move mate panel applet into a separate repository
xuzhen Dec 4, 2023
ca494ff
Fix directories
xuzhen Dec 5, 2023
9f780f9
Reorganize the directory structure
xuzhen Dec 5, 2023
70c4cbe
Disallow import all
xuzhen Dec 5, 2023
0d01977
Fix TypeError when running on Xwayland
xuzhen Dec 5, 2023
262b3bd
Fix compatibility with new version of systemd
xuzhen Dec 5, 2023
f54e7d6
Fix file names
xuzhen Dec 5, 2023
6b40bac
Fix condition and description
xuzhen Dec 6, 2023
31209c2
Fix path
xuzhen Dec 6, 2023
2942384
Update README
xuzhen Dec 16, 2023
ab8b856
Fix centered mode for applets
xuzhen Jan 3, 2024
b82bc38
Fix path
xuzhen Jun 8, 2024
3c5e6b3
bump version
xuzhen Jun 9, 2024
6559a53
Fix SyntaxWarning
xuzhen Jun 9, 2024
a95255c
Improve code
xuzhen Oct 20, 2025
e9fbbb6
Fix _NET_WM_STRUT & _NET_WM_STRUT_PARTIAL with scaling
xuzhen Oct 20, 2025
e5b98f4
Use register_object_with_closures2 if possible
xuzhen Oct 20, 2025
22d7ac8
Use hasattr
xuzhen Oct 21, 2025
3df4db3
Fix mouse position detecting
xuzhen Dec 4, 2025
8b7024d
Better scaling effects for popup window
xuzhen Dec 7, 2025
a99c1f2
Improve scaling visual effect for group icon
xuzhen Dec 7, 2025
8fa71e0
Handle scaling factor change events
xuzhen Dec 8, 2025
d0b9a09
Fix crash
xuzhen Dec 10, 2025
e923c96
Fix LockedPopup position
xuzhen Dec 10, 2025
18f5287
Fix preview size for minimized window
xuzhen Dec 11, 2025
82b6cb4
Fix icon source
xuzhen Dec 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
# DockbarX
### Version 1.0-beta2
### Version 1.0-beta4

## About DockbarX
The gtk3/python3 version of DockbarX is a lightweight taskbar / panel replacement for Linux which works as a stand-alone dock (called DockX), as a Xfce4 panel applet[^1] or a matepanel applet. DockbarX is a fork of dockbar made by Aleksey Shaferov. DockbarX branch is developed by Matias Särs.
The gtk3/python3 version of DockbarX is a lightweight taskbar / panel replacement for Linux which works as a stand-alone dock (called DockX), as a [Xfce4 panel plugin](https://github.com/xuzhen/xfce4-dockbarx-plugin), as a [MATE panel applet](https://github.com/xuzhen/dockbarx-mate-applet) or as a [LXQt panel plugin](https://github.com/xuzhen/dockbarx-lxqt-plugin). DockbarX is a fork of dockbar made by Aleksey Shaferov. DockbarX branch is developed by Matias Särs.

DockbarX is free software and is licensed under GPL3.

## Install in Ubuntu 18.04+ from PPA
## Install in Ubuntu from PPA
The main DockbarX PPA is not maintained for the moment. You can use Xu Zhen's unofficial DockbarX PPA instead. To add the PPA and install the application in Ubuntu (and derivatives), use the following commands:

```
sudo add-apt-repository ppa:xuzhen666/dockbarx
sudo apt-get update
sudo apt-get install dockbarx
sudo apt update
sudo apt install dockbarx
```

If you want to use dockbarx as a Xfce panel applet you also need this command
If you want to use dockbarx as a panel plugin you also need these commands

```
sudo apt-get install xfce4-dockbarx-plugin
```

If you want to use dockbarx as a MATE panel applet you also need this command
```
sudo apt-get install dockbarx-mate-panel-applet
sudo apt install xfce4-dockbarx-plugin # for Xfce4 panel
sudo apt install dockbarx-mate-panel-applet # for MATE panel
sudo apt install dockbarx-lxqt-plugin # for LXQt panel
```

To get more themes for DockbarX and DockX use this command

```
sudo apt-get install dockbarx-themes-extra
sudo apt install dockbarx-themes-extra
```

## Install in archlinux
Expand All @@ -45,12 +42,17 @@ https://aur.archlinux.org/packages/xfce4-dockbarx-plugin/
## Manual Installation

1. Following dependencies needs to be installed (many of them might be installed already on your system):
- gir1.2-gtk-3.0 (>= 3.22), gir1.2-glib-2.0 (>= 1.40), gir1.2-keybinder-3.0, gir1.2-pango-1.0, gir1.2-wnck-3.0, python3-cairo (>= 1.11.0), python3-dbus, python3-distutils, python3-gi, python3-gi-cairo, python3-pil, python3-polib, python3-xdg and python3-xlib.
- gir1.2-gtk-3.0 (>= 3.22), gir1.2-glib-2.0 (>= 1.40), gir1.2-keybinder-3.0, gir1.2-pango-1.0, gir1.2-wnck-3.0, python3 (>= 3.5), python3-cairo (>= 1.11.0), python3-dbus, python3-gi, python3-gi-cairo, python3-pil, python3-xdg and python3-xlib.
- (Only for build) python3-pip, python3-polib, python3-setuptools
- (Optional) gir1.2-zeitgeist-2.0 and zeitgeist, to access latest and most used documents.
- (Optional) indicator-application or ayatana-indicator-application, to use the appindicator applet with DockX
- (Optional) python3-pyudev (>= 0.15), to use the battery status applet with DockX
- (Optional) python3-lxml, to use the settings migrating tool
2. Extract dockbarx. Change directory to where you extracted dockbarx and run the setup.py install `$ sudo ./setup.py install`
2. Extract dockbarx. Change directory to where you extracted dockbarx and run the following commands:
```
sudo pip install .
sudo glib-compile-schemas /usr/local/share/glib-2.0/schemas/
```

## Usage
To run DockbarX as a stand alone dock use the command `dockx`.
Expand Down Expand Up @@ -118,5 +120,3 @@ A: Backup: ```dconf dump /org/dockbarx/ > dockbarx.xml```\

A: Dump the preferences from GConf database ```gconftool --dump /apps/dockbarx > dockbarx.xml```, run the migrating tool ```dbx_migrate_settings dockbarx.xml```, and move the application folder from ~/.dockbarx to ~/.local/share/dockbarx

[^1]: Using [xfce-dockbarx-plugin] (https://github.com/M7S/xfce4-dockbarx-plugin/tree/pygi-python3)

3 changes: 3 additions & 0 deletions applets/Creating applets
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
An applet for Dockbarx stand alone dock needs to be a text file named %name%.applet, where %name% can be whatever you want. It doesn't have to be the same as the name of the directory you specify within the file.

See hello_world for a simple example or clock for a more complex one.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from gi.repository import Pango
import dbus
import pyudev # >= 0.15
from xml.etree import ElementTree
from dockbarx.applets import DockXApplet, DockXAppletDialog
import dockbarx.i18n
_ = dockbarx.i18n.language.gettext
Expand Down Expand Up @@ -404,8 +405,11 @@ class SystemdUtils(GObject.GObject):
BUS_NAME = "org.freedesktop.login1"
LOGIN_PATH = "/org/freedesktop/login1"
LOGIN_IFNAME = "org.freedesktop.login1.Manager"
SESSION_PATH = "/org/freedesktop/login1/session/self"
SESSION_ROOT_PATH = "/org/freedesktop/login1/session"
SESSION_PATH_NEW = "/org/freedesktop/login1/session/auto"
SESSION_PATH_OLD = "/org/freedesktop/login1/session/self"
SESSION_IFNAME = "org.freedesktop.login1.Session"
INTROSPECTABLE_IFNAME = "org.freedesktop.DBus.Introspectable"

__gsignals__ = {
"error": (GObject.SignalFlags.RUN_FIRST, None, (str,))
Expand All @@ -425,7 +429,18 @@ def __init__(self):
self.__session_iface = None
try:
if self.__bus is not None:
session_object = self.__bus.get_object(self.BUS_NAME, self.SESSION_PATH)
use_old_path = True
session_object = self.__bus.get_object(self.BUS_NAME, self.SESSION_ROOT_PATH)
iface = dbus.Interface(session_object, self.INTROSPECTABLE_IFNAME)
xml_string = iface.Introspect()
for child in ElementTree.fromstring(xml_string):
if child.tag != "node":
continue
child_path = "/".join((self.SESSION_ROOT_PATH, child.attrib["name"]))
if child_path == self.SESSION_PATH_NEW:
use_old_path = False
break
session_object = self.__bus.get_object(self.BUS_NAME, self.SESSION_PATH_OLD if use_old_path else self.SESSION_PATH_NEW)
self.__session_iface = dbus.Interface(session_object, self.SESSION_IFNAME)
except dbus.exceptions.DBusException as exception:
self.emit("error", exception.__str__())
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,9 @@ def get_namebar_homedir():
global namebar_appdir
if namebar_appdir is not None:
return namebar_appdir
homedir = os.environ['HOME']
default = os.path.join(homedir, '.local', 'share')
namebar_appdir = os.path.join(
os.getenv('XDG_DATA_HOME', default),
'namebar'
)
homedir = os.environ.get("XDG_DATA_HOME", os.environ.get("HOME", os.path.expanduser('~')))
namebar_appdir = os.path.join(homedir, '.local', 'share', "namebar")

"""
Migration Path
From "$HOME/.namebar" to "${XDG_DATA_HOME:-$HOME/.local/share}/namebar"
Expand All @@ -52,9 +49,7 @@ def get_namebar_homedir():
try:
os.rename(old_appdir, namebar_appdir)
except OSError:
sys.stderr.write('Could not move dir "%s" to "%s". \
Move the contents of "%s" to "%s" manually \
and then remove the first location.'
sys.stderr.write('Could not move dir "%s" to "%s". Move the contents of "%s" to "%s" manually and then remove the first location.\n'
% (old_appdir, namebar_appdir, old_appdir, namebar_appdir))
"""
End Migration Path
Expand Down Expand Up @@ -453,7 +448,7 @@ def find_themes(self):
# a theme can be loaded
themes = {}
theme_paths = []
dirs = [os.path.join(os.path.dirname(__file__), "namebar_themes"),
dirs = [os.path.join(os.path.dirname(__file__), "themes"),
os.path.join(get_namebar_homedir(), "themes")]
for dir in dirs:
if os.path.exists(dir):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@
import dockbarx.i18n
_ = dockbarx.i18n.language.gettext

from pathlib import Path
file = Path(__file__).resolve()
parent, root = file.parent, file.parents[1]
sys.path.append(str(root))
from applets.namebar_common import get_namebar_homedir, create_context_menu, PrefDialog, Theme
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from namebar.namebar_common import get_namebar_homedir, create_context_menu, PrefDialog, Theme

try:
action_minimize = Wnck.WindowType.ACTION_MINIMIZE
Expand Down Expand Up @@ -149,7 +146,7 @@ def find_themes(self):
# a theme can be loaded
themes = {}
theme_paths = []
dirs = [os.path.join(os.path.dirname(__file__), "namebar_themes"),
dirs = [os.path.join(os.path.dirname(__file__), "themes"),
os.path.join(get_namebar_homedir(), "themes")]
for dir in dirs:
if os.path.exists(dir):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@
from tarfile import open as taropen
from dockbarx.applets import DockXApplet

from pathlib import Path
file = Path(__file__).resolve()
parent, root = file.parent, file.parents[1]
sys.path.append(str(root))
from applets.namebar_common import create_context_menu, PrefDialog
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from namebar.namebar_common import create_context_menu, PrefDialog

class WindowTitleApplet(DockXApplet):
def __init__(self, dbx_dict):
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
11 changes: 0 additions & 11 deletions dockbarx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,2 @@
#!/usr/bin/python3

__all__ = ["dockbar",
"groupbutton",
"windowbutton",
"cairowidgets",
"icon_factory",
"theme",
"common",
"i18n",
"log",
"mediabuttons",
"zg"]
50 changes: 27 additions & 23 deletions dockbarx/applets.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
from gi.repository import Gtk
from gi.repository import Gdk
import os
import imp
import importlib.util
import dbus
import weakref
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
from .log import logger
from .common import get_app_homedir, Globals
from .common import Globals
from .dirutils import get_app_dirs
from . import i18n
_ = i18n.language.gettext

Expand Down Expand Up @@ -128,29 +129,30 @@ def __init__(self):
self.globals = Globals()

def find_applets(self):
# Reads the applets from /usr/share/dockbarx/applets and
# ${XDG_DATA_HOME:-$HOME/.local/share}/dockbarx/applets
# Reads the applets from DATA_DIRS/dockbarx/applets
# and returns a dict of the applets file names and paths so that a
# applet can be loaded.
self.applets = {}
home_folder = os.path.expanduser("~")
applets_folder = os.path.join(get_app_homedir(), "applets")
dirs = ["/usr/share/dockbarx/applets", applets_folder]
for dir in dirs:
if not(os.path.exists(dir) and os.path.isdir(dir)):
continue
for f in os.listdir(dir):
name, ext = os.path.splitext(os.path.split(f)[-1])
if not(ext.lower() == ".applet"):
continue
path = os.path.join(dir, f)
applet, err = self.read_applet_file(path)
if err is not None:
logger.debug("Error: Did not load applet from %s: %s" % (path, err))
continue
name = applet["name"]
applet["dir"] = dir
self.applets[name] = applet
app_dirs = get_app_dirs()
applets_dirs = [ os.path.join(d, "applets") for d in app_dirs ]
for d in applets_dirs:
num_sep = d.count(os.path.sep)
for root, dirs, files in os.walk(d):
for f in files:
name, ext = os.path.splitext(os.path.basename(f))
if ext.lower() != ".applet":
continue
path = os.path.join(root, f)
applet, err = self.read_applet_file(path)
if err is not None:
logger.debug("Error: Did not load applet from %s: %s" % (path, err))
continue
name = applet["name"]
if name not in self.applets:
applet["dir"] = root
self.applets[name] = applet
if num_sep + 1 <= root.count(os.path.sep):
del dirs[:]

def read_applet_file(self, path):
try:
Expand Down Expand Up @@ -213,7 +215,9 @@ def get(self, name):
iname, ext = os.path.splitext(os.path.split(e)[-1])
path = os.path.join(self.applets[name]["dir"], e)
try:
applet = imp.load_source(iname, path)
spec = importlib.util.spec_from_file_location(iname, path)
applet = importlib.util.module_from_spec(spec)
spec.loader.exec_module(applet)
except:
message = "Error: Could not load applet from %s. " % path
message += "Could not import the script."
Expand Down
Loading