Журнал LinuxFormat - перейти на главную

LXF112:KDE4

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
м (Новая: {{Цикл/KDE4}} == Солидная аппаратура == : ''ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая ...)
 
 
Строка 3: Строка 3:
 
: ''ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая яркая, но далеко не единственная подсистема KDE4. В качестве финального аккорда '''Андрей Боровский''' припас для вас слой аппаратной абстракции под названием Solid.''
 
: ''ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая яркая, но далеко не единственная подсистема KDE4. В качестве финального аккорда '''Андрей Боровский''' припас для вас слой аппаратной абстракции под названием Solid.''
  
Не так давно один известный в Рунете писатель задал сооб-
+
Не так давно один известный в Рунете писатель задал сообществу программистов Linux вопрос: почему новые версии
ществу программистов Linux вопрос: почему новые версии
+
 
KDE все более и более громоздки и прожорливы? Ведь
 
KDE все более и более громоздки и прожорливы? Ведь
 
улучшение программы предполагает, в том числе, оптимизацию ее
 
улучшение программы предполагает, в том числе, оптимизацию ее
 
кода... Можно, конечно, ответить – мол, новые версии оболочки
 
кода... Можно, конечно, ответить – мол, новые версии оболочки
удовлетворяют растущим потребностям пользователей. Что, я пола-
+
удовлетворяют растущим потребностям пользователей. Что, я полагаю, будет лукавством: не так уж сильно эти потребности и выросли.
гаю, будет лукавством: не так уж сильно эти потребности и выросли.
+
Вспомните, когда в последний раз менялся интерфейс самой популярной текстовой оболочки – Midnight Commander? Следует признать, что в стремлении к свободному творчеству, которое и привлекает многих программистов в мир открытого ПО, разработчики
Вспомните, когда в последний раз менялся интерфейс самой попу-
+
лярной текстовой оболочки – Midnight Commander? Следует при-
+
знать, что в стремлении к свободному творчеству, которое и при-
+
влекает многих программистов в мир открытого ПО, разработчики
+
 
будут охотнее добавлять новые функции, нежели оптимизировать
 
будут охотнее добавлять новые функции, нежели оптимизировать
 
старые. Вот почему KDE постоянно растет и вширь, и вглубь, и, в
 
старые. Вот почему KDE постоянно растет и вширь, и вглубь, и, в
 
частности, обзавелся системой управления устройствами Solid.
 
частности, обзавелся системой управления устройствами Solid.
 +
 
Зачем нужен Solid? Разумеется, его смысл и польза заключаются
 
Зачем нужен Solid? Разумеется, его смысл и польза заключаются
 
вовсе не в том, чтобы удовлетворять амбиции KDE-программистов.
 
вовсе не в том, чтобы удовлетворять амбиции KDE-программистов.
Строка 23: Строка 19:
 
средства управления оборудованием. Solid представляет собой
 
средства управления оборудованием. Solid представляет собой
 
универсальный способ взаимодействия с устройствами всюду, где
 
универсальный способ взаимодействия с устройствами всюду, где
работает KDE. В идеале, система Solid должна сократить количе-
+
работает KDE. В идеале, система Solid должна сократить количество непереносимого кода в вашем приложении (хотя свести его к
ство непереносимого кода в вашем приложении (хотя свести его к
+
 
нулю все равно не удастся). В этом и заключается ответ на вопрос
 
нулю все равно не удастся). В этом и заключается ответ на вопрос
 
о том, не лучше ли использовать HAL (на котором основана система
 
о том, не лучше ли использовать HAL (на котором основана система
 
Solid) напрямую. Linux HAL поддерживается не на всех платформах,
 
Solid) напрямую. Linux HAL поддерживается не на всех платформах,
 
которые намерен завоевать KDE. Кроме того, Linux HAL базируется
 
которые намерен завоевать KDE. Кроме того, Linux HAL базируется
на Dbus, и эффективная работа с ним предполагает хорошее зна-
+
на Dbus, и эффективная работа с ним предполагает хорошее знание этой своеобразной системы. В процессе же общения с Solid
ние этой своеобразной системы. В процессе же общения с Solid
+
 
мы находимся в знакомой среде с сигналами, слотами и объектной
 
мы находимся в знакомой среде с сигналами, слотами и объектной
 
моделью Qt/KDE.
 
моделью Qt/KDE.
Исследуем дерево устройств
+
 
 +
=== Исследуем дерево устройств ===
 
Центральным классом Solid является Solid::Device (все классы
 
Центральным классом Solid является Solid::Device (все классы
 
Solid объявлены в пространстве имен Solid), который представляет
 
Solid объявлены в пространстве имен Solid), который представляет
устройство. Дать четкое определение того, что является устрой-
+
устройство. Дать четкое определение того, что является устройством с точки зрения Solid, не так-то просто. Это и процессор, и
ством с точки зрения Solid, не так-то просто. Это и процессор, и
+
его загружаемый микрокод, и видеокамера, и ее аудиоподсистема, оптический привод и диск в нем, винчестер и каждый его раздел... Если такая модель кажется вам запутанной, вспомните, что
его загружаемый микрокод, и видеокамера, и ее аудиоподсисте-
+
в других системах дело обстоит не лучше (в Диспетчер оборудования Windows давно заглядывали?). Устройства Solid образуют
ма, оптический привод и диск в нем, винчестер и каждый его раз-
+
дел... Если такая модель кажется вам запутанной, вспомните, что
+
в других системах дело обстоит не лучше (в Диспетчер оборудо-
+
вания Windows давно заглядывали?). Устройства Solid образуют
+
 
иерархию, корнем которой является, конечно, Computer. Чтобы
 
иерархию, корнем которой является, конечно, Computer. Чтобы
 
еще больше вас запутать – каждое устройство предоставляет один
 
еще больше вас запутать – каждое устройство предоставляет один
 
или несколько интерфейсов, представляемых потомками класса
 
или несколько интерфейсов, представляемых потомками класса
 
Solid::DeviceInterface.
 
Solid::DeviceInterface.
Пока вы еще не успели разочароваться в Solid, поспешим уточ-
+
 
нить, что может и чего не может эта система. Solid позволяет полу-
+
Пока вы еще не успели разочароваться в Solid, поспешим уточнить, что может и чего не может эта система. Solid позволяет получать различные сведения об устройствах, в том числе отслеживать
чать различные сведения об устройствах, в том числе отслеживать
+
их состояние в режиме реального времени. При этом Solid не предоставляет собственных средств для обмена данными с устройствами. С помощью Solid вы можете получить имя файла, соответствующего устройству, его идентификатор или другую информацию, необходимую для того, чтобы наладить обмен информацией,
их состояние в режиме реального времени. При этом Solid не пре-
+
но его придется выполнять с помощью интерфейса, предоставляемого операционной системой. Например, все известные Solid
доставляет собственных средств для обмена данными с устрой-
+
ствами. С помощью Solid вы можете получить имя файла, соответ-
+
ствующего устройству, его идентификатор или другую информа-
+
цию, необходимую для того, чтобы наладить обмен информацией,
+
но его придется выполнять с помощью интерфейса, предостав-
+
ляемого операционной системой. Например, все известные Solid
+
 
устройства, способные записывать и воспроизводить аудиоданные,
 
