This is the sidebar of my nautilus window. You can see two different icons - one for the USB sticks and one for drives.
How does nautilus determine which gets a hard drive icon and which gets a flash drive one?
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It only takes a minute to sign up.Sign up to join this community
TL;DR: Nautilus uses Gio's GDrive,GVolume, and GVolumeMonitor interfaces to obtain icons corresponding to particular device.
Gio has set of classes which allow applications to read available drives and volumes, and what icons are associated to them (and that's something API already handles). The icons are returned as
Gio Icon type, so applications don't particularly have to know where the icon is located, however requesting an icon by name or by full path is always possible. There's two types of icons you will see, "symbolic" and custom, and "symbolic" icons are sort of standard. If one looks through
/usr/share/icons they'll find there are lots of icons that end with
-symbolic in their name, stored across multiple theme folders (standard Adwaita, Humanity, and Oxygen are good examples) , typically
.svg files. In the screenshot in the question, the icon for hard drive is
drive-removable-media-usb. When user switches desktop theme, the application doesn't have to look up full path to the icon - so long as there is
drive-harddisk-symbolic icon in the theme folder, the Gio will find it and return to the application. How do I know all this ? I've used those same icons for my own UDisks Indicator (though my approach differs from what Nautilus does).
From reading Nautilus source code, specifically nautilusgtkplacesview.c code, essential parts are the following:
Nautilus adds drives and volumes (think partitions). There's add_drive function, which uses Gio's
g_drive_get_volumes (drive) function to get volumes on specific drive, and passes that information to Nautilus's
add_volume() function. Gio's
g_volume_monitor_get_volumes() on lines 1139 and g_volume_monitor_get_mounts() 1164 fetches volumes that may not be associated with a drive (such as ftp or network shares), but that same information goes to
add_volume() function and
Icons are queried for mountpoints and volumes: Inside
add_volume() function, Gio's
g_volume_get_icon() function fetches the icon of type
g_mount_get_icon() also of type
GIcon. In both cases, icon along with other information are combined into an object of type
NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW and passed to
Rows inserted into Places container: The Places menu that you show in the example actually is one of the basic Gtk container types - Gtk Box. Inside that box there's ListBox for subsections - that's why there's separators for all user's folders, drives and volumes, and bookmarks. Nautilus creates an object which extends from Gtk Box type,
G_DEFINE_TYPE_WITH_PRIVATE (NautilusGtkPlacesView, nautilus_gtk_places_view, GTK_TYPE_BOX)
. The G_DEFINE_TYPE_WITH_PRIVATE is another standard function , and as you can see in Nautilus's definition the last item defines
GTK_TYPE_BOX - parent object. In the
.c file on line 50,
NautilusGtkPlacesViewPrivate structure is defined, which among other things has pointer to ListView widget. That's the actual contents of the object.
insert_row() function takes instance of that type (
NautilusGtkPlacesView *view), connects whole bunch of signals to the row item it received as argument, and using
gtk_container_add inserts all the information ( along with the icon ) into the ListBox widget of the Nautilus Places object.
Lengthy explanation and probably looks simpler in a diagram, but also that's all written in C. Let's try to make something ourselves in Python which is by far simpler and easier.
Here's a simple window that utilizes Gio's Drive interface to create buttons with icons corresponding to the attached drives. This is overly simplistic and needs polish, and inline-comments
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gio,Gtk class drives_window(Gtk.Window): def __init__(self): super().__init__(title="Foobar") self.volume_monitor = Gio.VolumeMonitor.get() # We'll use Gtk.Box to hold all the buttons corresponding to each drive # although we haven't connected the buttons to any other function # so clicking on the does nothing self.box = Gtk.Box() for drive in self.volume_monitor.get_connected_drives(): button = Gtk.Button() button.set_label(drive.get_name()) button.set_always_show_image(True) icon = drive.get_symbolic_icon() # buttons need Gtk.Image widget, but .get_symbolic_icon() returns Gio.ThemedIcon # so we create new Gtk.Image using .new_From_gicon() function # Gtk.IconSize.BUTTON is a constant button.set_image(Gtk.Image.new_from_gicon(icon,Gtk.IconSize.BUTTON)) self.box.pack_start(button,True,True,0) # add the box container to this window self.add(self.box) window = drives_window() window.connect("destroy",Gtk.main_quit) window.show_all() Gtk.main()
Of course nowadays you don't have to reinvent the wheel. Gtk provides
#!/usr/bin/env python3 from gi.repository import Gtk,Gio,GLib w = Gtk.Window() b1 = Gtk.Box() p = Gtk.PlacesSidebar() b1.pack_start(p,True,True,0) b1.pack_start(Gtk.Button("Hello World"),True,True,0) b1.pack_start(Gtk.Button("Hello World 2"),True,True,0) w.add(b1) w..connect("destroy",Gtk.main_quit) w.show_all() Gtk.main()
Sidenote: Nautilus also has a header file nautilus-icon-names.h which defines constants with
NAUTILUS_ prefix, such as
#define NAUTILUS_ICON_FILESYSTEM "drive-harddisk-symbolic"
probably for developer/code consistency, instead of having to rely on looking up actual icon names. The only place where that particular definition is used is in
get_icon function, and that function ironically is not used for Places side bar but is used inside pathbar updating function. Go figure,right ?