[Simulator] Asynchronous SimulatorInterface & a few new features. (#4738)

* [Simulator] Create RadioKeyWidget class for UI buttons & refactor ButtonsWidget; Refactor SimulatedUIWidget (and subtypes) to use new RadioKeyWidgets/ButtonsWidget; Centralize help text for key mappings and get creative with some icons; Simplify some radio UI setups with rectangular buttons.

* [Simulator] Convert all simulator data I/O to signals/slots mechanism:
    * SimulatorInterface/OpenTxSimulator:
       - Now inherits from QObject to allow signal/slot interface;
       - Allows data exchange on a per-item basis (eg. each I/O value is treated separately instead of sending whole arrays or structs of data);
       - Checks for data changes and only emits signals when change is detected (GUI can now assume only new values are being sent);
       - Manages its own 10ms timer (doesn't rely on GUI to do that);
       - Sends "heartbeat" signals @ 1Hz for status monitoring;
    * Simulator GUI:
       - All data is exchanged between GUI elements as well as SimulatorInterface via signals/slots using standardized methods;
       - Data is sent immediately, and only, when actually changed (eg. a control is moved) instead of in bulk at specific time intervals;
       - Similarly, an asynchronous method is used for reading incoming data, w/out timers or loops;
       - Improve VirtualJoystickWidget to be more encapsulated and configurable;
       - Pause telemetry simulator if window is hidden;

* [Simulator] Move SimulatorInterface instance to separate thread, ensure safe asynchronous operations & proper timer interactions; Protect/remove some functions, & reorganize the order (cosmetics).

* [Simulator] Traces are now delivered to OpenTxSimulator and one or more QIODevice(s) can be added as recipient(s); Add SimulatorInterface::getCapability() for compile-time settings; Remove reversed POT1/SLIDER1 mixer exception (Taranis) requirement for SIMU; Fix plus/minus key delay on wheel event w/out encoder.

* [Simulator] Add current knob/slider/trim input value in tool-tips (KnobWidget and SliderWidget).

* [Simulator] Fix trims widget internal value not properly updating, and remove trim influence on virtual joystick X/Y value display (closes #4671).

* [SimulatorInterface] Add handling of transmitter input voltage, including a rough conversion of volts to ADC value for different boards, and default battery volts lookup function; Clear analogs array before starting.

* [Simulator] Add SimulatorInterface::init() method to separate pre-startup tasks; Report actual trim range, not just extended on/off; Change how radio widget states are restored; VirtualJoystickWidget: Connect trim changes directly from simulator, connect joystick events directly, report stick mode directly instead of setting values/constraints externally.

* [Simulator] Calculate default Tx V input based on configured range in radio settings (or warning V+2 for radios which don't support a range).

* [Simulator] Add functional aux. trims for Horus (closes #4699).

* [Companion] Remove problematic QMessageLogContext from AppDebugMessageHandler::messageOutput().

* [Simulator] Prevent trim change via slider if disabled for flight mode (closes #4600).

* [OpenTxSimulator] Fixes for Qt < 5.4.

* [OpenTxSimulator] Fix slot name.
This commit is contained in:
Max Paperno
2017-04-02 12:17:37 +02:00
committed by Bertrand Songis
parent 36bb951314
commit 57dc0159d6
46 changed files with 2058 additions and 1321 deletions
@@ -22,47 +22,12 @@
#define _BUTTONSWIDGET_H_
#include "radiouiaction.h"
#include "radiokeywidget.h"
#include <QWidget>
#include <QtGui>
#include <QStyleOption>
class Area : public QObject
{
Q_OBJECT
public:
explicit Area(const QPolygon & polygon, const QString &image, RadioUiAction * action = NULL, QObject * parent = NULL):
QObject(parent),
polygon(polygon),
imgFile(image),
action(action)
{
if (action)
connect(action, &RadioUiAction::toggled, this, &Area::onActionToggled);
}
bool contains(int x, int y)
{
return polygon.containsPoint(QPoint(x, y), Qt::OddEvenFill);
}
void onActionToggled(int, bool active)
{
if (active)
emit imageChanged(imgFile);
else
emit imageChanged("");
}
QPolygon polygon;
QString imgFile;
RadioUiAction * action;
signals:
void imageChanged(const QString image);
};
class ButtonsWidget : public QWidget
{
Q_OBJECT
@@ -80,18 +45,17 @@ class ButtonsWidget : public QWidget
QWidget::setStyleSheet(sheet);
}
void addArea(int x1, int y1, int x2, int y2, const char * image, RadioUiAction * action = NULL)
RadioKeyWidget * addArea(const QRect & rect, const char * image, RadioUiAction * action = NULL)
{
QPolygon polygon;
polygon.setPoints(4, x1, y1, x1, y2, x2, y2, x2, y1);
addArea(polygon, image, action);
return addArea(QPolygon(rect), image, action);
}
void addArea(const QPolygon & polygon, const char * image, RadioUiAction * action = NULL)
RadioKeyWidget * addArea(const QPolygon & polygon, const char * image, RadioUiAction * action = NULL)
{
Area * area = new Area(polygon, image, action, this);
areas.push_back(area);
connect(area, &Area::imageChanged, this, &ButtonsWidget::setBitmap);
RadioKeyWidget * btn = new RadioKeyWidget(polygon, image, action, this);
m_buttons.append(btn);
connect(btn, &RadioKeyWidget::imageChanged, this, &ButtonsWidget::setBitmap);
return btn;
}
protected:
@@ -113,16 +77,14 @@ class ButtonsWidget : public QWidget
return;
}
foreach(Area * area, areas) {
if (!area->action)
continue;
if (area->contains(event->x(), event->y())) {
area->action->trigger(press);
foreach(RadioKeyWidget * key, m_buttons) {
if (key->contains(event->pos())) {
key->toggle(press);
event->accept();
return;
}
else if (area->action->isActive()) {
area->action->trigger(false);
else if (key->getValue()) {
key->toggle(false);
}
}
event->ignore();
@@ -145,7 +107,7 @@ class ButtonsWidget : public QWidget
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
QList<Area *> areas;
QList<RadioKeyWidget *> m_buttons;
QString defaultStyleSheet;
};
@@ -0,0 +1,100 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef RADIOKEYWIDGET_H
#define RADIOKEYWIDGET_H
#include "radiouiaction.h"
#include "radiowidget.h"
class RadioKeyWidget : public RadioWidget
{
Q_OBJECT
public:
explicit RadioKeyWidget(const QPolygon & polygon, const QString &image, RadioUiAction * action = NULL, QWidget * parent = NULL, Qt::WindowFlags f = Qt::WindowFlags()):
RadioWidget(action, parent, f),
polygon(polygon),
imgFile(image)
{
m_type = RADIO_WIDGET_KEY;
setValue(0);
hide(); // we're a "virtual" button for now
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
virtual void setAction(RadioUiAction * action)
{
if (m_action)
disconnect(m_action, 0, this, 0);
RadioWidget::setAction(action);
if (m_action)
connect(m_action, &RadioUiAction::toggled, this, &RadioKeyWidget::onActionToggled);
}
virtual int getValue() const
{
return (m_action && m_action->isActive()) ? 1 : 0;
}
void press() {
if (m_action)
m_action->trigger(true);
}
void release() {
if (m_action)
m_action->trigger(false);
}
void toggle(bool down)
{
if (down)
press();
else
release();
}
bool contains(const QPoint & point)
{
return polygon.containsPoint(point, Qt::OddEvenFill);
}
signals:
void imageChanged(const QString image);
protected:
virtual void onActionToggled(int index, bool active)
{
RadioWidget::onActionToggled(index, active);
emit imageChanged(active ? imgFile : "");
}
QPolygon polygon;
QString imgFile;
};
#endif // RADIOKEYWIDGET_H
@@ -26,7 +26,7 @@
#include <QDial>
#include <QMouseEvent>
#include <QDebug>
#include <QToolTip>
#include <math.h>
class RadioKnobWidget : public RadioWidget
@@ -65,7 +65,6 @@ class RadioKnobWidget : public RadioWidget
Board::PotType m_potType;
class KnobWidget : public QDial
{
public:
@@ -73,6 +72,8 @@ class RadioKnobWidget : public RadioWidget
explicit KnobWidget(Board::PotType type, QWidget * parent = 0):
QDial(parent)
{
m_toolTip = tr("<p>Value (input): <b>%1</b></p>");
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setFixedSize(QSize(42, 42));
setNotchesVisible(true);
@@ -88,7 +89,7 @@ class RadioKnobWidget : public RadioWidget
}
else {
m_stepSize = 1;
setToolTip(tr("Right-double-click to reset to center."));
m_toolTip.append(tr("Right-double-click to reset to center."));
setMinimum(-1024);
setMaximum(1024);
setPageStep(128);
@@ -104,6 +105,19 @@ class RadioKnobWidget : public RadioWidget
QDial::setValue(value);
}
bool event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent * helpEvent = static_cast<QHelpEvent *>(event);
if (helpEvent) {
QToolTip::showText(helpEvent->globalPos(), m_toolTip.arg(this->value()));
return true;
}
}
return QWidget::event(event);
}
void mousePressEvent(QMouseEvent * event)
{
if (m_stepSize == 1 && event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
@@ -129,6 +143,7 @@ class RadioKnobWidget : public RadioWidget
}
quint16 m_stepSize;
QString m_toolTip;
};
};
@@ -110,9 +110,8 @@ class RadioSwitchWidget : public RadioWidget
void setToggleLocked(bool lock)
{
if (m_flags != (quint16)lock) {
m_flags = lock;
setFlags((quint16)lock);
setValue((int)lock);
emit flagsChanged(m_flags);
}
}
@@ -22,6 +22,7 @@
#define _RADIOTRIMWIDGET_H_
#include "radiowidget.h"
#include "boards.h"
#include "sliderwidget.h"
#include <QToolButton>
@@ -38,12 +39,6 @@ class RadioTrimWidget : public RadioWidget
{
init(orientation);
}
explicit RadioTrimWidget(const QString & labelText, Qt::Orientation orientation = Qt::Vertical, int value = 0, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
RadioWidget(labelText, value, parent, f),
m_slider(NULL)
{
init(orientation);
}
void setIndices(int sliderIdx = -1, int decrBtnIdx = -1, int incrBtnIdx = -1)
{
@@ -58,11 +53,27 @@ class RadioTrimWidget : public RadioWidget
m_slider->setRange(min, max);
}
signals:
void setTrimRangeQual(const int index, const int min, const int max)
{
if (index == m_index || index == Board::TRIM_AXIS_COUNT)
setTrimRange(min, max);
}
void trimButtonPressed(int index);
void trimButtonReleased(int index);
void trimSliderMoved(int index, int value);
void setTrimValue(const int index, const int value)
{
if (index == m_index || index == Board::TRIM_AXIS_COUNT)
setValue(value);
}
void setValue(const int & value)
{
if (sender() && qobject_cast<SliderWidget *>(sender())) {
RadioWidget::setValue(value);
}
else if (m_slider) {
m_slider->setValue(value);
}
}
protected slots:
@@ -75,20 +86,20 @@ class RadioTrimWidget : public RadioWidget
setTrimRange(-125, 125);
setIndices();
QSize btnIcnSz(12, 12);
const QSize btnSz(18, 18);
QWidget * trimWidget = new QWidget(this);
QBoxLayout * trimLayout = new QVBoxLayout(trimWidget);
trimLayout->setSpacing(5);
trimLayout->setContentsMargins(8, 8, 8, 8);
trimLayout->setSpacing(4);
QToolButton * trimBtnInc = new QToolButton(trimWidget);
trimBtnInc->setIconSize(btnIcnSz);
trimBtnInc->setMaximumSize(btnSz);
QToolButton * trimBtnDec = new QToolButton(trimWidget);
trimBtnDec->setIconSize(btnIcnSz);
trimBtnDec->setMaximumSize(btnSz);
Qt::Alignment algn;
if (orientation == Qt::Horizontal) {
trimWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
trimLayout->setDirection(QBoxLayout::RightToLeft);
trimLayout->setContentsMargins(0, 8, 0, 8);
trimBtnInc->setArrowType(Qt::RightArrow);
trimBtnDec->setArrowType(Qt::LeftArrow);
algn = Qt::AlignVCenter;
@@ -96,6 +107,7 @@ class RadioTrimWidget : public RadioWidget
else {
trimWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
trimLayout->setDirection(QBoxLayout::TopToBottom);
trimLayout->setContentsMargins(8, 0, 8, 0);
trimBtnInc->setArrowType(Qt::UpArrow);
trimBtnDec->setArrowType(Qt::DownArrow);
algn = Qt::AlignHCenter;
@@ -107,21 +119,11 @@ class RadioTrimWidget : public RadioWidget
setWidget(trimWidget, algn);
connect(m_slider, &SliderWidget::valueChanged, this, &RadioWidget::setValue);
connect(this, &RadioWidget::valueChanged, this, &RadioTrimWidget::onValueChanged);
connect(trimBtnInc, &QToolButton::pressed, [this]() { emit trimButtonPressed(m_btnIncIndex); });
connect(trimBtnInc, &QToolButton::released, [this]() { emit trimButtonReleased(m_btnIncIndex); });
connect(trimBtnDec, &QToolButton::pressed, [this]() { emit trimButtonPressed(m_btnDecIndex); });
connect(trimBtnDec, &QToolButton::released, [this]() { emit trimButtonReleased(m_btnDecIndex); });
}
void onValueChanged(int value)
{
m_slider->blockSignals(true);
m_slider->setValue(value);
m_slider->blockSignals(false);
emit trimSliderMoved(m_index, m_value);
connect(m_slider, &SliderWidget::valueChanged, this, &RadioTrimWidget::setValue);
connect(trimBtnInc, &QToolButton::pressed, [this]() { emit valueChange(m_type, m_btnIncIndex, RADIO_TRIM_BTN_ON); });
connect(trimBtnInc, &QToolButton::released, [this]() { emit valueChange(m_type, m_btnIncIndex, RADIO_TRIM_BTN_OFF); });
connect(trimBtnDec, &QToolButton::pressed, [this]() { emit valueChange(m_type, m_btnDecIndex, RADIO_TRIM_BTN_ON); });
connect(trimBtnDec, &QToolButton::released, [this]() { emit valueChange(m_type, m_btnDecIndex, RADIO_TRIM_BTN_OFF); });
}
protected:
@@ -130,5 +132,4 @@ class RadioTrimWidget : public RadioWidget
int m_btnIncIndex;
};
#endif // _RADIOTRIMWIDGET_H_
@@ -18,51 +18,66 @@
* GNU General Public License for more details.
*/
#include "radiouiaction.h"
#include "radiowidget.h"
RadioWidget::RadioWidget(QWidget * parent, Qt::WindowFlags f) :
QWidget(parent, f),
m_gridLayout(NULL),
m_controlWidget(NULL),
m_nameLabel(NULL),
m_action(NULL),
m_value(0),
m_flags(0),
m_invertValue(false),
m_valueReset(false),
m_showLabel(false),
m_labelText(""),
m_type(RADIO_WIDGET_NONE)
m_labelText("")
{
init();
qRegisterMetaType<RadioWidgetState>();
setIndex(-1);
setType(RADIO_WIDGET_NONE);
}
RadioWidget::RadioWidget(RadioUiAction * action, QWidget * parent, Qt::WindowFlags f) :
RadioWidget(parent, f)
{
setAction(action);
}
RadioWidget::RadioWidget(const QString & labelText, int value, QWidget * parent, Qt::WindowFlags f) :
QWidget(parent, f),
m_value(value),
m_flags(0),
m_invertValue(false),
m_showLabel(true),
m_labelText(labelText),
m_type(RADIO_WIDGET_NONE)
RadioWidget(parent, f)
{
init();
setValue(value);
setLabelText(labelText);
}
void RadioWidget::setIndex(int index)
void RadioWidget::setIndex(const int & index)
{
m_index = index;
}
void RadioWidget::setInvertValue(bool invertValue)
void RadioWidget::setType(const RadioWidgetType & type)
{
m_invertValue = invertValue;
m_type = type;
}
void RadioWidget::setValue(int value)
void RadioWidget::setValue(const int & value)
{
if (value != m_value) {
if (value != m_value || m_valueReset) {
m_value = value;
m_valueReset = false;
emit valueChanged(value);
emit valueChange(m_type, m_index, value);
}
}
void RadioWidget::setFlags(quint16 flags)
void RadioWidget::setValueQual(const RadioWidget::RadioWidgetType & type, const int & index, const int & value)
{
if (type == m_type && index == m_index)
setValue(value);
}
void RadioWidget::setFlags(const quint16 & flags)
{
if (flags != m_flags) {
m_flags = flags;
@@ -70,7 +85,7 @@ void RadioWidget::setFlags(quint16 flags)
}
}
void RadioWidget::setShowLabel(bool show)
void RadioWidget::setShowLabel(const bool show)
{
if (show != m_showLabel) {
m_showLabel = show;
@@ -85,13 +100,12 @@ void RadioWidget::setLabelText(const QString & labelText, bool showLabel)
addLabel();
}
void RadioWidget::setStateData(const QByteArray & data)
void RadioWidget::setStateData(const RadioWidgetState & state)
{
RadioWidgetState state;
QDataStream stream(data);
stream >> state;
if (state.type != m_type || state.index != m_index)
return;
//setIndex(state.index);
m_valueReset = true;
setValue(state.value);
setFlags(state.flags);
}
@@ -109,9 +123,26 @@ void RadioWidget::changeVisibility(bool visible)
}
}
void RadioWidget::setAction(RadioUiAction * action)
{
if (m_action) {
disconnect(m_action, 0, this, 0);
delete m_action;
m_action = NULL;
}
m_action = action;
if (m_action) {
if (m_action->getIndex() > -1)
setIndex(m_action->getIndex());
connect(m_action, &RadioUiAction::toggled, this, &RadioWidget::onActionToggled);
}
}
int RadioWidget::getValue() const
{
return m_value * (m_invertValue ? -1 : 1);
return m_value;
}
int RadioWidget::getIndex() const
@@ -134,25 +165,34 @@ QByteArray RadioWidget::getStateData() const
RadioWidget::RadioWidgetState RadioWidget::getState() const
{
return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value), quint16(m_flags));;
return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value), quint16(m_flags));
}
void RadioWidget::init()
RadioUiAction * RadioWidget::getAction() const
{
setIndex(0);
m_controlWidget = NULL;
m_nameLabel = NULL;
return m_action;
}
m_gridLayout= new QGridLayout(this);
void RadioWidget::onActionToggled(int index, bool active)
{
emit valueChange(m_type, index, getValue());
}
void RadioWidget::addLayout()
{
if (m_gridLayout)
return;
m_gridLayout = new QGridLayout(this);
m_gridLayout->setContentsMargins(0, 0, 0, 0);
m_gridLayout->setVerticalSpacing(4);
m_gridLayout->setHorizontalSpacing(0);
addLabel();
}
void RadioWidget::addLabel()
{
addLayout();
if (m_nameLabel) {
m_gridLayout->removeWidget(m_nameLabel);
m_nameLabel->deleteLater();
@@ -168,6 +208,7 @@ void RadioWidget::addLabel()
void RadioWidget::setWidget(QWidget * widget, Qt::Alignment align)
{
addLayout();
if (m_controlWidget) {
m_gridLayout->removeWidget(m_controlWidget);
m_controlWidget->deleteLater();
+47 -32
View File
@@ -28,6 +28,8 @@
#define RADIO_WIDGET_STATE_VERSION 1
class RadioUiAction;
class RadioWidget : public QWidget
{
Q_OBJECT
@@ -41,10 +43,14 @@ class RadioWidget : public QWidget
RADIO_WIDGET_SWITCH,
RADIO_WIDGET_KNOB,
RADIO_WIDGET_FADER,
RADIO_WIDGET_TRIM,
RADIO_WIDGET_STICK // actually one axis of a stick
RADIO_WIDGET_TRIM, // trim axis, usually 2 buttons & slider
RADIO_WIDGET_STICK, // actually one axis of a stick
RADIO_WIDGET_KEY, // UI key/pushbutton
RADIO_WIDGET_ENUM_COUNT
};
enum RadioTrimWidgetButtonStates { RADIO_TRIM_BTN_ON = 1024, RADIO_TRIM_BTN_OFF = -1024 };
struct RadioWidgetState {
public:
RadioWidgetState(quint8 type = 0, qint8 index = 0, qint16 value = 0, quint16 flags = 0) :
@@ -65,49 +71,58 @@ class RadioWidget : public QWidget
quint8 _version = RADIO_WIDGET_STATE_VERSION; // structure definition version
};
explicit RadioWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
explicit RadioWidget(const QString &labelText, int value = 0, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
void setIndex(int index);
void setInvertValue(bool invertValue);
void setValue(int value);
void setFlags(quint16 flags);
void setShowLabel(bool show);
void setLabelText(const QString &labelText, bool showLabel = true);
void setStateData(const QByteArray & data);
void changeVisibility(bool visible);
explicit RadioWidget(QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
explicit RadioWidget(RadioUiAction * action = NULL, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
explicit RadioWidget(const QString & labelText, int value = 0, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
virtual int getValue() const;
int getIndex() const;
int getType() const;
QByteArray getStateData() const;
RadioWidgetState getState() const;
virtual int getIndex() const;
virtual int getType() const;
virtual QByteArray getStateData() const;
virtual RadioWidgetState getState() const;
virtual RadioUiAction * getAction() const;
public slots:
virtual void setIndex(const int & index);
virtual void setType(const RadioWidgetType & type);
virtual void setValue(const int & value);
virtual void setValueQual(const RadioWidgetType & type, const int & index, const int & value);
virtual void setFlags(const quint16 & flags);
virtual void setShowLabel(const bool show);
virtual void setLabelText(const QString & labelText, bool showLabel = true);
virtual void setStateData(const RadioWidgetState & state);
virtual void changeVisibility(bool visible);
virtual void setAction(RadioUiAction * action);
signals:
void valueChange(const RadioWidgetType type, const int index, int value);
void valueChanged(const int value);
void flagsChanged(const quint16 flags);
protected:
void init();
void addLayout();
void addLabel();
void setWidget(QWidget * widget = NULL, Qt::Alignment align = Qt::AlignHCenter);
int m_value;
int m_index;
quint16 m_flags;
bool m_invertValue;
bool m_showLabel;
QString m_labelText;
RadioWidgetType m_type;
private:
virtual void onActionToggled(int index, bool active);
QGridLayout * m_gridLayout;
QWidget * m_controlWidget;
QLabel * m_nameLabel;
RadioUiAction * m_action;
signals:
void valueChanged(int value);
void flagsChanged(quint16 flags);
int m_value;
int m_index;
quint16 m_flags;
bool m_valueReset;
bool m_showLabel;
QString m_labelText;
RadioWidgetType m_type;
};
Q_DECLARE_METATYPE(RadioWidget::RadioWidgetState)
#endif // _RADIOWIDGET_H_
+26 -11
View File
@@ -23,6 +23,7 @@
#include <QtGui>
#include <QSlider>
#include <QToolTip>
class SliderWidget : public QSlider
{
@@ -33,20 +34,34 @@ class SliderWidget : public QSlider
explicit SliderWidget(QWidget * parent = 0):
QSlider(parent)
{
setToolTip(tr("Right-double-click to reset to center."));
m_toolTip = tr("<p>Value (input): <b>%1</b></p>") % tr("Right-double-click to reset to center.");
}
protected:
protected:
void mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
setValue(0);
emit sliderMoved(0);
event->accept();
}
QSlider::mousePressEvent(event);
}
bool event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent * helpEvent = static_cast<QHelpEvent *>(event);
if (helpEvent) {
QToolTip::showText(helpEvent->globalPos(), m_toolTip.arg(this->value()));
return true;
}
}
return QWidget::event(event);
}
void mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
setValue(0);
emit sliderMoved(0);
event->accept();
}
QSlider::mousePressEvent(event);
}
QString m_toolTip;
};
#endif // _SLIDERWIDGET_H_
@@ -21,11 +21,11 @@
#define GBALL_SIZE 25
#define GBALL_SIZE_MN 20
#define GBALL_SIZE_MX 35
#define CENTER_INTERVAL 50 // ms
#include "virtualjoystickwidget.h"
#include "boards.h"
#include "constants.h"
#include "modeledit/node.h"
#include "helpers.h"
#include "radiotrimwidget.h"
@@ -43,6 +43,7 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
btnFixY(NULL),
nodeLabelX(NULL),
nodeLabelY(NULL),
m_stickScale(1024),
m_stickPressed(false)
{
ar = (float)size.width() / size.height();
@@ -135,12 +136,18 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
layout->addItem(new QSpacerItem(0, 0), 1, 4, 2, 1); // r1-2 c4: right h spacer
layout->addItem(new QSpacerItem(0, 0), 3, 0, 1, 5); // r4 c0-4: bot v spacer
connect(node, &Node::xChanged, this, &VirtualJoystickWidget::updateNodeValueLabels);
connect(node, &Node::yChanged, this, &VirtualJoystickWidget::updateNodeValueLabels);
setStickIndices(getStickIndex('H'), getStickIndex('V'));
connect(node, &Node::xChanged, this, &VirtualJoystickWidget::onNodeXChanged);
connect(node, &Node::yChanged, this, &VirtualJoystickWidget::onNodeYChanged);
connect(scene, &CustomGraphicsScene::mouseEvent, this, &VirtualJoystickWidget::onGsMouseEvent);
setSize(prefSize, frameSize());
centerStickTimer.setInterval(CENTER_INTERVAL);
connect(&centerStickTimer, &QTimer::timeout, this, &VirtualJoystickWidget::centerStick);
centerStickTimer.start();
}
void VirtualJoystickWidget::setStickX(qreal x)
@@ -158,6 +165,31 @@ void VirtualJoystickWidget::setStickPos(QPointF xy)
node->setPos(xy);
}
void VirtualJoystickWidget::setStickAxisValue(int index, int value)
{
using namespace Board;
qreal rvalue = value / 1024.0f;
switch (index) {
case STICK_AXIS_LH :
if (stickSide == 'L')
setStickX(rvalue);
break;
case STICK_AXIS_RH :
if (stickSide == 'R')
setStickX(rvalue);
break;
case STICK_AXIS_LV :
if (stickSide == 'L')
setStickY(rvalue);
break;
case STICK_AXIS_RV :
if (stickSide == 'R')
setStickY(rvalue);
break;
}
}
void VirtualJoystickWidget::centerStick()
{
node->stepToCenter();
@@ -178,20 +210,55 @@ QPointF VirtualJoystickWidget::getStickPos()
return QPointF(node->getX(), node->getY());
}
void VirtualJoystickWidget::setTrimValue(int which, int value)
void VirtualJoystickWidget::setTrimsRange(int min, int max)
{
RadioTrimWidget * trim = getTrimWidget(which);
if (hTrimWidget)
hTrimWidget->setTrimRange(min, max);
if (vTrimWidget)
vTrimWidget->setTrimRange(min, max);
}
void VirtualJoystickWidget::setTrimsRange(int rng)
{
setTrimsRange(-rng, rng);
}
void VirtualJoystickWidget::setTrimRange(int index, int min, int max)
{
if (index == Board::TRIM_AXIS_COUNT) {
setTrimsRange(min, max);
return;
}
RadioTrimWidget * trim = getTrimWidget(index);
if (trim) {
trim->setTrimRange(min, max);
}
}
void VirtualJoystickWidget::setTrimValue(int index, int value)
{
RadioTrimWidget * trim = getTrimWidget(index);
if (trim) {
trim->setValue(value);
}
}
void VirtualJoystickWidget::setTrimRange(int which, int min, int max)
int VirtualJoystickWidget::getStickScale() const
{
RadioTrimWidget * trim = getTrimWidget(which);
if (trim) {
trim->setTrimRange(min, max);
}
return m_stickScale;
}
void VirtualJoystickWidget::setStickScale(int stickScale)
{
m_stickScale = stickScale;
}
void VirtualJoystickWidget::setWidgetValue(const RadioWidget::RadioWidgetType type, const int index, const int value)
{
if (type == RadioWidget::RADIO_WIDGET_TRIM)
setTrimValue(index, value);
else if (type == RadioWidget::RADIO_WIDGET_STICK)
setStickAxisValue(index, value);
}
int VirtualJoystickWidget::getTrimValue(int which)
@@ -203,27 +270,25 @@ int VirtualJoystickWidget::getTrimValue(int which)
return 0;
}
void VirtualJoystickWidget::setStickConstraint(int which, bool active)
void VirtualJoystickWidget::setStickIndices(int xIdx, int yIdx)
{
if (btnHoldX == NULL)
m_xIndex = xIdx;
m_yIndex = yIdx;
}
void VirtualJoystickWidget::setStickConstraint(quint8 index, bool active)
{
if (btnHoldX == NULL || !index)
return; // no buttons
switch (which) {
case HOLD_X:
btnHoldX->setChecked(active);
break;
case HOLD_Y:
btnHoldY->setChecked(active);
break;
case FIX_X:
btnFixX->setChecked(active);
break;
case FIX_Y:
btnFixY->setChecked(active);
break;
default:
break;
}
if (index & HOLD_X)
btnHoldX->setChecked(active);
if (index & HOLD_Y)
btnHoldY->setChecked(active);
if (index & FIX_X)
btnFixX->setChecked(active);
if (index & FIX_Y)
btnFixY->setChecked(active);
}
void VirtualJoystickWidget::setStickColor(const QColor & color)
@@ -231,6 +296,15 @@ void VirtualJoystickWidget::setStickColor(const QColor & color)
node->setColor(color);
}
void VirtualJoystickWidget::loadDefaultsForMode(const unsigned mode)
{
if (((mode & 1) && stickSide == 'L') || (!(mode & 1) && stickSide == 'R')) {
setStickConstraint(HOLD_Y, true);
setStickY(1.0);
onNodeYChanged();
}
}
QSize VirtualJoystickWidget::sizeHint() const {
return prefSize;
}
@@ -241,6 +315,18 @@ void VirtualJoystickWidget::resizeEvent(QResizeEvent * event)
setSize(event->size(), event->oldSize());
}
void VirtualJoystickWidget::onNodeXChanged()
{
emit valueChange(RadioWidget::RADIO_WIDGET_STICK, m_xIndex, int(m_stickScale * getStickX()));
updateNodeValueLabels();
}
void VirtualJoystickWidget::onNodeYChanged()
{
emit valueChange(RadioWidget::RADIO_WIDGET_STICK, m_yIndex, int(-m_stickScale * getStickY()));
updateNodeValueLabels();
}
void VirtualJoystickWidget::setSize(const QSize & size, const QSize &)
{
float thisAspectRatio = (float)size.width() / size.height();
@@ -301,9 +387,7 @@ RadioTrimWidget * VirtualJoystickWidget::createTrimWidget(QChar type)
RadioTrimWidget * trimWidget = new RadioTrimWidget(type == 'H' ? Qt::Horizontal : Qt::Vertical);
trimWidget->setIndices(getTrimSliderType(type), getTrimButtonType(type, 0), getTrimButtonType(type, 1));
connect(trimWidget, &RadioTrimWidget::trimButtonPressed, this, &VirtualJoystickWidget::trimButtonPressed);
connect(trimWidget, &RadioTrimWidget::trimButtonReleased, this, &VirtualJoystickWidget::trimButtonReleased);
connect(trimWidget, &RadioTrimWidget::trimSliderMoved, this, &VirtualJoystickWidget::trimSliderMoved);
connect(trimWidget, &RadioTrimWidget::valueChange, this, &VirtualJoystickWidget::valueChange);
return trimWidget;
}
@@ -371,6 +455,24 @@ QLayout *VirtualJoystickWidget::createNodeValueLayout(QChar type, QLabel *& valL
return layout;
}
int VirtualJoystickWidget::getStickIndex(QChar type)
{
using namespace Board;
if (stickSide == 'L') {
if (type == 'H')
return STICK_AXIS_LH;
else
return STICK_AXIS_LV;
}
else {
if (type == 'H')
return STICK_AXIS_RH;
else
return STICK_AXIS_RV;
}
}
int VirtualJoystickWidget::getTrimSliderType(QChar type)
{
using namespace Board;
@@ -426,10 +528,12 @@ int VirtualJoystickWidget::getTrimButtonType(QChar type, int pos)
RadioTrimWidget * VirtualJoystickWidget::getTrimWidget(int which)
{
if (which == Board::TRIM_AXIS_LH || which == Board::TRIM_AXIS_RH)
if ((stickSide == 'L' && which == Board::TRIM_AXIS_LH) || (stickSide == 'R' && which == Board::TRIM_AXIS_RH))
return hTrimWidget;
else
else if ((stickSide == 'L' && which == Board::TRIM_AXIS_LV) || (stickSide == 'R' && which == Board::TRIM_AXIS_RV))
return vTrimWidget;
else
return NULL;
}
void VirtualJoystickWidget::onButtonChange(bool checked)
@@ -456,9 +560,9 @@ void VirtualJoystickWidget::onButtonChange(bool checked)
void VirtualJoystickWidget::updateNodeValueLabels()
{
if (nodeLabelX)
nodeLabelX->setText(QString("%1").arg((qreal)node->getX() * 100 + getTrimValue(0) / 5, 2, 'f', 0));
nodeLabelX->setText(QString("%1").arg(node->getX() * 100.0f, 2, 'f', 0));
if (nodeLabelY)
nodeLabelY->setText(QString("%1").arg((qreal)node->getY() * -100 + getTrimValue(1) / 5, 2, 'f', 0));
nodeLabelY->setText(QString("%1").arg(node->getY() * -100.0f, 2, 'f', 0));
}
void VirtualJoystickWidget::onGsMouseEvent(QGraphicsSceneMouseEvent * event)
@@ -21,6 +21,8 @@
#ifndef VIRTUALJOYSTICKWIDGET_H
#define VIRTUALJOYSTICKWIDGET_H
#include "radiowidget.h"
#include <QWidget>
#include <QResizeEvent>
#include <QBoxLayout>
@@ -28,6 +30,7 @@
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QToolButton>
#include <QTimer>
#include <QLabel>
class CustomGraphicsScene;
@@ -40,38 +43,47 @@ class VirtualJoystickWidget : public QWidget
public:
enum ConstraintTypes {
HOLD_X = 0,
HOLD_Y,
FIX_X,
FIX_Y
HOLD_X = 0x01,
HOLD_Y = 0x02,
FIX_X = 0x04,
FIX_Y = 0x08
};
explicit VirtualJoystickWidget(QWidget * parent = NULL, QChar side = 'L', bool showTrims = true, bool showBtns = true, bool showValues = true, QSize size = QSize(125, 125));
void setStickX(qreal x);
void setStickY(qreal y);
void setStickPos(QPointF xy);
void centerStick();
qreal getStickX();
qreal getStickY();
QPointF getStickPos();
void setTrimValue(int which, int value);
void setTrimRange(int which, int min, int max);
int getTrimValue(int which);
void setStickConstraint(int which, bool active);
void setStickColor(const QColor & color);
virtual QSize sizeHint() const;
virtual void resizeEvent(QResizeEvent *event);
int getStickScale() const;
void setStickScale(int stickScale);
public slots:
void setStickX(qreal x);
void setStickY(qreal y);
void setStickPos(QPointF xy);
void setStickAxisValue(int index, int value);
void centerStick();
void setTrimsRange(int min, int max);
void setTrimsRange(int rng);
void setTrimRange(int index, int min, int max);
void setTrimValue(int index, int value);
void setWidgetValue(const RadioWidget::RadioWidgetType type, const int index, const int value);
void setStickIndices(int xIdx = -1, int yIdx = -1);
void setStickConstraint(quint8 index, bool active);
void setStickColor(const QColor & color);
void loadDefaultsForMode(const unsigned mode);
signals:
void trimButtonPressed(int index);
void trimButtonReleased(int index);
void trimSliderMoved(int index, int value);
void valueChange(const RadioWidget::RadioWidgetType type, const int index, int value);
protected slots:
void onNodeXChanged();
void onNodeYChanged();
void onButtonChange(bool checked);
void updateNodeValueLabels();
void onGsMouseEvent(QGraphicsSceneMouseEvent * event);
@@ -81,6 +93,7 @@ class VirtualJoystickWidget : public QWidget
RadioTrimWidget * createTrimWidget(QChar type);
QToolButton * createButtonWidget(int type);
QLayout * createNodeValueLayout(QChar type, QLabel *& valLabel);
int getStickIndex(QChar type);
int getTrimSliderType(QChar type);
int getTrimButtonType(QChar type, int pos);
RadioTrimWidget * getTrimWidget(int which);
@@ -97,7 +110,11 @@ class VirtualJoystickWidget : public QWidget
QToolButton * btnFixX, * btnFixY;
QLabel * nodeLabelX, * nodeLabelY;
QSize extraSize;
QTimer centerStickTimer;
float ar; // aspect ratio
int m_xIndex;
int m_yIndex;
int m_stickScale;
bool m_stickPressed;
};