устройства, способные записывать и воспроизводить аудиоданные,
 
предоставляют интерфейс Solid::AudioInterface. У него есть метод
 
предоставляют интерфейс Solid::AudioInterface. У него есть метод
 
driverHandle(), который возвращает значение типа QVariant. Для
 
driverHandle(), который возвращает значение типа QVariant. Для
устройств, которые обслуживают драйверы ALSA, фактическим зна-
+
устройств, которые обслуживают драйверы ALSA, фактическим значением driverHandle() будет запись, содержащая имя звуковой карты (в системе ALSA) и номера первичного и вторичного устройств.
чением driverHandle() будет запись, содержащая имя звуковой кар-
+
ты (в системе ALSA) и номера первичного и вторичного устройств.
+
 
Если устройство работает под управлением драйвера OSS, метод
 
Если устройство работает под управлением драйвера OSS, метод
 
driverHandle() вернет строку с именем файла устройства.
 
driverHandle() вернет строку с именем файла устройства.
Практическое знакомство с Solid мы начнем с программы обзо-
+
 
ра всех устройств, присутствующих в системе. Приложение devtree,
+
[[Изображение:LXF112 70 1.png|thumb|200px|(Рис. 1) Программа devtree – ее-то мы сегодня и напишем.]]
чьи исходные тексты вы найдете на диске, покажет вам полный спи-
+
Практическое знакомство с Solid мы начнем с программы обзора всех устройств, присутствующих в системе. Приложение devtree,
сок устройств с учетом их иерархии (рис. 1).
+
чьи исходные тексты вы найдете на диске, покажет вам полный список устройств с учетом их иерархии (рис. 1).
  
 
Доступ к полному списку устройств, поддерживаемых Solid, можно
 
Доступ к полному списку устройств, поддерживаемых Solid, можно
Строка 78: Строка 61:
 
}</source>
 
}</source>
 
позволит перебрать все элементы данного списка (роль итератора играет переменная device). Однако последовательный перебор
 
позволит перебрать все элементы данного списка (роль итератора играет переменная device). Однако последовательный перебор
перечня устройств не так уж и интересен. Дело в том, что расположе-
+
перечня устройств не так уж и интересен. Дело в том, что расположение устройств в списке Solid::Device::allDevices() не учитывает иерархических отношений между ними. В программе devtree мы используем рекурсивный метод заполнения виджета QTreeWidget элементами
ние устройств в списке Solid::Device::allDevices() не учитывает иерар-
+
хических отношений между ними. В программе devtree мы использу-
+
ем рекурсивный метод заполнения виджета QTreeWidget элементами
+
 
списка (он не самый эффективный, но самый простой – альтернативу
 
списка (он не самый эффективный, но самый простой – альтернативу
 
рассмотрим в конце статьи):
 
рассмотрим в конце статьи):
void devtreeView::enumDevices()
+
<source lang="cpp-qt">void devtreeView::enumDevices()
 
{
 
{
 
ui_devtreeview_base.treeWidget->setColumnCount(3);
 
ui_devtreeview_base.treeWidget->setColumnCount(3);
Строка 110: Строка 90:
 
}
 
}
 
}
 
}
}
+
}</source>
 
Один из наиболее часто используемых методов класса
 
Один из наиболее часто используемых методов класса
Solid::Device, udi(), возвращает уникальный идентификатор устрой-
+
Solid::Device, udi(), возвращает уникальный идентификатор устройства. В Linux, где Solid основан на FreeDesktop HAL, идентификатором устройства служит HAL-адрес устройства (строка вида “/org/freedestop/Hal/...”). Нам не обязательно разбираться в том, как
ства. В Linux, где Solid основан на FreeDesktop HAL, идентифика-
+
формируются эти адреса: важно знать, что именно они идентифицируют устройства. Метод parentUdi() класса Solid::Device возвращает идентификатор родительского устройства, а соответствующий ему объект класса Solid::Device можно получить с помощью
тором устройства служит HAL-адрес устройства (строка вида “/
+
org/freedestop/Hal/...”). Нам не обязательно разбираться в том, как
+
формируются эти адреса: важно знать, что именно они идентифи-
+
цируют устройства. Метод parentUdi() класса Solid::Device возвра-
+
щает идентификатор родительского устройства, а соответствую-
+
щий ему объект класса Solid::Device можно получить с помощью
+
 
метода Solid::Device::parent(). Для корневого устройства метод
 
метода Solid::Device::parent(). Для корневого устройства метод
parentUdi() возвращает пустую строку, чем мы и пользуемся в мето-
+
parentUdi() возвращает пустую строку, чем мы и пользуемся в методе enumDevices(). После того как корневое устройство найдено,
де enumDevices(). После того как корневое устройство найдено,
+
 
мы вызываем рекурсивно метод findChildren(), который добавляет
 
мы вызываем рекурсивно метод findChildren(), который добавляет
 
в виджет ui_devtreeview_base.treeWidget устройства, являющиеся
 
в виджет ui_devtreeview_base.treeWidget устройства, являющиеся
 
непосредственными потомками корневого. Для каждого найденного
 
непосредственными потомками корневого. Для каждого найденного
устройства наша программа отображает три элемента данных: иден-
+
устройства наша программа отображает три элемента данных: идентификатор устройства, наименование изготовителя (возвращается
тификатор устройства, наименование изготовителя (возвращается
+
методом Solid::Device::vendor()) и описание устройства (возвращается методом product()). У класса Solid::Device есть еще и метод
методом Solid::Device::vendor()) и описание устройства (возвраща-
+
icon(), который возвращает строку с именем пиктограммы устройства (пока что непустая строка возвращается только для корневого
ется методом product()). У класса Solid::Device есть еще и метод
+
icon(), который возвращает строку с именем пиктограммы устрой-
+
ства (пока что непустая строка возвращается только для корневого
+
 
устройства Computer).
 
устройства Computer).
Если бы система Solid позволяла только перечислить имею-
+
 
щиеся в наличии устройства, пользы от нее было бы не очень
+
Если бы система Solid позволяла только перечислить имеющиеся в наличии устройства, пользы от нее было бы не очень
много. Однако основная задача Solid заключается в том, что-
+
много. Однако основная задача Solid заключается в том, что-бы информировать систему о различных событиях, связанных с
бы информировать систему о различных событиях, связанных с
+
 
устройствами. Делается это, естественно, с помощью сигналов и
 
устройствами. Делается это, естественно, с помощью сигналов и
 
слотов. Сигналы, оповещающие систему о событиях, связанных с
 
слотов. Сигналы, оповещающие систему о событиях, связанных с
Строка 145: Строка 114:
 
и отключении устройств. Объект класса Solid::Networking::Notifier
 
и отключении устройств. Объект класса Solid::Networking::Notifier
 
сообщает о подключении и отключении системы от сети, а объект
 
сообщает о подключении и отключении системы от сети, а объект
класса Solid::PowerManagement::Notifier позволяет программе отсле-
+
класса Solid::PowerManagement::Notifier позволяет программе отслеживать режим работы системы питания. Особняком стоит класс
живать режим работы системы питания. Особняком стоит класс
+
 
WebcamWatcher, который выполняет функции Solid::DeviceNotifier
 
