12 QSizePolicy policy{QSizePolicy::Policy::Fixed, QSizePolicy::Policy::Fixed};
13 setSizePolicy(policy);
19 QWidget::paintEvent(event);
26 fg = QColor(200, 0, 0);
30 fg = QColor(200, 100, 0);
33 if (state != dto::BatteryState::discharging)
35 fg = QColor(255, 255, 255);
39 QColor bg(fg.red() * .6, fg.green() * .6, fg.blue() * .6);
40 int ht =
event->rect().height();
43 painter.setRenderHint(QPainter::Antialiasing,
true);
45 int m = 1 * ht / 10 + 1;
46 QRect cap(QPoint(m, m), QPoint(wd - m, 2 * m));
47 painter.fillRect(cap, filled == 100 ? fg : bg);
48 QRect body(QPoint(0, 2 * m), QPoint(wd, ht - m));
49 painter.fillRect(body, bg);
50 float dist = (1.0 - filled * .01);
51 QRect bodyFull(QPoint(0, 2 * m + dist * (ht - 3 * m)), QPoint(wd, ht - m));
52 painter.fillRect(bodyFull, fg);
57 a = QRect(QPoint(m, m), QPoint(wd - m, m + lWd));
58 painter.fillRect(
a, bg);
59 a = QRect(QPoint(wd - m - lWd, m), QPoint(wd - m, 2 * m + lWd));
60 painter.fillRect(
a, bg);
61 a = QRect(QPoint(wd - m - lWd, 2 * m), QPoint(wd, 2 * m + lWd));
62 painter.fillRect(
a, bg);
63 a = QRect(QPoint(wd - lWd, 2 * m), QPoint(wd, ht - m));
64 painter.fillRect(
a, bg);
65 a = QRect(QPoint(0, ht - m - lWd), QPoint(wd, ht - m));
66 painter.fillRect(
a, bg);
67 a = QRect(QPoint(0, 2 * m), QPoint(lWd, ht - m));
68 painter.fillRect(
a, bg);
69 a = QRect(QPoint(0, 2 * m), QPoint(m + lWd, 2 * m + lWd));
70 painter.fillRect(
a, bg);
71 a = QRect(QPoint(m, m), QPoint(m + lWd, 2 * m + lWd));
72 painter.fillRect(
a, bg);
75 if (state == dto::BatteryState::charging)
78 float lHeight = .6 * ht;
79 int xOff = .1 * ht + 1;
80 int yOff = .2 * ht + 1;
81 poly << QPoint(xOff + 7 * lHeight / 20, yOff)
82 << QPoint(xOff + 5 * lHeight / 20, yOff + 4.5 * lHeight / 10)
83 << QPoint(xOff + 10 * lHeight / 20, yOff + 4.5 * lHeight / 10)
84 << QPoint(xOff + 3 * lHeight / 20, yOff + lHeight)
85 << QPoint(xOff + 5 * lHeight / 20, yOff + 5.5 * lHeight / 10)
86 << QPoint(xOff + 0 * lHeight / 20, yOff + 5.5 * lHeight / 10)
87 << QPoint(xOff + 7 * lHeight / 20, yOff);
89 path.addPolygon(poly);
90 painter.fillPath(path, QColor(40, 40, 40));
94 if (state == dto::BatteryState::unavailable)
98 poly << QPoint(u, .1 * ht) << QPoint(wd - 2 * u, .9 * ht) << QPoint(wd - u, .9 * ht)
99 << QPoint(2 * u, .1 * ht) << QPoint(u, .1 * ht);
101 path.addPolygon(poly);
102 painter.fillPath(path, QColor(200, 0, 0));
104 poly2 << QPoint(wd - 2 * u, .1 * ht) << QPoint(wd - u, .1 * ht)
105 << QPoint(2 * u, .9 * ht) << QPoint(u, .9 * ht) << QPoint(wd - 2 * u, .1 * ht);
107 path2.addPolygon(poly2);
108 painter.fillPath(path2, QColor(200, 0, 0));
117 this->percentage =
std::clamp(percentage, 0, 100);
129 return QSize{16, 32};
133 mainWindow(mainWindow), timer(new QTimer(this))
135 qRegisterMetaType<dto::BatteryStatus>(
"dto::BatteryStatus");
136 QGridLayout* layout =
new QGridLayout(this->
getWidget());
137 this->
getWidget()->setContentsMargins(4, 0, 0, 0);
139 layout->addWidget(icon, 0, 0);
140 layout->setContentsMargins(0, 0, 0, 0);
142 icon->setVisible(
true);
144 txtPercentage = QString(
"%1%").arg(lastStatus.energyFromFullCharge_pct);
145 lbl->setText(txtPercentage);
146 lbl->setVisible(
false);
147 layout->addWidget(lbl, 0, 1);
149 qRegisterMetaType<dto::BatteryState>(
"BatteryState");
151 timer->setInterval(std::chrono::milliseconds(1
'000));
152 connect(timer, &QTimer::timeout, this, &BatteryWidget::updateBatteryStatus);
153 connect(this, &BatteryWidget::startPeriodicStateUpdate, timer, qOverload<>(&QTimer::start));
154 connect(this, &BatteryWidget::stopPeriodicStateUpdate, timer, &QTimer::stop);
156 QString style = QString("QToolTip {"
157 // StyleSheet for tool tip
158 " background: white;"
161 getWidget()->setStyleSheet(style);
167 BatteryWidget::getBatteryWidget()
169 return this->getWidget();
173 BatteryWidget::updateTooltip()
175 dto::BatteryState state = lastStatus.state;
179 if (state == dto::BatteryState::unavailable)
181 text = "No battery detected.";
183 else if (state == dto::BatteryState::full)
185 text = "Battery fully charged.";
192 case dto::BatteryState::charging:
195 case dto::BatteryState::discharging:
196 status = "discharging";
198 case dto::BatteryState::full:
201 case dto::BatteryState::notCharging:
202 status = "not charging";
204 case dto::BatteryState::unavailable:
205 status = "unavailable";
209 QString detailedReport;
211 QString charge = QString(" - Charge: %1<br>").arg(txtPercentage);
213 QString energy = QString(" - Energy: %2 Wh / %3 Wh<br>")
214 .arg(lastStatus.energy_Wh, 0, 'f
', 1)
215 .arg(lastStatus.fullChargeEnergy_Wh, 0, 'f
', 1);
217 QString power = QString(" - %1: %2 W (%3 A, %4 V)<br>")
218 .arg(state == dto::BatteryState::charging ? "Charging power"
220 .arg(std::abs(lastStatus.power_W), 0, 'f
', 1)
221 .arg(std::abs(lastStatus.current_A), 0, 'f
', 1)
222 .arg(lastStatus.voltage_V, 0, 'f
', 1);
225 state == dto::BatteryState::charging or state == dto::BatteryState::discharging;
226 QString prognosis = "";
229 QString fullOrEmpty = state == dto::BatteryState::charging ? "Full" : "Empty";
230 prognosis = QString(" - %1 in: ≈ %2 h <br>")
232 .arg(lastStatus.remainingTime_h, 0, 'f
', 1);
235 QString temperature = QString(" - Temperature: %6 °C<br>")
236 .arg(lastStatus.temperature_degC, 0, 'f
', 1);
238 QString health = QString(" - Health: %7%")
239 .arg(lastStatus.fullEnergyFromDesignEnergy_pct, 0, 'f
', 1);
241 detailedReport = QString("%1%2%3%4%5%6")
242 .arg(charge, energy, power, prognosis, temperature, health);
247 ? "<br><b><font color='red'>Battery error occured! Check the logs.</font></b>"
250 text = QString("Battery %1.<br>%2%3").arg(status).arg(detailedReport).arg(errorsText);
253 getWidget()->setToolTip(text);
255 if (not QToolTip::isVisible())
260 QPoint p = getWidget()->mapFromGlobal(QCursor::pos());
262 if (p.x() >= 0 and p.y() >= 0 and p.x() < getWidget()->width() and
263 p.y() < getWidget()->height())
265 QToolTip::showText(QCursor::pos(), text);
270 BatteryWidget::onInitComponent()
272 usingProxy(std::string{battery_master_default_name});
276 BatteryWidget::onConnectComponent()
278 QMetaObject::invokeMethod(icon, "setVisible", Qt::QueuedConnection, Q_ARG(bool, true));
280 ARMARX_INFO << "Battery widget connected.";
281 batteryManagement = getProxy<armarx::BatteryManagementInterfacePrx>(
282 std::string{battery_master_default_name});
284 emit startPeriodicStateUpdate();
288 BatteryWidget::onDisconnectComponent()
291 << "Battery widget disconnected. This is expected if the earlier connected "
292 "robot unit shut down.";
294 QMetaObject::invokeMethod(this,
296 Qt::QueuedConnection,
297 Q_ARG(dto::BatteryStatus, invalidStatus));
299 emit stopPeriodicStateUpdate();
303 BatteryWidget::loadSettings(QSettings* settings)
309 BatteryWidget::saveSettings(QSettings* settings)
315 BatteryWidget::updateBatteryStatus()
317 dto::BatteryStatus newStatus;
321 newStatus = batteryManagement->getBatteryStatus();
325 ARMARX_DEBUG << deactivateSpam(60) << "Failed to get current battery percentage";
326 newStatus.state = dto::BatteryState::unavailable;
329 QMetaObject::invokeMethod(
330 this, "setBatteryStatus", Qt::QueuedConnection, Q_ARG(dto::BatteryStatus, newStatus));
334 BatteryWidget::setBatteryStatus(dto::BatteryStatus batteryStatus)
336 lastStatus = batteryStatus;
338 icon->setPercentage(lastStatus.energyFromFullCharge_pct);
339 txtPercentage = QString("%1%").arg(lastStatus.energyFromFullCharge_pct, 0, 'f
', 1);
340 lbl->setText(txtPercentage);
342 dto::BatteryState state = lastStatus.state;
344 icon->setState(state);
345 if (state == dto::BatteryState::unavailable)
347 lbl->setVisible(false);
351 lbl->setVisible(true);
359 BatteryWidget::getDefaultName() const
361 return "BatteryWidget" + iceNameUUID;
364 } // namespace armarx