答案1
总结: Nautilus 用途Gio 的 GDrive、GVolume 和 GVolumeMonitor 接口来获取特定设备对应的图标。
Gio 的 API、驱动器和图标
Gio 有一组类,允许应用程序读取可用的驱动器和卷,以及与它们关联的图标(这是 API 已经处理的事情)。图标作为Gio Icon
类型返回,因此应用程序不必特别知道图标的位置,但是始终可以通过名称或完整路径请求图标。您将看到两种类型的图标,“符号”和自定义,“符号”图标是标准的。如果仔细查看,/usr/share/icons
他们会发现有很多图标以 结尾-symbolic
,存储在多个主题文件夹中(标准 Adwaita、Humanity 和 Oxygen 就是很好的例子),通常是.svg
文件。在问题的屏幕截图中,硬盘驱动器的图标是drive-harddisk-symbolic
和drive-removable-media-usb
。当用户切换桌面主题时,应用程序不必查找图标的完整路径 - 只要drive-harddisk-symbolic
主题文件夹中有图标,Gio 就会找到它并返回到应用程序。我怎么知道这一切的?我已经将相同的图标用于我自己的U盘指示灯(尽管我的方法与 Nautilus 不同)。
Nautilus 如何使用 Gio 的 API
通过阅读 Nautilus 源代码,具体来说nautilusgtkplacesview.c代码中,必要部分如下:
Nautilus 添加驱动器和卷(想想分区)。添加驱动器函数,它使用 Gio 的
g_drive_get_volumes (drive)
函数获取特定驱动器上的卷,并将该信息传递给 Nautilus 的add_volume()
函数。Gio 的g_volume_monitor_get_volumes()
在第 1139 行和复制代码1164 获取可能不与驱动器关联的卷(例如 ftp 或网络共享),但相同的信息将用于add_volume()
功能和add_mount()
。查询图标以获取挂载点和卷: 内部
add_volume()
功能,Gio'sg_volume_get_icon()
功能获取类型的图标GIcon
。在add_mount()
Gio 的g_mount_get_icon()
也是 类型GIcon
。在这两种情况下,图标与其他信息都组合成 类型的对象。NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW
并传递给insert_row()
函数插入到位置容器中的行:您在示例中展示的 Places 菜单实际上是 Gtk 基本容器类型之一 - Gtk Box。该框内有用于子部分的 ListBox - 这就是为什么所有用户的文件夹、驱动器和卷以及书签都有分隔符。Nautilus 创建一个从 Gtk Box 类型扩展的对象,
G_DEFINE_TYPE_WITH_PRIVATE (NautilusGtkPlacesView, nautilus_gtk_places_view, GTK_TYPE_BOX)
。G_DEFINE_TYPE_WITH_PRIVATE 是另一个标准函数,正如您在 Nautilus 的定义中看到的那样,最后一项定义了GTK_TYPE_BOX
- 父对象。.c
在第 50 行,NautilusGtkPlacesViewPrivate
定义了结构,其中包括指向 ListView 小部件的指针。这就是对象的实际内容。现在,
insert_row()
功能获取该类型的实例(NautilusGtkPlacesView *view
),将整个信号束连接到它作为参数接收的行项,并使用gtk_container_add
将所有信息(连同图标)插入到 Nautilus Places 对象的 ListBox 小部件中。
冗长的解释,在图表中可能看起来更简单,但这都是用 C 语言编写的。让我们尝试用 Python 自己做一些更简单、更容易的事情。
Python 示例
这是一个简单的窗口,利用 Gio 的 Drive 界面创建按钮,其中的图标与所连接的驱动器相对应。这过于简单,需要完善和内联注释
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()
更简单的方法
当然,现在你不必重新发明轮子了。Gtk 提供了PlacesSidebar
小部件。
#!/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()
旁注:Nautilus 也有一个头文件鹦鹉螺图标名称.h定义带NAUTILUS_
前缀的常量,例如
#define NAUTILUS_ICON_FILESYSTEM "drive-harddisk-symbolic"
可能是为了开发人员/代码的一致性,而不是必须依赖查找实际的图标名称。唯一使用该特定定义的地方是get_icon
功能,讽刺的是,该功能不用于地点侧栏,而是用于内部路径栏更新函数。想想看,对吧?¯\_(ツ)_/¯