WebcamWatcher, который выполняет функции Solid::DeviceNotifier
 
исключительно для web-камер.
 
исключительно для web-камер.
 +
 
На первый взгляд может показаться, что возможности Solid в
 
На первый взгляд может показаться, что возможности Solid в
 
плане оповещения программ о событиях устройств крайне скромны,
 
плане оповещения программ о событиях устройств крайне скромны,
 
но это не так. Помните, что устройствами в Solid считаются самые
 
но это не так. Помните, что устройствами в Solid считаются самые
 
разные вещи. Объект класса Solid::DeviceNotifier может оповещать
 
разные вещи. Объект класса Solid::DeviceNotifier может оповещать
программу о таких событиях, как установка нового CD-диска в при-
+
программу о таких событиях, как установка нового CD-диска в привод, подключение USB-устройства или монтирование нового раздела файловой системы.
вод, подключение USB-устройства или монтирование нового разде-
+
 
ла файловой системы.
+
 
Вполне логично задействовать объект класса Solid::DeviceNotifier
 
Вполне логично задействовать объект класса Solid::DeviceNotifier
 
в программе devtree для обновления дерева устройств в случае
 
в программе devtree для обновления дерева устройств в случае
 
изменения оного. Система Solid предоставляет каждой программе
 
изменения оного. Система Solid предоставляет каждой программе
 
один объект класса Solid::DeviceNotifier. Для получения указателя
 
один объект класса Solid::DeviceNotifier. Для получения указателя
на него мы воспользуемся статическим методом Solid::DeviceNotif
+
на него мы воспользуемся статическим методом Solid::DeviceNotifier::instance():
ier::instance():
+
<source lang="cpp-qt">Solid::DeviceNotifier * dn = Solid::DeviceNotifier::instance();</source>
Solid::DeviceNotifier * dn = Solid::DeviceNotifier::instance();
+
 
Объект класса Solid::DeviceNotifier эмитирует два сигнала:
 
Объект класса Solid::DeviceNotifier эмитирует два сигнала:
 
deviceAdded() (добавлено новое устройство) и deviceRemoved()
 
deviceAdded() (добавлено новое устройство) и deviceRemoved()
Строка 169: Строка 136:
 
со слотом update(), который мы добавили в главный класс нашей
 
со слотом update(), который мы добавили в главный класс нашей
 
программы:
 
программы:
dn->connect(dn, SIGNAL(deviceAdded(const QString)), this,
+
<source lang="cpp-qt">dn->connect(dn, SIGNAL(deviceAdded(const QString)), this, SLOT(update()));
SLOT(update()));
+
dn->connect(dn, SIGNAL(deviceRemoved(const QString)), this, SLOT(update()));</source>
dn->connect(dn, SIGNAL(deviceRemoved(const QString)), this,
+
Слот update() не обрабатывает переданный ему параметр, а просто очищает виджет ui_devtreeview_base.treeWidget и заново строит
SLOT(update()));
+
Слот update() не обрабатывает переданный ему параметр, а про-
+
сто очищает виджет ui_devtreeview_base.treeWidget и заново строит
+
 
дерево устройств. Разумеется, такой подход нельзя назвать самым
 
дерево устройств. Разумеется, такой подход нельзя назвать самым
 
эффективным. Для ускорения работы программы можно было бы
 
эффективным. Для ускорения работы программы можно было бы
 
найти для добавленного/удаленного родительское устройство и
 
найти для добавленного/удаленного родительское устройство и
 
перестроить только соответствующую ему часть дерева.
 
перестроить только соответствующую ему часть дерева.
Для сборки программы devtree в файл CMakeLists.txt следу-
+
 
ет добавить данные о заголовочных файлах и библиотеках Solid.
+
Для сборки программы devtree в файл CMakeLists.txt следует добавить данные о заголовочных файлах и библиотеках Solid.
Вопреки тому, что говорит документация, все заголовочные фай-
+
Вопреки тому, что говорит документация, все заголовочные файлы Solid API хранятся в поддиректории solid стандартной директории include. Переменная CMake KDE4_INCLUDE_DIR уже содержит
лы Solid API хранятся в поддиректории solid стандартной директо-
+
рии include. Переменная CMake KDE4_INCLUDE_DIR уже содержит
+
 
ее имя. Единственное изменение, которое нужно внести в файл
 
ее имя. Единственное изменение, которое нужно внести в файл
 
CMakeLists.txt по сравнению со стандартным файлом приложения
 
CMakeLists.txt по сравнению со стандартным файлом приложения
Строка 189: Строка 151:
 
аргумента команды target_link_libraries(). Теперь программу devtree
 
аргумента команды target_link_libraries(). Теперь программу devtree
 
можно компилировать.
 
можно компилировать.
Интерфейсы устройств
+
 
 +
=== Интерфейсы устройств ===
 
В модели Solid объекты класса Solid::Device соответствуют общему
 
В модели Solid объекты класса Solid::Device соответствуют общему
понятию «устройство» и позволяют непосредственно получить толь-
+
понятию «устройство» и позволяют непосредственно получить только те данные, которые имеют смысл для всех устройств, независимо от их типа. Более подробную информацию о каждом устройстве
ко те данные, которые имеют смысл для всех устройств, независи-
+
мо от их типа. Более подробную информацию о каждом устройстве
+
 
можно извлечь с помощью интерфейсов, которые представлены в
 
можно извлечь с помощью интерфейсов, которые представлены в
 
Solid объектами класса Solid::DeviceInterface и его потомков. Каждое
 
Solid объектами класса Solid::DeviceInterface и его потомков. Каждое
устройство (то есть корректно созданный объект Solid::Device) пре-
+
устройство (то есть корректно созданный объект Solid::Device) предоставляет как минимум один интерфейс. Узнать, поддерживает ли
доставляет как минимум один интерфейс. Узнать, поддерживает ли
+
 
устройство определенный интерфейс, можно с помощью метода
 
устройство определенный интерфейс, можно с помощью метода
 
isDeviceInterface() класса Solid::Device. Его аргументом должно быть
 
isDeviceInterface() класса Solid::Device. Его аргументом должно быть
значение типа Solid::DeviceInterface::Type, указывающее тип требуе-
+
значение типа Solid::DeviceInterface::Type, указывающее тип требуемого интерфейса. Полный список типов интерфейсов можно найти
мого интерфейса. Полный список типов интерфейсов можно найти
+
 
в документации Solid и файле solid/deviceinterface.h. Например, тип
 
в документации Solid и файле solid/deviceinterface.h. Например, тип
 
Processor определяет интерфейс ЦП, Block – блочное устройство,
 
Processor определяет интерфейс ЦП, Block – блочное устройство,
 
StorageVolume – раздел на диске, Battery – аккумулятор. Учитывая
 
StorageVolume – раздел на диске, Battery – аккумулятор. Учитывая
структуру Solid, вы не должны удивляться, что среди поддержива-
+
структуру Solid, вы не должны удивляться, что среди поддерживаемых типов интерфейсов одновременно присутствуют интерфейсы
емых типов интерфейсов одновременно присутствуют интерфейсы
+
логических и физических устройств. Если объект Solid::Device поддерживает запрошенный интерфейс, метод isDeviceInterface() возвращает значение true.
логических и физических устройств. Если объект Solid::Device поддерживает запрошенный интерфейс, метод isDeviceInterface() воз-
+
 
