LXF112:KDE4
Yaleks (обсуждение | вклад) м (Новая: {{Цикл/KDE4}} == Солидная аппаратура == : ''ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая ...) |
Yaleks (обсуждение | вклад) |
||
Строка 3: | Строка 3: | ||
: ''ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая яркая, но далеко не единственная подсистема KDE4. В качестве финального аккорда '''Андрей Боровский''' припас для вас слой аппаратной абстракции под названием Solid.'' | : ''ЧАСТЬ 5 Plasma, о которой мы много говорили на прошлых уроках – самая яркая, но далеко не единственная подсистема KDE4. В качестве финального аккорда '''Андрей Боровский''' припас для вас слой аппаратной абстракции под названием Solid.'' | ||
− | Не так давно один известный в Рунете писатель задал | + | Не так давно один известный в Рунете писатель задал сообществу программистов Linux вопрос: почему новые версии |
− | + | ||
KDE все более и более громоздки и прожорливы? Ведь | KDE все более и более громоздки и прожорливы? Ведь | ||
улучшение программы предполагает, в том числе, оптимизацию ее | улучшение программы предполагает, в том числе, оптимизацию ее | ||
кода... Можно, конечно, ответить – мол, новые версии оболочки | кода... Можно, конечно, ответить – мол, новые версии оболочки | ||
− | удовлетворяют растущим потребностям пользователей. Что, я | + | удовлетворяют растущим потребностям пользователей. Что, я полагаю, будет лукавством: не так уж сильно эти потребности и выросли. |
− | + | Вспомните, когда в последний раз менялся интерфейс самой популярной текстовой оболочки – 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 |
− | + | ||
мы находимся в знакомой среде с сигналами, слотами и объектной | мы находимся в знакомой среде с сигналами, слотами и объектной | ||
моделью Qt/KDE. | моделью Qt/KDE. | ||
− | Исследуем дерево устройств | + | |
+ | === Исследуем дерево устройств === | ||
Центральным классом Solid является Solid::Device (все классы | Центральным классом Solid является Solid::Device (все классы | ||
Solid объявлены в пространстве имен Solid), который представляет | Solid объявлены в пространстве имен Solid), который представляет | ||
− | устройство. Дать четкое определение того, что является | + | устройство. Дать четкое определение того, что является устройством с точки зрения Solid, не так-то просто. Это и процессор, и |
− | + | его загружаемый микрокод, и видеокамера, и ее аудиоподсистема, оптический привод и диск в нем, винчестер и каждый его раздел... Если такая модель кажется вам запутанной, вспомните, что | |
− | его загружаемый микрокод, и видеокамера, и ее | + | в других системах дело обстоит не лучше (в Диспетчер оборудования Windows давно заглядывали?). Устройства Solid образуют |
− | + | ||
− | + | ||
− | в других системах дело обстоит не лучше (в Диспетчер | + | |
− | + | ||
иерархию, корнем которой является, конечно, Computer. Чтобы | иерархию, корнем которой является, конечно, Computer. Чтобы | ||
еще больше вас запутать – каждое устройство предоставляет один | еще больше вас запутать – каждое устройство предоставляет один | ||
или несколько интерфейсов, представляемых потомками класса | или несколько интерфейсов, представляемых потомками класса | ||
Solid::DeviceInterface. | Solid::DeviceInterface. | ||
− | Пока вы еще не успели разочароваться в Solid, поспешим | + | |
− | + | Пока вы еще не успели разочароваться в Solid, поспешим уточнить, что может и чего не может эта система. Solid позволяет получать различные сведения об устройствах, в том числе отслеживать | |
− | + | их состояние в режиме реального времени. При этом Solid не предоставляет собственных средств для обмена данными с устройствами. С помощью Solid вы можете получить имя файла, соответствующего устройству, его идентификатор или другую информацию, необходимую для того, чтобы наладить обмен информацией, | |
− | их состояние в режиме реального времени. При этом Solid не | + | но его придется выполнять с помощью интерфейса, предоставляемого операционной системой. Например, все известные Solid |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | но его придется выполнять с помощью интерфейса, | + | |
− | + | ||
устройства, способные записывать и воспроизводить аудиоданные, | устройства, способные записывать и воспроизводить аудиоданные, | ||
предоставляют интерфейс Solid::AudioInterface. У него есть метод | предоставляют интерфейс Solid::AudioInterface. У него есть метод | ||
driverHandle(), который возвращает значение типа QVariant. Для | driverHandle(), который возвращает значение типа QVariant. Для | ||
− | устройств, которые обслуживают драйверы ALSA, фактическим | + | устройств, которые обслуживают драйверы ALSA, фактическим значением driverHandle() будет запись, содержащая имя звуковой карты (в системе ALSA) и номера первичного и вторичного устройств. |
− | + | ||
− | + | ||
Если устройство работает под управлением драйвера OSS, метод | Если устройство работает под управлением драйвера OSS, метод | ||
driverHandle() вернет строку с именем файла устройства. | driverHandle() вернет строку с именем файла устройства. | ||
− | Практическое знакомство с Solid мы начнем с программы | + | |
− | + | [[Изображение:LXF112 70 1.png|thumb|200px|(Рис. 1) Программа devtree – ее-то мы сегодня и напишем.]] | |
− | чьи исходные тексты вы найдете на диске, покажет вам полный | + | Практическое знакомство с Solid мы начнем с программы обзора всех устройств, присутствующих в системе. Приложение devtree, |
− | + | чьи исходные тексты вы найдете на диске, покажет вам полный список устройств с учетом их иерархии (рис. 1). | |
Доступ к полному списку устройств, поддерживаемых Solid, можно | Доступ к полному списку устройств, поддерживаемых Solid, можно | ||
Строка 78: | Строка 61: | ||
}</source> | }</source> | ||
позволит перебрать все элементы данного списка (роль итератора играет переменная device). Однако последовательный перебор | позволит перебрать все элементы данного списка (роль итератора играет переменная device). Однако последовательный перебор | ||
− | перечня устройств не так уж и интересен. Дело в том, что | + | перечня устройств не так уж и интересен. Дело в том, что расположение устройств в списке 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/...”). Нам не обязательно разбираться в том, как |
− | + | формируются эти адреса: важно знать, что именно они идентифицируют устройства. Метод parentUdi() класса Solid::Device возвращает идентификатор родительского устройства, а соответствующий ему объект класса Solid::Device можно получить с помощью | |
− | + | ||
− | org/freedestop/Hal/...”). Нам не обязательно разбираться в том, как | + | |
− | формируются эти адреса: важно знать, что именно они | + | |
− | + | ||
− | + | ||
− | + | ||
метода Solid::Device::parent(). Для корневого устройства метод | метода Solid::Device::parent(). Для корневого устройства метод | ||
− | parentUdi() возвращает пустую строку, чем мы и пользуемся в | + | parentUdi() возвращает пустую строку, чем мы и пользуемся в методе enumDevices(). После того как корневое устройство найдено, |
− | + | ||
мы вызываем рекурсивно метод findChildren(), который добавляет | мы вызываем рекурсивно метод findChildren(), который добавляет | ||
в виджет ui_devtreeview_base.treeWidget устройства, являющиеся | в виджет ui_devtreeview_base.treeWidget устройства, являющиеся | ||
непосредственными потомками корневого. Для каждого найденного | непосредственными потомками корневого. Для каждого найденного | ||
− | устройства наша программа отображает три элемента данных: | + | устройства наша программа отображает три элемента данных: идентификатор устройства, наименование изготовителя (возвращается |
− | + | методом Solid::Device::vendor()) и описание устройства (возвращается методом product()). У класса Solid::Device есть еще и метод | |
− | методом Solid::Device::vendor()) и описание устройства ( | + | icon(), который возвращает строку с именем пиктограммы устройства (пока что непустая строка возвращается только для корневого |
− | + | ||
− | 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-устройства или монтирование нового раздела файловой системы. |
− | + | ||
− | + | ||
Вполне логично задействовать объект класса Solid::DeviceNotifier | Вполне логично задействовать объект класса Solid::DeviceNotifier | ||
в программе devtree для обновления дерева устройств в случае | в программе devtree для обновления дерева устройств в случае | ||
изменения оного. Система Solid предоставляет каждой программе | изменения оного. Система Solid предоставляет каждой программе | ||
один объект класса Solid::DeviceNotifier. Для получения указателя | один объект класса Solid::DeviceNotifier. Для получения указателя | ||
− | на него мы воспользуемся статическим методом Solid:: | + | на него мы воспользуемся статическим методом Solid::DeviceNotifier::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() не обрабатывает переданный ему параметр, а | + | |
− | + | ||
дерево устройств. Разумеется, такой подход нельзя назвать самым | дерево устройств. Разумеется, такой подход нельзя назвать самым | ||
эффективным. Для ускорения работы программы можно было бы | эффективным. Для ускорения работы программы можно было бы | ||
найти для добавленного/удаленного родительское устройство и | найти для добавленного/удаленного родительское устройство и | ||
перестроить только соответствующую ему часть дерева. | перестроить только соответствующую ему часть дерева. | ||
− | Для сборки программы devtree в файл CMakeLists.txt | + | |
− | + | Для сборки программы devtree в файл CMakeLists.txt следует добавить данные о заголовочных файлах и библиотеках Solid. | |
− | Вопреки тому, что говорит документация, все заголовочные | + | Вопреки тому, что говорит документация, все заголовочные файлы 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() | + | |
− | + | После того как вы выяснили, поддерживает ли устройство некоторый интерфейс, вы, скорее всего, захотите получить объект, его | |
− | После того как вы выяснили, поддерживает ли устройство | + | |
− | + | ||
реализующий. Сделать это можно с помощью метода Solid::Device: | реализующий. Сделать это можно с помощью метода Solid::Device: | ||
:asDeviceInterface(), аргументом которого выступает тип требуемого | :asDeviceInterface(), аргументом которого выступает тип требуемого | ||
− | интерфейса. В случае успеха метод возвращает указатель на | + | интерфейса. В случае успеха метод возвращает указатель на объект класса, производного от Solid::DeviceInterface, соответствующего запрошенному интерфейсу. Имейте в виду, что не для каждого |
− | + | ||
− | + | ||
значения Solid::DeviceInterface::Type существует свой класс (так, по | значения Solid::DeviceInterface::Type существует свой класс (так, по | ||
крайней мере, обстояло дело на момент написания статьи). | крайней мере, обстояло дело на момент написания статьи). | ||
− | В приложениях, использующих Solid, довольно часто | + | |
− | + | В приложениях, использующих Solid, довольно часто требуется получить список устройств, поддерживающих определенный | |
− | интерфейс (вполне логично, что программа, которая | + | интерфейс (вполне логично, что программа, которая обрабатывает изображение, поступающее с видеокамер, захочет иметь полный список устройств, поддерживающих интерфейс Camera, а не |
− | + | вообще всех устройств). Для этого можно было бы, конечно, перебрать полный список устройств системы (как мы делали выше), | |
− | + | ||
− | вообще всех устройств). Для этого можно было бы, конечно, | + | |
− | + | ||
проверяя, поддерживает ли каждое из них требуемый интерфейс, | проверяя, поддерживает ли каждое из них требуемый интерфейс, | ||
однако Solid предоставляет нам более простой и эффективный | однако Solid предоставляет нам более простой и эффективный | ||
способ. Класс Solid::Device экспортирует несколько статических | способ. Класс Solid::Device экспортирует несколько статических | ||
− | методов, предназначенных для управления не конкретным | + | методов, предназначенных для управления не конкретным устройством, а всем списком устройств Solid (с одним из них, allDevices(), |
− | + | ||
мы уже знакомы). Статические методы Solid::Device::listFromType() | мы уже знакомы). Статические методы Solid::Device::listFromType() | ||
и Solid::Device::listFromQuery() позволяют получить подмножество | и Solid::Device::listFromQuery() позволяют получить подмножество | ||
− | списка устройств, заданное специальными признаками. Нас | + | списка устройств, заданное специальными признаками. Нас интересует метод 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, как |
− | + | ||
− | плазмоиды хорошо подходят для того, чтобы информировать | + | |
− | + | ||
мы уже видели, может служить источником такого рода событий. | мы уже видели, может служить источником такого рода событий. | ||
− | Для передачи данных от Solid плазмоиду мы воспользуемся | + | Для передачи данных от Solid плазмоиду мы воспользуемся стандартным механизмом поставщиков данных Plasma ([[LXF110:KDE4|LXF110]]). Наш |
− | + | ||
плазмоид будет информировать пользователя о состоянии сетевых | плазмоид будет информировать пользователя о состоянии сетевых | ||
интерфейсов системы (рис. 2). | интерфейсов системы (рис. 2). | ||
+ | |||
Полные исходные тексты поставщика данных (архив | Полные исходные тексты поставщика данных (архив | ||
networkengine) и плазмоида ncview вы найдете на LXFDVD. | networkengine) и плазмоида ncview вы найдете на LXFDVD. | ||
Networkengine предоставляет один источник по имени NCs. Данные | Networkengine предоставляет один источник по имени NCs. Данные | ||
− | представляют собой массив строк (QStringList), в котором | + | представляют собой массив строк (QStringList), в котором содержится перечень сетевых устройств, имена соответствующих сетевых интерфейсов Linux и MAC-адреса устройств (это, собственно |
− | + | ||
− | + | ||
говоря, вся информация, которую можно извлечь из интерфейса | говоря, вся информация, которую можно извлечь из интерфейса | ||
− | NetworkInterface). Рассмотрим объявление главного класса | + | NetworkInterface). Рассмотрим объявление главного класса поставщика данных 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: пришла пора обновиться, если вы до сих пор этого |
− | + | ||
не сделали). Новый элемент здесь – только слот updateDevices(). В | не сделали). Новый элемент здесь – только слот updateDevices(). В | ||
конструкторе класса мы вызываем метод updateSourceEvent(), как | конструкторе класса мы вызываем метод updateSourceEvent(), как | ||
− | и в любом поставщике данных, а затем получаем указатель на | + | и в любом поставщике данных, а затем получаем указатель на глобальный объект 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, как было описано выше. |
− | + | ||
− | + | ||
Далее мы перебираем все элементы списка сетевых устройств | Далее мы перебираем все элементы списка сетевых устройств | ||
и для каждого из них получаем указатель на объект класса | и для каждого из них получаем указатель на объект класса | ||
Строка 324: | Строка 264: | ||
Данные, полученные с помощью интерфейсов, заносятся в список | Данные, полученные с помощью интерфейсов, заносятся в список | ||
QStringList, который передается методу setData(). Метод ifaceName() | QStringList, который передается методу setData(). Метод ifaceName() | ||
− | класса Solid::NetworkInterface возвращает строку с именем | + | класса Solid::NetworkInterface возвращает строку с именем интерфейса (“eth0”, “eth1” и т.д.). С помощью метода hwAddress() |
− | + | ||
мы можем получить MAC-адрес устройства в текстовом виде | мы можем получить MAC-адрес устройства в текстовом виде | ||
(метод macAddress() возвращает MAC-адрес в виде числа). Метод | (метод macAddress() возвращает MAC-адрес в виде числа). Метод | ||
isWireless() вернет значение true, если сетевой интерфейс является | isWireless() вернет значение true, если сетевой интерфейс является | ||
− | беспроводным, и false – в противном случае. Чтобы сделать | + | беспроводным, и false – в противном случае. Чтобы сделать список сетевых устройств более информативным, мы выводим название каждого устройства. Его можно получить с помощью метода |
− | + | product() класса Solid::Device; однако для всех устройств, соответствующих сетевым интерфейсам, он возвращает строку “Network | |
− | + | ||
− | product() класса Solid::Device; однако для всех устройств, | + | |
− | + | ||
Interface”, что, конечно, не очень интересно. Для получения имени | Interface”, что, конечно, не очень интересно. Для получения имени | ||
физического устройства, предоставляющего сетевой интерфейс, | физического устройства, предоставляющего сетевой интерфейс, | ||
− | мы вызываем метод product() устройства, являющегося | + | мы вызываем метод product() устройства, являющегося «родителем» сетевого интерфейса. |
− | + | ||
Нам осталось рассмотреть вопрос о том, как поставщик данных | Нам осталось рассмотреть вопрос о том, как поставщик данных | ||
− | оповещает плазмоид о подключении и отключении сетевых | + | оповещает плазмоид о подключении и отключении сетевых интерфейсов (в [[LXF110:KDE4|LXF110]], напомним, это происходило в результате периодического опроса). Для такого нечастого события, как добавление |
− | + | ||
− | + | ||
и удаление сетевых интерфейсов, регулярный опрос, выполняемый | и удаление сетевых интерфейсов, регулярный опрос, выполняемый | ||
− | плазмоидом, явно избыточен. Для нерегулярных и нечастых | + | плазмоидом, явно избыточен. Для нерегулярных и нечастых событий лучше подходит модель, при которой поставщик сам информирует заинтересованный плазмоид об изменении данных. Наладить |
− | + | этот механизм очень просто. Ниже приводится исходный текст слота updateDevices(), который обрабатывает сигналы deviceAdded() и | |
− | + | ||
− | этот механизм очень просто. Ниже приводится исходный текст | + | |
− | + | ||
deviceRemoved(): | deviceRemoved(): | ||
− | void NetStatEngine::updateDevices() | + | <source lang="cpp-qt">void NetStatEngine::updateDevices() |
{ | { | ||
updateSourceEvent("NCs"); | updateSourceEvent("NCs"); | ||
− | } | + | }</source> |
Не удивляйтесь – это действительно все, что необходимо для | Не удивляйтесь – это действительно все, что необходимо для | ||
оповещения плазмоида. Метод updateSourceEvent() вызывает метод | оповещения плазмоида. Метод updateSourceEvent() вызывает метод | ||
− | setData(), который с помощью соответствующего сигнала | + | setData(), который с помощью соответствующего сигнала активирует слот 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
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Содержание |
[править] Солидная аппаратура
- ЧАСТЬ 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() вернет строку с именем файла устройства.
Практическое знакомство с 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
Работу с интерфейсами устройств мы продемонстрируем на примере динамического плазмоида. Как уже говорилось в 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, которое, возможно, даже оправдает утяжеление будущего дистрибутива на несколько мегабайт. Не забудьте сообщить нам, если получится что-то стоящее!