Как Наутилус решает, какую иконку использовать?
Это боковая панель моего окна наутилуса. Вы можете увидеть два разных значка - один для флешек и один для накопителей.
https://i.s tack.imgur.com/KerHO.png
Как nautilus определяет, кто получает значок жесткого диска, а какой - флэш-накопитель?
1 ответ
TL; DR: Наутилус использует интерфейсы Gio GDrive,GVolume и GVolumeMonitor для получения значков, соответствующих конкретному устройству.
API, диски и иконки Gio
Gio имеет набор классов, которые позволяют приложениям читать доступные диски и тома, а также какие значки с ними связаны (и это то, что API уже обрабатывает). Значки возвращаются как Gio Icon
тип, поэтому приложениям не нужно особенно знать, где находится значок, однако запрос значка по имени или по полному пути всегда возможен. Вы увидите два типа значков: "символические" и пользовательские, и "символические" значки являются стандартными. Если смотреть /usr/share/icons
они найдут много значков, которые заканчиваются -symbolic
их названия хранятся в нескольких папках с темами (например, стандартные Adwaita, Humanity и Oxygen). .svg
файлы. На скриншоте в вопросе значок жесткого диска drive-harddisk-symbolic
а также drive-removable-media-usb
, Когда пользователь переключает тему рабочего стола, приложению не нужно искать полный путь к значку - пока есть drive-harddisk-symbolic
Значок в папке темы, Gio найдет его и вернется в приложение. Откуда я все это знаю? Я использовал те же значки для моего собственного индикатора UDisks (хотя мой подход отличается от того, что делает Nautilus).
Как Наутилус использует API Джио
При чтении исходного кода Nautilus, в частности кода nautilusgtkplacesview.c, важными частями являются следующие:
Наутилус добавляет диски и тома (думаю, разделы). Есть функция add_drive, которая использует Gio's
g_drive_get_volumes (drive)
функция, чтобы получить тома на конкретном диске, и передает эту информацию в Наутилусadd_volume()
функция. Гиоg_volume_monitor_get_volumes()
в строках 1139 и g_volume_monitor_get_mounts () 1164 выбирает тома, которые могут быть не связаны с диском (например, ftp или сетевые ресурсы), но эта же информация передаетсяadd_volume()
функция иadd_mount()
,Значки запрашиваются для точек монтирования и томов: Внутри
add_volume()
функция, Гиоg_volume_get_icon()
функция выбирает значок типаGIcon
, внутриadd_mount()
Гиоg_mount_get_icon()
также типаGIcon
, В обоих случаях значок вместе с другой информацией объединяются в объект типаNAUTILUS_TYPE_GTK_PLACES_VIEW_ROW
и перешел кinsert_row()
функцияСтроки, вставленные в контейнер Places: меню Places, которое вы показываете в примере, на самом деле является одним из основных типов контейнеров Gtk - Gtk Box. Внутри этого поля есть ListBox для подразделов - вот почему есть разделители для всех пользовательских папок, дисков и томов, а также закладок. Наутилус создает объект, который происходит от типа Gtk Box,
G_DEFINE_TYPE_WITH_PRIVATE (NautilusGtkPlacesView, nautilus_gtk_places_view, GTK_TYPE_BOX)
, G_DEFINE_TYPE_WITH_PRIVATE - это еще одна стандартная функция, и, как видно из определения Наутилуса, последний элемент определяетGTK_TYPE_BOX
родительский объект. в.c
файл в строке 50,NautilusGtkPlacesViewPrivate
определена структура, которая помимо прочего имеет указатель на виджет ListView. Это фактическое содержание объекта.Сейчас,
insert_row()
функция принимает экземпляр этого типа (NautilusGtkPlacesView *view
), связывает всю группу сигналов с элементом строки, полученным в качестве аргумента, и используетgtk_container_add
вставляет всю информацию (вместе со значком) в виджет ListBox объекта Nautilus Places.
Длинное объяснение и, вероятно, выглядит проще на диаграмме, но также все это написано на 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()
Более простой подход
Конечно, в настоящее время вам не нужно изобретать велосипед. ГТК обеспечивает 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-icon-names.h, который определяет константы с NAUTILUS_
префикс, такой как
#define NAUTILUS_ICON_FILESYSTEM "drive-harddisk-symbolic"
вероятно, для согласованности между разработчиком и кодом, вместо того, чтобы полагаться на поиск реальных имен значков. Единственное место, где это конкретное определение используется в get_icon
функция, и эта функция по иронии судьбы не используется для боковой панели Places, но используется внутри функции обновления панели пути. Пойди разберись, верно? ¯\_(ツ)_/¯