вращает значение true.
+
После того как вы выяснили, поддерживает ли устройство некоторый интерфейс, вы, скорее всего, захотите получить объект, его
После того как вы выяснили, поддерживает ли устройство неко-
+
торый интерфейс, вы, скорее всего, захотите получить объект, его
+
 
реализующий. Сделать это можно с помощью метода Solid::Device:
 
реализующий. Сделать это можно с помощью метода Solid::Device:
 
:asDeviceInterface(), аргументом которого выступает тип требуемого
 
:asDeviceInterface(), аргументом которого выступает тип требуемого
интерфейса. В случае успеха метод возвращает указатель на объ-
+
интерфейса. В случае успеха метод возвращает указатель на объект класса, производного от Solid::DeviceInterface, соответствующего запрошенному интерфейсу. Имейте в виду, что не для каждого
ект класса, производного от Solid::DeviceInterface, соответствующе-
+
го запрошенному интерфейсу. Имейте в виду, что не для каждого
+
 
значения Solid::DeviceInterface::Type существует свой класс (так, по
 
значения Solid::DeviceInterface::Type существует свой класс (так, по
 
крайней мере, обстояло дело на момент написания статьи).
 
крайней мере, обстояло дело на момент написания статьи).
В приложениях, использующих Solid, довольно часто требует-
+
 
ся получить список устройств, поддерживающих определенный
+
В приложениях, использующих Solid, довольно часто требуется получить список устройств, поддерживающих определенный
интерфейс (вполне логично, что программа, которая обрабатыва-
+
интерфейс (вполне логично, что программа, которая обрабатывает изображение, поступающее с видеокамер, захочет иметь полный список устройств, поддерживающих интерфейс Camera, а не
ет изображение, поступающее с видеокамер, захочет иметь пол-
+
вообще всех устройств). Для этого можно было бы, конечно, перебрать полный список устройств системы (как мы делали выше),
ный список устройств, поддерживающих интерфейс Camera, а не
+
вообще всех устройств). Для этого можно было бы, конечно, пере-
+
брать полный список устройств системы (как мы делали выше),
+
 
проверяя, поддерживает ли каждое из них требуемый интерфейс,
 
проверяя, поддерживает ли каждое из них требуемый интерфейс,
 
однако Solid предоставляет нам более простой и эффективный
 
однако Solid предоставляет нам более простой и эффективный
 
способ. Класс Solid::Device экспортирует несколько статических
 
способ. Класс Solid::Device экспортирует несколько статических
методов, предназначенных для управления не конкретным устрой-
+
методов, предназначенных для управления не конкретным устройством, а всем списком устройств Solid (с одним из них, allDevices(),
ством, а всем списком устройств Solid (с одним из них, allDevices(),
+
 
мы уже знакомы). Статические методы Solid::Device::listFromType()
 
мы уже знакомы). Статические методы Solid::Device::listFromType()
 
и Solid::Device::listFromQuery() позволяют получить подмножество
 
и Solid::Device::listFromQuery() позволяют получить подмножество
списка устройств, заданное специальными признаками. Нас инте-
+
списка устройств, заданное специальными признаками. Нас интересует метод listFromType(), который возвращает список устройств,
ресует метод listFromType(), который возвращает список устройств,
+
 
поддерживающих определенный интерфейс (говоря «устройство»,
 
поддерживающих определенный интерфейс (говоря «устройство»,
 
я, разумеется, имею в виду объект класса Solid::Device). Вот как,
 
я, разумеется, имею в виду объект класса Solid::Device). Вот как,
 
например, мы можем получить список устройств, поддерживающих
 
например, мы можем получить список устройств, поддерживающих
 
интерфейс NetworkInterface:
 
интерфейс NetworkInterface:
QList<Solid::Device> devlist =
+
<source lang="cpp-qt">QList<Solid::Device> devlist =
Solid::Device::listFromType(Solid::DeviceInterface::NetworkInterface,
+
Solid::Device::listFromType(Solid::DeviceInterface::NetworkInterface, QString());</source>
QString());
+
Если во втором параметре listFromType() передать идентификатор устройства, в результирующий список войдут только сетевые
Если во втором параметре listFromType() передать идентифика-
+
устройства, являющиеся потомками заданного (пустая строка, разумеется, означает, что нас интересуют все сетевые устройства).
тор устройства, в результирующий список войдут только сетевые
+
 
устройства, являющиеся потомками заданного (пустая строка, раз-
+
=== Solid и Plasma ===
умеется, означает, что нас интересуют все сетевые устройства).
+
[[Изображение:LXF112 72 1.png|thumb|200px|Рис. 2. Плазмоид netstat — то, что нужно администратору.]]
Solid и Plasma
+
Работу с интерфейсами устройств мы продемонстрируем на примере динамического плазмоида. Как уже говорилось в [[LXF110:KDE4|LXF110]],
Работу с интерфейсами устройств мы продемонстрируем на при-
+
плазмоиды хорошо подходят для того, чтобы информировать пользователя о важных событиях, происходящих в системе. Solid, как
мере динамического плазмоида. Как уже говорилось в LXF110,
+
плазмоиды хорошо подходят для того, чтобы информировать поль-
+
зователя о важных событиях, происходящих в системе. Solid, как
+
 
мы уже видели, может служить источником такого рода событий.
 
мы уже видели, может служить источником такого рода событий.
  
Для передачи данных от Solid плазмоиду мы воспользуемся стан-
+
Для передачи данных от Solid плазмоиду мы воспользуемся стандартным механизмом поставщиков данных Plasma ([[LXF110:KDE4|LXF110]]). Наш
дартным механизмом поставщиков данных Plasma (LXF110). Наш
+
 
плазмоид будет информировать пользователя о состоянии сетевых
 
плазмоид будет информировать пользователя о состоянии сетевых
 
интерфейсов системы (рис. 2).
 
интерфейсов системы (рис. 2).
 +
 
Полные исходные тексты поставщика данных (архив
 
Полные исходные тексты поставщика данных (архив
 
networkengine) и плазмоида ncview вы найдете на LXFDVD.
 
networkengine) и плазмоида ncview вы найдете на LXFDVD.
 
Networkengine предоставляет один источник по имени NCs. Данные
 
Networkengine предоставляет один источник по имени NCs. Данные
представляют собой массив строк (QStringList), в котором содер-
+
представляют собой массив строк (QStringList), в котором содержится перечень сетевых устройств, имена соответствующих сетевых интерфейсов Linux и MAC-адреса устройств (это, собственно
жится перечень сетевых устройств, имена соответствующих сете-
+
вых интерфейсов Linux и MAC-адреса устройств (это, собственно
+
 
говоря, вся информация, которую можно извлечь из интерфейса
 
говоря, вся информация, которую можно извлечь из интерфейса
NetworkInterface). Рассмотрим объявление главного класса постав-
+
NetworkInterface). Рассмотрим объявление главного класса поставщика данных NetStatEngine:
щика данных NetStatEngine:
+
<source lang="cpp-qt">class NetStatEngine : public Plasma::DataEngine {
class NetStatEngine : public Plasma::DataEngine {
+
 
Q_OBJECT
 
Q_OBJECT
 
public:
 
public:
Строка 274: Строка 218:
 
private slots:
 
private slots:
 
void updateDevices();
 
void updateDevices();
};
+
};</source>
 
