Evgenii Legotckoi
Evgenii Legotckoi01 квітня 2017 р. 12:16

Qt Linux - Урок 003. Перевірка рівня заряду батареї ноутбука за допомогою libacpi

Зайшла на форумі розмова про перевірку рівня заряду батареї ноутбука під 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, необхідно:

  1. Встановити бібліотеку з репозиторію:
sudo apt-get install libacpi-dev
  1. Підключити бібліотеку в pro-файлі проекту:
LIBS += -lacpi
  1. Підключити заголовковий файл бібліотеки у файлі вихідних кодів, де отримуватиме дані про заряд батареї:
#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);
}

Результат роботи бібліотеки

Результат роботи бібліотеки я назвав би задовільним, хоча вона і визначає відсотки заряду батареї, а також час для повного заряду батареї. Але час до розряду вона не визначила. А також не знайшла жодних вентиляторів та теплових зон. Вважаю, що на моїй моделі ноутбука ці дані просто недоступні. Також траплялися періодичні падіння програми при спробі доступу до деяких параметрів структур.

Скріншот, який наведено на початку статті, відображає ті дані, які бібліотека змогла знайти в моєму ноутбуці. Там звичайно, наведені не всі дані, якщо подивіться заголовковий файл бібліотеки, то виявите, що структури надають багатший вибір параметрів. Але для демонстрації запуску бібліотеки цього вважаю за достатнє.

Спробуйте запустити проект у себе і по можливості відпишіться, якщо у вас в ноутбуці дана бібліотека знайде теплові зони та вентилятори.

Проект заявки

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Дмитрий

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:60бали,
  • Рейтинг балів-1
Дмитрий

C++ - Тест 003. Условия и циклы

  • Результат:92бали,
  • Рейтинг балів8
d
  • dsfs
  • 26 квітня 2024 р. 04:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 21:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
George1307 травня 2024 р. 00:27
добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
BlinCT
BlinCT05 травня 2024 р. 05:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
PS
Peter Son03 травня 2024 р. 17:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi02 травня 2024 р. 14:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 04:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

Слідкуйте за нами в соціальних мережах