Зайшла на форумі розмова про перевірку рівня заряду батареї ноутбука під Linux за допомогою бібліотеки Qt. За фактом Qt не надає на даний момент такого класу, який давав би можливість перевіряти заряд батареї.
Спробуємо створити додаток, який забиратиме хоч якісь дані, і який виглядатиме таким чином:
libacpi
Сама бібліотека написана на C і має такі, що цікавлять функції та структури даних:
- global_t - глобальна структура даних, яка містить у собі кількість вентиляторів, батарей та теплових зон, а також структуру з адаптером живлення.
- init_acpi_batt(global_t *globals) - ініціалізація даних про батареї
- init_acpi_acadapt(global_t *globals) - ініціалізація даних про адаптер
- init_acpi_fan(global_t *globals) - ініціалізація даних про вентилятори
- init_acpi_thermal(global_t *globals) - ініціалізація даних про теплові зони
- read_acpi_batt(int num) - зчитування даних про батареї
- read_acpi_zone(int num, global_t *globals) - зчитування даних про теплові зони
- read_acpi_fan(int num) - зчитування даних про вентилятори
- battery_t - структура даних про батарею
- fans_t - структура даних про вентилятори
- thermal_t - структура даних про теплову зону
- battery_t batteries [MAX_ITEMS] - масив батарей, в нього зберігаються дані при виклику read_acpi_batt
- thermal_t thermals [MAX_ITEMS] - масив теплових зон, в нього зберігаються дані при виклику read_acpi_zone
- fan_t fans [MAX_ITEMS] - масив вентиляторів, у нього зберігаються дані при викликі read_acpi_fan
Використання бібліотеки
Щоб використовувати бібліотеку у проекті на Qt, необхідно:
- Встановити бібліотеку з репозиторію:
sudo apt-get install libacpi-dev
- Підключити бібліотеку в pro-файлі проекту:
LIBS += -lacpi
- Підключити заголовковий файл бібліотеки у файлі вихідних кодів, де отримуватиме дані про заряд батареї:
#ifdef __cplusplus extern "C" { #endif #include <libacpi.h> #ifdef __cplusplus } #endif
Головне вікно програми
Головне вікно є класом Widget, який зроблений з використанням ui файлу і інтерфейс якого зроблений через графічний дизайнер. У ньому є ScrollArea, в якій розміщено об'єкт вертикального розміщення елементів, віджетів, які будуть створюватися динамічно, залежно від того, що змогла знайти бібліотека. Також є кнопка для запуску зчитування даних бібліотекою.
віджет.h
У заголовному файлі оголошено вектор з віджетами для батарей, вентиляторів і теплових зон. Також оголошено покажчик на віджет із адаптером живлення. Усі віджети є кастомними класами. Трохи далі буде як приклад наведено код одного з віджетів.
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "batterywidget.h" #include "adapterwidget.h" #include "fanwidget.h" #include "thermalwidget.h" namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: void on_pushButton_clicked(); private: Ui::Widget *ui; QVector<BatteryWidget*> m_butteryWidgetList; // Вектор виджетов с батареями QVector<FanWidget*> m_fanWidgetList; // Вектор виджетов с вентиляторами QVector<ThermalWidget*> m_thermalWidgetList; // Вектор виджетов с тепловыми зонами AdapterWidget* m_adapterWidget; // Виджет для адаптера питания всегда один }; #endif // WIDGET_H
widget.cpp
Оскільки бібліотека написана на C, їй властивий процедурний стиль виконання коду. А також є одна маленька хитрість. Справа в тому, що в бібліотеці є кілька глобальних змінних, які записуються дані. Це масиви з вентиляторами, тепловими зонами та батареями.
Тобто, коли ви викликаєте функцію read_acpi_batt(1) , то записуєте структуру даних battery_t в осередок масиву batteries[1].
Тоді як для створення глобальної структури, що містить дані про кількість всіх вище перерахованих структур, необхідно виділяти пам'ять.
#include "widget.h" #include "ui_widget.h" #ifdef __cplusplus extern "C" { #endif #include <libacpi.h> #ifdef __cplusplus } #endif #include <QFileDialog> Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget), m_adapterWidget(nullptr) { ui->setupUi(this); } Widget::~Widget() { delete ui; } void Widget::on_pushButton_clicked() { // Выделяем память под глобальную структуру данных бибилотеки global_t *global = static_cast<global_t*>(malloc(sizeof(global_t))); // Инициализируем структуру для init_acpi_batt(global); // батареи init_acpi_acadapt(global); // адаптера питания init_acpi_fan(global); // вентиляторов init_acpi_thermal(global); // тепловых зон // Battery battery_t *binfo; // Если нашлись батареи, то считываем в цикле данные и добавляем виджеты for(int i=0; i < global->batt_count; i++) { read_acpi_batt(i); // считываем данные // которые считываются в специальный массив batteries - он есть в самой библиотеке binfo = &batteries[i]; BatteryWidget* butteryWidget; // Создаём виджет if (m_butteryWidgetList.count() <= i) { butteryWidget = new BatteryWidget(this); butteryWidget->setTitle(QString(binfo->name)); m_butteryWidgetList.append(butteryWidget); ui->verticalLayout->addWidget(butteryWidget); } else { // Или забираем его из списка виджетов butteryWidget = m_butteryWidgetList.at(i); } // Устанавливаем butteryWidget->setPercentage(binfo->percentage); // Процент заряда butteryWidget->setChargeTime(binfo->charge_time); // Время до полного заряда butteryWidget->setRemainingTime(binfo->remaining_time); // Время до полного разряда // Определяем статус заряда switch (binfo->charge_state) { case C_CHARGE: butteryWidget->setChargeState("Charging"); break; case C_DISCHARGE: butteryWidget->setChargeState("Discharging"); case C_CHARGED: butteryWidget->setChargeState("Charged"); case C_NOINFO: butteryWidget->setChargeState("No info"); default: butteryWidget->setChargeState("Error"); break; } } // Adapter // Настройка виджета для адптера питания adapter_t *ac = &global->adapt; if (!m_adapterWidget) { m_adapterWidget = new AdapterWidget(this); ui->verticalLayout->addWidget(m_adapterWidget); } // Здесь проверяем только, подключён или нет switch (ac->ac_state) { case P_AC: m_adapterWidget->setStatus("Runs on AC"); break; case P_BATT: m_adapterWidget->setStatus("Runs on battery"); default: m_adapterWidget->setStatus("Error"); break; } // Fan fan_t *finfo; // Если нашлись вентиляторы, то считываем в цикле данные и добавляем виджеты for(int i=0; i < global->fan_count; i++) { read_acpi_fan(i); // считываем данные // которые считываются в специальный массив fans - он есть в самой библиотеке finfo = &fans[i]; FanWidget* fanWidget; // Создаём виджет if (m_fanWidgetList.count() <= i) { fanWidget = new FanWidget(this); fanWidget->setTitle(QString(finfo->name)); m_fanWidgetList.append(fanWidget); ui->verticalLayout->addWidget(fanWidget); } else { // Или забираем его из списка виджетов fanWidget = m_fanWidgetList.at(i); } // Устанавливаем статус, включён или нет switch (finfo->fan_state) { case F_ON: fanWidget->setStatus("ON"); break; case F_OFF: fanWidget->setStatus("OFF"); default: fanWidget->setStatus("Error"); break; } } // Thermal thermal_t *tinfo; // Если нашлись тепловые зоны, то считываем в цикле данные и добавляем виджеты for(int i=0; i < global->fan_count; i++) { read_acpi_zone(i, global); // считываем данные // которые считываются в специальный массив thermals - он есть в самой библиотеке tinfo = &thermals[i]; ThermalWidget* thermalWidget; // Создаём виджет if (m_thermalWidgetList.count() <= i) { thermalWidget = new ThermalWidget(this); thermalWidget->setTitle(QString(tinfo->name)); m_thermalWidgetList.append(thermalWidget); ui->verticalLayout->addWidget(thermalWidget); } else { // Или забираем его из списка виджетов thermalWidget = m_thermalWidgetList.at(i); } thermalWidget->setTemperature(tinfo->temperature); // Устанавливаем температуру thermalWidget->setFrequency(tinfo->frequency); // и частоту } free(global); }
Віджети
У програмі є віджети для всіх вище перерахованих структур. Вони служать лише для відображення інформації та динамічного додавання. Наведу як приклад віджет, який відображає інформацію про батарею.
batterywidget.h
#ifndef BATTERYWIDGET_H #define BATTERYWIDGET_H #include <QWidget> namespace Ui { class BatteryWidget; } class BatteryWidget : public QWidget { Q_OBJECT public: explicit BatteryWidget(QWidget *parent = 0); ~BatteryWidget(); void setTitle(QString title); // Название батареи (как определено в ОС Linux) void setPercentage(int percentage); // Процент заряда void setChargeTime(int chargeTime); // Время до полного заряда в минутах void setRemainingTime(int remainingTime); // Время до полного разряда в минутах void setChargeState(QString chargeState); // Состояние private: Ui::BatteryWidget *ui; }; #endif // BATTERYWIDGET_H
batterywidget.cpp
#include "batterywidget.h" #include "ui_batterywidget.h" BatteryWidget::BatteryWidget(QWidget *parent) : QWidget(parent), ui(new Ui::BatteryWidget) { ui->setupUi(this); } BatteryWidget::~BatteryWidget() { delete ui; } void BatteryWidget::setTitle(QString title) { ui->label_title->setText(title); } void BatteryWidget::setPercentage(int percentage) { ui->label_percentage->setText(QString::number(percentage)); } void BatteryWidget::setChargeTime(int chargeTime) { ui->label_charge_time->setText(QString("%1:%2").arg(chargeTime / 60).arg(chargeTime % 60)); } void BatteryWidget::setRemainingTime(int remainingTime) { ui->label_remaining_time->setText(QString("%1:%2").arg(remainingTime / 60).arg(remainingTime % 60)); } void BatteryWidget::setChargeState(QString chargeState) { ui->label_charge_state->setText(chargeState); }
Результат роботи бібліотеки
Результат роботи бібліотеки я назвав би задовільним, хоча вона і визначає відсотки заряду батареї, а також час для повного заряду батареї. Але час до розряду вона не визначила. А також не знайшла жодних вентиляторів та теплових зон. Вважаю, що на моїй моделі ноутбука ці дані просто недоступні. Також траплялися періодичні падіння програми при спробі доступу до деяких параметрів структур.
Скріншот, який наведено на початку статті, відображає ті дані, які бібліотека змогла знайти в моєму ноутбуці. Там звичайно, наведені не всі дані, якщо подивіться заголовковий файл бібліотеки, то виявите, що структури надають багатший вибір параметрів. Але для демонстрації запуску бібліотеки цього вважаю за достатнє.
Спробуйте запустити проект у себе і по можливості відпишіться, якщо у вас в ноутбуці дана бібліотека знайде теплові зони та вентилятори.