Основные элементы этого класса должны быть знакомы вам по
 
Основные элементы этого класса должны быть знакомы вам по
LXF110 ( в отличие от той статьи, здесь мы используем только син-
+
[[LXF110:KDE4|LXF110]] ( в отличие от той статьи, здесь мы используем только синтаксис KDE 4.1.x: пришла пора обновиться, если вы до сих пор этого
таксис KDE 4.1.x: пришла пора обновиться, если вы до сих пор этого
+
 
не сделали). Новый элемент здесь – только слот updateDevices(). В
 
не сделали). Новый элемент здесь – только слот updateDevices(). В
 
конструкторе класса мы вызываем метод updateSourceEvent(), как
 
конструкторе класса мы вызываем метод updateSourceEvent(), как
и в любом поставщике данных, а затем получаем указатель на гло-
+
и в любом поставщике данных, а затем получаем указатель на глобальный объект Solid::DeviceNotifier:
бальный объект Solid::DeviceNotifier:
+
<source lang="cpp-qt">NetStatEngine::NetStatEngine(QObject* parent, const QVariantList&)
NetStatEngine::NetStatEngine(QObject* parent, const QVariantList&)
+
 
: Plasma::DataEngine(parent)
 
: Plasma::DataEngine(parent)
 
{
 
{
Строка 291: Строка 233:
 
dn->connect(dn, SIGNAL(deviceRemoved(const QString)), this,
 
dn->connect(dn, SIGNAL(deviceRemoved(const QString)), this,
 
SLOT(updateDevices()));
 
SLOT(updateDevices()));
}
+
}</source>
 
Как и в рассмотренном выше случае, этот объект нужен нам для
 
Как и в рассмотренном выше случае, этот объект нужен нам для
 
того, чтобы связать слот updateDevices() с сигналами deviceAdded()
 
того, чтобы связать слот updateDevices() с сигналами deviceAdded()
 
и deviceRemoved(). Рабочая лошадка нашего поставщика данных –
 
и deviceRemoved(). Рабочая лошадка нашего поставщика данных –
 
метод updateSourceEvent():
 
метод updateSourceEvent():
bool NetStatEngine::updateSourceEvent(const QString &name)
+
<source lang="cpp-qt">bool NetStatEngine::updateSourceEvent(const QString &name)
 
{
 
{
 
if (name == "NCs") {
 
if (name == "NCs") {
Строка 315: Строка 257:
 
}
 
}
 
return false;
 
return false;
}
+
}</source>
Наша первая задача – получить список устройств, поддер-
+
Наша первая задача – получить список устройств, поддерживающих интерфейс NetworkInterface, как было описано выше.
живающих интерфейс NetworkInterface, как было описано выше.
+
 
+
 
Далее мы перебираем все элементы списка сетевых устройств
 
Далее мы перебираем все элементы списка сетевых устройств
 
и для каждого из них получаем указатель на объект класса
 
и для каждого из них получаем указатель на объект класса
Строка 324: Строка 264:
 
Данные, полученные с помощью интерфейсов, заносятся в список
 
Данные, полученные с помощью интерфейсов, заносятся в список
 
QStringList, который передается методу setData(). Метод ifaceName()
 
QStringList, который передается методу setData(). Метод ifaceName()
класса Solid::NetworkInterface возвращает строку с именем интер-
+
класса Solid::NetworkInterface возвращает строку с именем интерфейса (“eth0”, “eth1” и т.д.). С помощью метода hwAddress()
фейса (“eth0”, “eth1” и т.д.). С помощью метода hwAddress()
+
 
мы можем получить MAC-адрес устройства в текстовом виде
 
мы можем получить MAC-адрес устройства в текстовом виде
 
(метод macAddress() возвращает MAC-адрес в виде числа). Метод
 
(метод macAddress() возвращает MAC-адрес в виде числа). Метод
 
isWireless() вернет значение true, если сетевой интерфейс является
 
isWireless() вернет значение true, если сетевой интерфейс является
беспроводным, и false – в противном случае. Чтобы сделать спи-
+
беспроводным, и false – в противном случае. Чтобы сделать список сетевых устройств более информативным, мы выводим название каждого устройства. Его можно получить с помощью метода
сок сетевых устройств более информативным, мы выводим назва-
+
product() класса Solid::Device; однако для всех устройств, соответствующих сетевым интерфейсам, он возвращает строку “Network
ние каждого устройства. Его можно получить с помощью метода
+
product() класса Solid::Device; однако для всех устройств, соответ-
+
ствующих сетевым интерфейсам, он возвращает строку “Network
+
 
Interface”, что, конечно, не очень интересно. Для получения имени
 
Interface”, что, конечно, не очень интересно. Для получения имени
 
физического устройства, предоставляющего сетевой интерфейс,
 
физического устройства, предоставляющего сетевой интерфейс,
мы вызываем метод product() устройства, являющегося «родите-
+
мы вызываем метод product() устройства, являющегося «родителем» сетевого интерфейса.
лем» сетевого интерфейса.
+
 
 
Нам осталось рассмотреть вопрос о том, как поставщик данных
 
Нам осталось рассмотреть вопрос о том, как поставщик данных
оповещает плазмоид о подключении и отключении сетевых интер-
+
оповещает плазмоид о подключении и отключении сетевых интерфейсов [[LXF110:KDE4|LXF110]], напомним, это происходило в результате периодического опроса). Для такого нечастого события, как добавление
фейсов (в LXF110, напомним, это происходило в результате перио-
+
дического опроса). Для такого нечастого события, как добавление
+
 
и удаление сетевых интерфейсов, регулярный опрос, выполняемый
 
и удаление сетевых интерфейсов, регулярный опрос, выполняемый
плазмоидом, явно избыточен. Для нерегулярных и нечастых собы-
+
плазмоидом, явно избыточен. Для нерегулярных и нечастых событий лучше подходит модель, при которой поставщик сам информирует заинтересованный плазмоид об изменении данных. Наладить
тий лучше подходит модель, при которой поставщик сам информи-
+
этот механизм очень просто. Ниже приводится исходный текст слота updateDevices(), который обрабатывает сигналы deviceAdded() и
рует заинтересованный плазмоид об изменении данных. Наладить
+
этот механизм очень просто. Ниже приводится исходный текст сло-
+
та updateDevices(), который обрабатывает сигналы deviceAdded() и
+
 
deviceRemoved():
 
deviceRemoved():
void NetStatEngine::updateDevices()
+
<source lang="cpp-qt">void NetStatEngine::updateDevices()
 
{
 
{
 
updateSourceEvent("NCs");
 
updateSourceEvent("NCs");
}
+
}</source>
 
Не удивляйтесь – это действительно все, что необходимо для
 
Не удивляйтесь – это действительно все, что необходимо для
 
оповещения плазмоида. Метод updateSourceEvent() вызывает метод
 
оповещения плазмоида. Метод updateSourceEvent() вызывает метод
setData(), который с помощью соответствующего сигнала активиру-
+
setData(), который с помощью соответствующего сигнала активирует слот dataUpdated() в плазмоиде.
ет слот dataUpdated() в плазмоиде.
+
 
 
Наконец, зная про метод listFromType(), мы можем написать
 
Наконец, зная про метод listFromType(), мы можем написать
 
улучшенный вариант метода findChildren() для программы devtree:
 
улучшенный вариант метода findChildren() для программы devtree:
void devtreeView::findChildren(QTreeWidgetItem * root) {
+
<source lang="cpp-qt">void devtreeView::findChildren(QTreeWidgetItem * root) {
 
QList<Solid::Device> children =
 
QList<Solid::Device> children =
 
Solid::Device::listFromType(Solid::DeviceInterface::GenericInterface,
 
Solid::Device::listFromType(Solid::DeviceInterface::GenericInterface,
Строка 368: Строка 299:
 
findChildren(new QTreeWidgetItem(root, sl));
 
findChildren(new QTreeWidgetItem(root, sl));
 
}
 
}
}
+
}</source>
 
Значение Solid::DeviceInterface::GenericInterface соответствует интерфейсу базового типа, который поддерживается всеми
 
Значение Solid::DeviceInterface::GenericInterface соответствует интерфейсу базового типа, который поддерживается всеми
 
устройствами.
 
устройствами.
Строка 374: Строка 305:
 
На тему программирования для KDE 4 можно было бы написать
 
На тему программирования для KDE 4 можно было бы написать
 
еще очень много, но мы завершаем наш обзор. Того, что вы узнали
 
еще очень много, но мы завершаем наш обзор. Того, что вы узнали
о новой версии KDE из этих уроков, вполне достаточно, чтобы написать расширение KDE, которое, возможно, даже оправдает утяжеление будущего дистрибутива на несколько мегабайт. Не забудьте
+
о новой версии KDE из этих уроков, вполне достаточно, чтобы написать расширение KDE, которое, возможно, даже оправдает утяжеление будущего дистрибутива на несколько мегабайт. Не забудьте сообщить нам, если получится что-то стоящее!
сообщить нам, если получится что-то стоящее!
+

Текущая версия на 18:31, 4 октября 2009

Содержание

[править] Солидная аппаратура

ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая яркая, но далеко не единственная подсистема KDE4. В качестве финального аккорда Андрей Боровский припас для вас слой аппаратной абстракции под названием Solid.

Не так давно один известный в Рунете писатель задал сообществу программистов Linux вопрос: почему новые версии KDE все более и более громоздки и прожорливы? Ведь улучшение программы предполагает, в том числе, оптимизацию ее кода... Можно, конечно, ответить – мол, новые версии оболочки удовлетворяют растущим потребностям пользователей. Что, я полагаю, будет лукавством: не так уж сильно эти потребности и выросли. Вспомните, когда в последний раз менялся интерфейс самой популярной текстовой оболочки – Midnight Commander? Следует признать, что в стремлении к свободному творчеству, которое и привлекает многих программистов в мир открытого ПО, разработчики будут охотнее добавлять новые функции, нежели оптимизировать старые. Вот почему KDE постоянно растет и вширь, и вглубь, и, в частности, обзавелся системой управления устройствами Solid.

Зачем нужен Solid? Разумеется, его смысл и польза заключаются вовсе не в том, чтобы удовлетворять амбиции KDE-программистов. Как вы, конечно, знаете, KDE портируется на множество ОС, в том числе Windows и Mac OS X, и у каждой из них есть собственные средства управления оборудованием. Solid представляет собой универсальный способ взаимодействия с устройствами всюду, где работает KDE. В идеале, система Solid должна сократить количество непереносимого кода в вашем приложении (хотя свести его к нулю все равно не удастся). В этом и заключается ответ на вопрос о том, не лучше ли использовать HAL (на котором основана система Solid) напрямую. Linux HAL поддерживается не на всех платформах, которые намерен завоевать KDE. Кроме того, Linux HAL базируется на Dbus, и эффективная работа с ним предполагает хорошее знание этой своеобразной системы. В процессе же общения с Solid мы находимся в знакомой среде с сигналами, слотами и объектной моделью Qt/KDE.

[править] Исследуем дерево устройств

Центральным классом Solid является Solid::Device (все классы Solid объявлены в пространстве имен Solid), который представляет устройство. Дать четкое определение того, что является устройством с точки зрения Solid, не так-то просто. Это и процессор, и его загружаемый микрокод, и видеокамера, и ее аудиоподсистема, оптический привод и диск в нем, винчестер и каждый его раздел... Если такая модель кажется вам запутанной, вспомните, что в других системах дело обстоит не лучше (в Диспетчер оборудования Windows давно заглядывали?). Устройства Solid образуют иерархию, корнем которой является, конечно, Computer. Чтобы еще больше вас запутать – каждое устройство предоставляет один или несколько интерфейсов, представляемых потомками класса Solid::DeviceInterface.

Пока вы еще не успели разочароваться в Solid, поспешим уточнить, что может и чего не может эта система. Solid позволяет получать различные сведения об устройствах, в том числе отслеживать их состояние в режиме реального времени. При этом Solid не предоставляет собственных средств для обмена данными с устройствами. С помощью Solid вы можете получить имя файла, соответствующего устройству, его идентификатор или другую информацию, необходимую для того, чтобы наладить обмен информацией, но его придется выполнять с помощью интерфейса, предоставляемого операционной системой. Например, все известные Solid устройства, способные записывать и воспроизводить аудиоданные, предоставляют интерфейс Solid::AudioInterface. У него есть метод driverHandle(), который возвращает значение типа QVariant. Для устройств, которые обслуживают драйверы ALSA, фактическим значением driverHandle() будет запись, содержащая имя звуковой карты (в системе ALSA) и номера первичного и вторичного устройств. Если устройство работает под управлением драйвера OSS, метод driverHandle() вернет строку с именем файла устройства.

(thumbnail)
(Рис. 1) Программа devtree – ее-то мы сегодня и напишем.

Практическое знакомство с Solid мы начнем с программы обзора всех устройств, присутствующих в системе. Приложение devtree, чьи исходные тексты вы найдете на диске, покажет вам полный список устройств с учетом их иерархии (рис. 1).

Доступ к полному списку устройств, поддерживаемых Solid, можно получить с помощью статического метода Solid::Device::allDevices(). Он возвращает нам объект типа QList<Solid::Device>. Простая конструкция

foreach (const Solid::Device &device, Solid::Device::allDevices()) {
...
}

позволит перебрать все элементы данного списка (роль итератора играет переменная device). Однако последовательный перебор перечня устройств не так уж и интересен. Дело в том, что расположение устройств в списке Solid::Device::allDevices() не учитывает иерархических отношений между ними. В программе devtree мы используем рекурсивный метод заполнения виджета QTreeWidget элементами списка (он не самый эффективный, но самый простой – альтернативу рассмотрим в конце статьи):

void devtreeView::enumDevices()
{
ui_devtreeview_base.treeWidget->setColumnCount(3);
QStringList hsl;
hsl << "UDI" << "Vendor" << "Name";
ui_devtreeview_base.treeWidget->setHeaderLabels(hsl);
QTreeWidgetItem * root = NULL;
foreach (const Solid::Device &device, Solid::Device::allDevices())
{
QStringList sl;
if(device.parentUdi() == "") {
sl << device.udi() << device.vendor() << device.product();
root = new QTreeWidgetItem(ui_devtreeview_base.treeWidget, sl);
break;
}
}
findChildren(root);
}
void devtreeView::findChildren(QTreeWidgetItem * root) {
foreach (const Solid::Device &device, Solid::Device::allDevices()) {
if(device.parentUdi() == root->text(0)) {
QStringList sl;
sl << device.udi() << device.vendor() << device.product();
findChildren(new QTreeWidgetItem(root, sl));
}
}
}

Один из наиболее часто используемых методов класса Solid::Device, udi(), возвращает уникальный идентификатор устройства. В Linux, где Solid основан на FreeDesktop HAL, идентификатором устройства служит HAL-адрес устройства (строка вида “/org/freedestop/Hal/...”). Нам не обязательно разбираться в том, как формируются эти адреса: важно знать, что именно они идентифицируют устройства. Метод parentUdi() класса Solid::Device возвращает идентификатор родительского устройства, а соответствующий ему объект класса Solid::Device можно получить с помощью метода Solid::Device::parent(). Для корневого устройства метод parentUdi() возвращает пустую строку, чем мы и пользуемся в методе enumDevices(). После того как корневое устройство найдено, мы вызываем рекурсивно метод findChildren(), который добавляет в виджет ui_devtreeview_base.treeWidget устройства, являющиеся непосредственными потомками корневого. Для каждого найденного устройства наша программа отображает три элемента данных: идентификатор устройства, наименование изготовителя (возвращается методом Solid::Device::vendor()) и описание устройства (возвращается методом product()). У класса Solid::Device есть еще и метод icon(), который возвращает строку с именем пиктограммы устройства (пока что непустая строка возвращается только для корневого устройства Computer).

Если бы система Solid позволяла только перечислить имеющиеся в наличии устройства, пользы от нее было бы не очень много. Однако основная задача Solid заключается в том, что-бы информировать систему о различных событиях, связанных с устройствами. Делается это, естественно, с помощью сигналов и слотов. Сигналы, оповещающие систему о событиях, связанных с устройствами, эмитируются объектами специальных классов. На момент написания статьи их существовало три: Solid::DeviceNotifier, Solid::Networking::Notifier и Solid::PowerManagement::Notifier. Объект класса Solid::DeviceNotifier информирует программу о подключении и отключении устройств. Объект класса Solid::Networking::Notifier сообщает о подключении и отключении системы от сети, а объект класса Solid::PowerManagement::Notifier позволяет программе отслеживать режим работы системы питания. Особняком стоит класс WebcamWatcher, который выполняет функции Solid::DeviceNotifier исключительно для web-камер.

На первый взгляд может показаться, что возможности Solid в плане оповещения программ о событиях устройств крайне скромны, но это не так. Помните, что устройствами в Solid считаются самые разные вещи. Объект класса Solid::DeviceNotifier может оповещать программу о таких событиях, как установка нового CD-диска в привод, подключение USB-устройства или монтирование нового раздела файловой системы.

Вполне логично задействовать объект класса Solid::DeviceNotifier в программе devtree для обновления дерева устройств в случае изменения оного. Система Solid предоставляет каждой программе один объект класса Solid::DeviceNotifier. Для получения указателя на него мы воспользуемся статическим методом Solid::DeviceNotifier::instance():

Solid::DeviceNotifier * dn = Solid::DeviceNotifier::instance();

Объект класса Solid::DeviceNotifier эмитирует два сигнала: deviceAdded() (добавлено новое устройство) и deviceRemoved() (устройство отключено). В качестве аргумента оба сигнала передают строку с идентификатором устройства. Мы связываем оба сигнала со слотом update(), который мы добавили в главный класс нашей программы:

dn->connect(dn, SIGNAL(deviceAdded(const QString)), this, SLOT(update()));
dn->connect(dn, SIGNAL(deviceRemoved(const QString)), this, SLOT(update()));

Слот update() не обрабатывает переданный ему параметр, а просто очищает виджет ui_devtreeview_base.treeWidget и заново строит дерево устройств. Разумеется, такой подход нельзя назвать самым эффективным. Для ускорения работы программы можно было бы найти для добавленного/удаленного родительское устройство и перестроить только соответствующую ему часть дерева.

Для сборки программы devtree в файл CMakeLists.txt следует добавить данные о заголовочных файлах и библиотеках Solid. Вопреки тому, что говорит документация, все заголовочные файлы Solid API хранятся в поддиректории solid стандартной директории include. Переменная CMake KDE4_INCLUDE_DIR уже содержит ее имя. Единственное изменение, которое нужно внести в файл CMakeLists.txt по сравнению со стандартным файлом приложения KDE 4 – это добавить переменную KDE4_SOLID_LIBS в качестве аргумента команды target_link_libraries(). Теперь программу devtree можно компилировать.

[править] Интерфейсы устройств

В модели Solid объекты класса Solid::Device соответствуют общему понятию «устройство» и позволяют непосредственно получить только те данные, которые имеют смысл для всех устройств, независимо от их типа. Более подробную информацию о каждом устройстве можно извлечь с помощью интерфейсов, которые представлены в Solid объектами класса Solid::DeviceInterface и его потомков. Каждое устройство (то есть корректно созданный объект Solid::Device) предоставляет как минимум один интерфейс. Узнать, поддерживает ли устройство определенный интерфейс, можно с помощью метода isDeviceInterface() класса Solid::Device. Его аргументом должно быть значение типа Solid::DeviceInterface::Type, указывающее тип требуемого интерфейса. Полный список типов интерфейсов можно найти в документации Solid и файле solid/deviceinterface.h. Например, тип Processor определяет интерфейс ЦП, Block – блочное устройство, StorageVolume – раздел на диске, Battery – аккумулятор. Учитывая структуру Solid, вы не должны удивляться, что среди поддерживаемых типов интерфейсов одновременно присутствуют интерфейсы логических и физических устройств. Если объект Solid::Device поддерживает запрошенный интерфейс, метод isDeviceInterface() возвращает значение true.

После того как вы выяснили, поддерживает ли устройство некоторый интерфейс, вы, скорее всего, захотите получить объект, его реализующий. Сделать это можно с помощью метода Solid::Device:

asDeviceInterface(), аргументом которого выступает тип требуемого

интерфейса. В случае успеха метод возвращает указатель на объект класса, производного от Solid::DeviceInterface, соответствующего запрошенному интерфейсу. Имейте в виду, что не для каждого значения Solid::DeviceInterface::Type существует свой класс (так, по крайней мере, обстояло дело на момент написания статьи).

В приложениях, использующих Solid, довольно часто требуется получить список устройств, поддерживающих определенный интерфейс (вполне логично, что программа, которая обрабатывает изображение, поступающее с видеокамер, захочет иметь полный список устройств, поддерживающих интерфейс Camera, а не вообще всех устройств). Для этого можно было бы, конечно, перебрать полный список устройств системы (как мы делали выше), проверяя, поддерживает ли каждое из них требуемый интерфейс, однако Solid предоставляет нам более простой и эффективный способ. Класс Solid::Device экспортирует несколько статических методов, предназначенных для управления не конкретным устройством, а всем списком устройств Solid (с одним из них, allDevices(), мы уже знакомы). Статические методы Solid::Device::listFromType() и Solid::Device::listFromQuery() позволяют получить подмножество списка устройств, заданное специальными признаками. Нас интересует метод listFromType(), который возвращает список устройств, поддерживающих определенный интерфейс (говоря «устройство», я, разумеется, имею в виду объект класса Solid::Device). Вот как, например, мы можем получить список устройств, поддерживающих интерфейс NetworkInterface:

QList<Solid::Device> devlist =
Solid::Device::listFromType(Solid::DeviceInterface::NetworkInterface, QString());

Если во втором параметре listFromType() передать идентификатор устройства, в результирующий список войдут только сетевые устройства, являющиеся потомками заданного (пустая строка, разумеется, означает, что нас интересуют все сетевые устройства).

[править] Solid и Plasma

(thumbnail)
Рис. 2. Плазмоид netstat — то, что нужно администратору.

Работу с интерфейсами устройств мы продемонстрируем на примере динамического плазмоида. Как уже говорилось в LXF110, плазмоиды хорошо подходят для того, чтобы информировать пользователя о важных событиях, происходящих в системе. Solid, как мы уже видели, может служить источником такого рода событий.

Для передачи данных от Solid плазмоиду мы воспользуемся стандартным механизмом поставщиков данных Plasma (LXF110). Наш плазмоид будет информировать пользователя о состоянии сетевых интерфейсов системы (рис. 2).

Полные исходные тексты поставщика данных (архив networkengine) и плазмоида ncview вы найдете на LXFDVD. Networkengine предоставляет один источник по имени NCs. Данные представляют собой массив строк (QStringList), в котором содержится перечень сетевых устройств, имена соответствующих сетевых интерфейсов Linux и MAC-адреса устройств (это, собственно говоря, вся информация, которую можно извлечь из интерфейса NetworkInterface). Рассмотрим объявление главного класса поставщика данных NetStatEngine:

class NetStatEngine : public Plasma::DataEngine {
Q_OBJECT
public:
NetStatEngine(QObject* parent, const QVariantList&);
protected:
bool sourceRequestEvent(const QString& name);
bool updateSourceEvent(const QString& source);
private slots:
void updateDevices();
};

Основные элементы этого класса должны быть знакомы вам по LXF110 ( в отличие от той статьи, здесь мы используем только синтаксис KDE 4.1.x: пришла пора обновиться, если вы до сих пор этого не сделали). Новый элемент здесь – только слот updateDevices(). В конструкторе класса мы вызываем метод updateSourceEvent(), как и в любом поставщике данных, а затем получаем указатель на глобальный объект Solid::DeviceNotifier:

NetStatEngine::NetStatEngine(QObject* parent, const QVariantList&)
: Plasma::DataEngine(parent)
{
updateSourceEvent("NCs");
Solid::DeviceNotifier * dn = Solid::DeviceNotifier::instance();
dn->connect(dn, SIGNAL(deviceAdded(const QString)), this,
SLOT(updateDevices()));
dn->connect(dn, SIGNAL(deviceRemoved(const QString)), this,
SLOT(updateDevices()));
}

Как и в рассмотренном выше случае, этот объект нужен нам для того, чтобы связать слот updateDevices() с сигналами deviceAdded() и deviceRemoved(). Рабочая лошадка нашего поставщика данных – метод updateSourceEvent():

bool NetStatEngine::updateSourceEvent(const QString &name)
{
if (name == "NCs") {
QList<Solid::Device> devlist =
Solid::Device::listFromType(Solid::DeviceInterface::NetworkInterface,
QString());
QStringList sl;
foreach (const Solid::Device &device, devlist) {
sl << "Device: " + device.parent().product();
Solid::NetworkInterface * ni = (Solid::NetworkInterface *) device.as
DeviceInterface(Solid::DeviceInterface::NetworkInterface);
sl << "Interface: " + ni->ifaceName();
sl << "Address: " + ni->hwAddress();
sl << QString("Type: ") + (ni->isWireless() ? "Wireless" : "Wired");
}
setData(name, sl);
return true;
}
return false;
}

Наша первая задача – получить список устройств, поддерживающих интерфейс NetworkInterface, как было описано выше. Далее мы перебираем все элементы списка сетевых устройств и для каждого из них получаем указатель на объект класса Solid::NetworkInterface посредством метода asDeviceInterface(). Данные, полученные с помощью интерфейсов, заносятся в список QStringList, который передается методу setData(). Метод ifaceName() класса Solid::NetworkInterface возвращает строку с именем интерфейса (“eth0”, “eth1” и т.д.). С помощью метода hwAddress() мы можем получить MAC-адрес устройства в текстовом виде (метод macAddress() возвращает MAC-адрес в виде числа). Метод isWireless() вернет значение true, если сетевой интерфейс является беспроводным, и false – в противном случае. Чтобы сделать список сетевых устройств более информативным, мы выводим название каждого устройства. Его можно получить с помощью метода product() класса Solid::Device; однако для всех устройств, соответствующих сетевым интерфейсам, он возвращает строку “Network Interface”, что, конечно, не очень интересно. Для получения имени физического устройства, предоставляющего сетевой интерфейс, мы вызываем метод product() устройства, являющегося «родителем» сетевого интерфейса.

Нам осталось рассмотреть вопрос о том, как поставщик данных оповещает плазмоид о подключении и отключении сетевых интерфейсов (в LXF110, напомним, это происходило в результате периодического опроса). Для такого нечастого события, как добавление и удаление сетевых интерфейсов, регулярный опрос, выполняемый плазмоидом, явно избыточен. Для нерегулярных и нечастых событий лучше подходит модель, при которой поставщик сам информирует заинтересованный плазмоид об изменении данных. Наладить этот механизм очень просто. Ниже приводится исходный текст слота updateDevices(), который обрабатывает сигналы deviceAdded() и deviceRemoved():

void NetStatEngine::updateDevices()
{
updateSourceEvent("NCs");
}

Не удивляйтесь – это действительно все, что необходимо для оповещения плазмоида. Метод updateSourceEvent() вызывает метод setData(), который с помощью соответствующего сигнала активирует слот dataUpdated() в плазмоиде.

Наконец, зная про метод listFromType(), мы можем написать улучшенный вариант метода findChildren() для программы devtree:

void devtreeView::findChildren(QTreeWidgetItem * root) {
QList<Solid::Device> children =
Solid::Device::listFromType(Solid::DeviceInterface::GenericInterface,
root->text(0));
foreach (const Solid::Device &device, children) {
QStringList sl;
sl << device.udi() << device.vendor() << device.product();
findChildren(new QTreeWidgetItem(root, sl));
}
}

Значение Solid::DeviceInterface::GenericInterface соответствует интерфейсу базового типа, который поддерживается всеми устройствами.

На тему программирования для KDE 4 можно было бы написать еще очень много, но мы завершаем наш обзор. Того, что вы узнали о новой версии KDE из этих уроков, вполне достаточно, чтобы написать расширение KDE, которое, возможно, даже оправдает утяжеление будущего дистрибутива на несколько мегабайт. Не забудьте сообщить нам, если получится что-то стоящее!

Персональные инструменты
купить
подписаться
Яндекс.Метрика