1
0
Fork 0
mirror of https://github.com/EdgeTX/edgetx.git synced 2025-07-26 17:55:12 +03:00

Reduce width of all simulator windows (#4154)

* [simu] Reduce width of all simulator windows and also height of Horus window. (https://github.com/opentx/opentx/issues/3135)

* [simu] New vertical channel outputs layout for all simulators, all outputs in one window and horizontally scrollable. Consolidate outputs and gvars widgets to one code base for all sims. Make simulator dialog re-sizable.

* [simu] Make simulation dialog resizable, and greatly reduce minimum size requirements of all simulators. Introduces new VirtualJoystickWidget class with all controls. Simplifies every simulator dialog UI form.
This commit is contained in:
Max Paperno 2016-12-23 17:56:28 -05:00 committed by Bertrand Songis
parent df835da07d
commit b0de35f57d
15 changed files with 3879 additions and 6099 deletions

View file

@ -59,10 +59,10 @@ enum BoardEnum {
#define CPN_MAX_KEYS 32
#define CPN_MAX_MOUSE_ANALOGS 2
const char * const ARROW_LEFT = "\xE2\x86\x90";
const char * const ARROW_UP = "\xE2\x86\x91";
const char * const ARROW_RIGHT = "\xE2\x86\x92";
const char * const ARROW_DOWN = "\xE2\x86\x93";
const char * const ARROW_LEFT = "\u2190";
const char * const ARROW_UP = "\u2191";
const char * const ARROW_RIGHT = "\u2192";
const char * const ARROW_DOWN = "\u2193";
#if defined(DEBUG)
#define HORUS_READY_FOR_RELEASE() true

View file

@ -4,6 +4,7 @@ set(simulation_SRCS
trainersimu.cpp
debugoutput.cpp
simulatorinterface.cpp
virtualjoystickwidget.cpp
)
set(simulation_UIS
@ -24,6 +25,7 @@ set(simulation_HDRS
telemetrysimu.h
trainersimu.h
debugoutput.h
virtualjoystickwidget.h
)
if(SDL_FOUND)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -24,10 +24,7 @@
#include <iostream>
#include "helpers.h"
#include "simulatorinterface.h"
#include "sliderwidget.h"
#define GBALL_SIZE 20
#define RESX 1024
#include "virtualjoystickwidget.h"
int SimulatorDialog::screenshotIdx = 0;
SimulatorDialog * traceCallbackInstance = 0;
@ -90,6 +87,7 @@ SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator
trimPressed(TRIM_NONE),
middleButtonPressed(false)
{
setWindowFlags(Qt::Window);
//shorcut for telemetry simulator
// new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_T), this, SLOT(openTelemetrySimulator()));
new QShortcut(QKeySequence(Qt::Key_F4), this, SLOT(openTelemetrySimulator()));
@ -110,6 +108,23 @@ void SimulatorDialog::closeEvent (QCloseEvent *)
{
simulator->stop();
timer->stop();
//g.simuWinGeo(GetCurrentFirmware()->getId(), saveGeometry());
}
void SimulatorDialog::showEvent(QShowEvent * event)
{
static bool firstShow = true;
if (firstShow) {
if (flags & SIMULATOR_FLAGS_STICK_MODE_LEFT) {
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickY(1);
}
else {
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyRight->setStickY(1);
}
firstShow = false;
}
}
void SimulatorDialog::mousePressEvent(QMouseEvent *event)
@ -126,24 +141,9 @@ void SimulatorDialog::mouseReleaseEvent(QMouseEvent *event)
}
}
void SimulatorDialog::onTrimPressed()
void SimulatorDialog::onTrimPressed(int which)
{
if (sender()->objectName() == QString("trimHL_L"))
trimPressed = TRIM_LH_L;
else if (sender()->objectName() == QString("trimHL_R"))
trimPressed = TRIM_LH_R;
else if (sender()->objectName() == QString("trimVL_D"))
trimPressed = TRIM_LV_DN;
else if (sender()->objectName() == QString("trimVL_U"))
trimPressed = TRIM_LV_UP;
else if (sender()->objectName() == QString("trimVR_D"))
trimPressed = TRIM_RV_DN;
else if (sender()->objectName() == QString("trimVR_U"))
trimPressed = TRIM_RV_UP;
else if (sender()->objectName() == QString("trimHR_L"))
trimPressed = TRIM_RH_L;
else if (sender()->objectName() == QString("trimHR_R"))
trimPressed = TRIM_RH_R;
trimPressed = which;
}
void SimulatorDialog::onTrimReleased()
@ -151,6 +151,11 @@ void SimulatorDialog::onTrimReleased()
trimPressed = TRIM_NONE;
}
void SimulatorDialog::onTrimSliderMoved(int which, int value)
{
simulator->setTrim(which, value);
}
void SimulatorDialog::openTelemetrySimulator()
{
// allow only one instance
@ -264,28 +269,34 @@ void SimulatorDialog::initUi(T * ui)
{
ui->setupUi(this);
windowName = tr("Simulating Radio (%1)").arg(GetCurrentFirmware()->getName());
setWindowTitle(windowName);
simulator->setSdPath(g.profile[g.id()].sdPath());
simulator->setVolumeGain(g.profile[g.id()].volumeGain());
lcd = ui->lcd;
leftStick = ui->leftStick;
rightStick = ui->rightStick;
lcd->setData(simulator->getLcd(), lcdWidth, lcdHeight, lcdDepth);
tabWidget = ui->tabWidget;
pots = findWidgets<QDial *>(this, "pot%1");
potLabels = findWidgets<QLabel *>(this, "potLabel%1");
potValues = findWidgets<QLabel *>(this, "potValue%1");
sliders = findWidgets<QSlider *>(this, "slider%1");
trimHLeft = ui->trimHLeft;
trimVLeft = ui->trimVLeft;
trimHRight = ui->trimHRight;
trimVRight = ui->trimVRight;
tabWidget = ui->tabWidget;
leftXPerc = ui->leftXPerc;
leftYPerc = ui->leftYPerc;
rightXPerc = ui->rightXPerc;
rightYPerc = ui->rightYPerc;
vJoyLeft = new VirtualJoystickWidget(this, 'L');
ui->leftStickLayout->addWidget(vJoyLeft);
setupSticks();
vJoyRight = new VirtualJoystickWidget(this, 'R');
ui->rightStickLayout->addWidget(vJoyRight);
resize(0, 0); // to force min height, min width
setFixedSize(width(), height());
connect(vJoyLeft, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
connect(vJoyLeft, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
connect(vJoyLeft, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
connect(vJoyRight, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
connect(vJoyRight, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
connect(vJoyRight, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
#ifdef JOYSTICKS
if (g.jsSupport()) {
@ -313,14 +324,11 @@ void SimulatorDialog::initUi(T * ui)
joystick->sensitivities[j] = 0;
joystick->deadzones[j]=0;
}
nodeRight->setCenteringY(false); //mode 1,3 -> THR on right
ui->holdRightY->setChecked(true);
nodeRight->setCenteringX(false); //mode 1,3 -> THR on right
ui->holdRightX->setChecked(true);
nodeLeft->setCenteringY(false); //mode 1,3 -> THR on right
ui->holdLeftY->setChecked(true);
nodeLeft->setCenteringX(false); //mode 1,3 -> THR on right
ui->holdLeftX->setChecked(true);
//mode 1,3 -> THR on right
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
connect(joystick, SIGNAL(axisValueChanged(int, int)), this, SLOT(onjoystickAxisValueChanged(int, int)));
}
else {
@ -331,126 +339,15 @@ void SimulatorDialog::initUi(T * ui)
}
#endif
windowName = tr("Simulating Radio (%1)").arg(GetCurrentFirmware()->getName());
setWindowTitle(windowName);
simulator->setSdPath(g.profile[g.id()].sdPath());
simulator->setVolumeGain(g.profile[g.id()].volumeGain());
lcd->setData(simulator->getLcd(), lcdWidth, lcdHeight, lcdDepth);
if (flags & SIMULATOR_FLAGS_STICK_MODE_LEFT) {
nodeLeft->setCenteringY(false);
ui->holdLeftY->setChecked(true);
}
else {
nodeRight->setCenteringY(false);
ui->holdRightY->setChecked(true);
}
setupOutputsDisplay();
setupGVarsDisplay();
setTrims();
int outputs = std::min(32, GetCurrentFirmware()->getCapability(Outputs));
if (outputs <= 16) {
// hide second Outputs tab
tabWidget->removeTab(tabWidget->indexOf(ui->outputs2));
}
else {
tabWidget->setTabText(tabWidget->indexOf(ui->outputs), tr("Outputs") + QString(" 1-%1").arg(16));
tabWidget->setTabText(tabWidget->indexOf(ui->outputs2), tr("Outputs") + QString(" 17-%1").arg(outputs));
}
for (int i=0; i<outputs; i++) {
QGridLayout * outputTab = ui->channelsLayout;
int column = i / (std::min(16,outputs)/2);
int line = i % (std::min(16,outputs)/2);
if (i >= 16 ) {
outputTab = ui->channelsLayout2;
column = (i-16) / (std::min(16,outputs-16)/2);
line = (i-16) % (std::min(16,outputs-16)/2);
}
QLabel * label = new QLabel(tabWidget);
label->setText(RawSource(SOURCE_TYPE_CH, i).toString());
outputTab->addWidget(label, line, column == 0 ? 0 : 5, 1, 1);
//restoreGeometry(g.simuWinGeo(GetCurrentFirmware()->getId()));
QSlider * slider = new QSlider(tabWidget);
slider->setEnabled(false);
/*slider->setMaximumSize(QSize(16777215, 18));
slider->setStyleSheet(QString::fromUtf8("QSlider::sub-page:horizontal:disabled {\n"
"border-color: #999;\n"
"}\n"
"\n"
"QSlider::add-page:horizontal:disabled {\n"
"border-color: #999;\n"
"}\n"
"\n"
"QSlider::handle:horizontal:disabled {\n"
"background: #0000CC;\n"
"border: 1px solid #aaa;\n"
"border-radius: 4px;\n"
"}")); */
slider->setMinimum(-1024);
slider->setMaximum(1024);
slider->setPageStep(128);
slider->setTracking(false);
slider->setOrientation(Qt::Horizontal);
slider->setInvertedAppearance(false);
slider->setTickPosition(QSlider::TicksBelow);
channelSliders << slider;
outputTab->addWidget(slider, line, column == 0 ? 1 : 4, 1, 1);
if (flags & SIMULATOR_FLAGS_NOTX)
tabWidget->setCurrentIndex(1);
QLabel * value = new QLabel(tabWidget);
value->setMinimumSize(QSize(50, 0));
value->setAlignment(Qt::AlignCenter);
channelValues << value;
outputTab->addWidget(value, line, column == 0 ? 2 : 3, 1, 1);
}
int switches = GetCurrentFirmware()->getCapability(LogicalSwitches);
for (int i=0; i<switches; i++) {
QFrame * swtch = createLogicalSwitch(tabWidget, i, logicalSwitchLabels);
ui->logicalSwitchesLayout->addWidget(swtch, i / (switches/2), i % (switches/2), 1, 1);
if (outputs > 16) {
// repeat logical switches on second outputs tab
swtch = createLogicalSwitch(tabWidget, i, logicalSwitchLabels2);
ui->logicalSwitchesLayout2->addWidget(swtch, i / (switches/2), i % (switches/2), 1, 1);
}
}
int fmodes = GetCurrentFirmware()->getCapability(FlightModes);
int gvars = GetCurrentFirmware()->getCapability(Gvars);
if (gvars>0) {
for (int fm=0; fm<fmodes; fm++) {
QLabel * label = new QLabel(tabWidget);
label->setText(QString("FM%1").arg(fm));
label->setAlignment(Qt::AlignCenter);
ui->gvarsLayout->addWidget(label, 0, fm+1);
}
for (int i=0; i<gvars; i++) {
QLabel * label = new QLabel(tabWidget);
label->setText(QString("GV%1").arg(i+1));
label->setAutoFillBackground(true);
if ((i % 2) ==0 ) {
label->setStyleSheet("QLabel { background-color: rgb(220, 220, 220) }");
}
ui->gvarsLayout->addWidget(label, i+1, 0);
for (int fm=0; fm<fmodes; fm++) {
QLabel * value = new QLabel(tabWidget);
value->setAutoFillBackground(true);
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
if ((i % 2) ==0 ) {
value->setStyleSheet("QLabel { background-color: rgb(220, 220, 220) }");
}
gvarValues << value;
ui->gvarsLayout->addWidget(value, i+1, fm+1);
}
}
}
if (flags & SIMULATOR_FLAGS_NOTX) {
ui->tabWidget->setCurrentWidget(ui->outputs);
}
else {
ui->tabWidget->setCurrentWidget(ui->simu);
}
}
QFrame * SimulatorDialog::createLogicalSwitch(QWidget * parent, int switchNo, QVector<QLabel *> & labels)
@ -459,17 +356,135 @@ QFrame * SimulatorDialog::createLogicalSwitch(QWidget * parent, int switchNo, QV
swtch->setAutoFillBackground(true);
swtch->setFrameShape(QFrame::Panel);
swtch->setFrameShadow(QFrame::Raised);
swtch->setLineWidth(2);
swtch->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
swtch->setMaximumHeight(18);
QVBoxLayout * layout = new QVBoxLayout(swtch);
layout->setContentsMargins(2, 2, 2, 2);
layout->setContentsMargins(2, 0, 2, 0);
QFont font;
font.setPointSize(8);
QLabel * label = new QLabel(swtch);
label->setFont(font);
label->setText(RawSwitch(SWITCH_TYPE_VIRTUAL, switchNo+1).toString());
label->setAlignment(Qt::AlignCenter);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
labels << label;
layout->addWidget(label);
return swtch;
}
void SimulatorDialog::setupOutputsDisplay()
{
// setup Outputs tab
QWidget * outputsWidget = new QWidget();
QGridLayout * gridLayout = new QGridLayout(outputsWidget);
gridLayout->setHorizontalSpacing(0);
gridLayout->setVerticalSpacing(3);
gridLayout->setContentsMargins(5, 3, 5, 3);
// logical switches area
QWidget * logicalSwitches = new QWidget(outputsWidget);
logicalSwitches->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
QGridLayout * logicalSwitchesLayout = new QGridLayout(logicalSwitches);
logicalSwitchesLayout->setHorizontalSpacing(3);
logicalSwitchesLayout->setVerticalSpacing(2);
logicalSwitchesLayout->setContentsMargins(0, 0, 0, 0);
gridLayout->addWidget(logicalSwitches, 0, 0, 1, 1);
// channels area
QScrollArea * scrollArea = new QScrollArea(outputsWidget);
QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred);
sp.setHorizontalStretch(0);
sp.setVerticalStretch(0);
scrollArea->setSizePolicy(sp);
scrollArea->setWidgetResizable(true);
QWidget * channelsWidget = new QWidget();
QGridLayout * channelsLayout = new QGridLayout(channelsWidget);
channelsLayout->setHorizontalSpacing(4);
channelsLayout->setVerticalSpacing(3);
channelsLayout->setContentsMargins(0, 0, 0, 3);
scrollArea->setWidget(channelsWidget);
gridLayout->addWidget(scrollArea, 1, 0, 1, 1);
tabWidget->insertTab(1, outputsWidget, QString(tr("Outputs")));
// populate outputs
int outputs = std::min(32, GetCurrentFirmware()->getCapability(Outputs));
int column = 0;
for (int i=0; i<outputs; i++) {
QLabel * label = new QLabel(tabWidget);
label->setText(" " + RawSource(SOURCE_TYPE_CH, i).toString() + " ");
label->setAlignment(Qt::AlignCenter);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
channelsLayout->addWidget(label, 0, column, 1, 1);
QSlider * slider = new QSlider(tabWidget);
slider->setEnabled(false);
slider->setMinimum(-1024);
slider->setMaximum(1024);
slider->setPageStep(128);
slider->setTracking(false);
slider->setOrientation(Qt::Vertical);
slider->setInvertedAppearance(false);
slider->setTickPosition(QSlider::TicksRight);
slider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
QLabel * value = new QLabel(tabWidget);
value->setMinimumSize(QSize(value->fontMetrics().size(Qt::TextSingleLine, "-100.0").width(), 0));
value->setAlignment(Qt::AlignCenter);
value->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
channelValues << value;
channelsLayout->addWidget(value, 1, column, 1, 1);
channelSliders << slider;
channelsLayout->addWidget(slider, 2, column++, 1, 1);
channelsLayout->setAlignment(slider, Qt::AlignHCenter);
}
// populate logical switches
int switches = GetCurrentFirmware()->getCapability(LogicalSwitches);
int rows = switches / (switches > 16 ? 4 : 2);
for (int i=0; i<switches; i++) {
QFrame * swtch = createLogicalSwitch(tabWidget, i, logicalSwitchLabels);
logicalSwitchesLayout->addWidget(swtch, i / rows, i % rows, 1, 1);
}
}
void SimulatorDialog::setupGVarsDisplay()
{
int fmodes = GetCurrentFirmware()->getCapability(FlightModes);
int gvars = GetCurrentFirmware()->getCapability(Gvars);
if (gvars>0) {
// setup GVars tab
QWidget * gvarsWidget = new QWidget();
QGridLayout * gvarsLayout = new QGridLayout(gvarsWidget);
tabWidget->addTab(gvarsWidget, QString(tr("GVars")));
for (int fm=0; fm<fmodes; fm++) {
QLabel * label = new QLabel(tabWidget);
label->setText(QString("FM%1").arg(fm));
label->setAlignment(Qt::AlignCenter);
gvarsLayout->addWidget(label, 0, fm+1);
}
for (int i=0; i<gvars; i++) {
QLabel * label = new QLabel(tabWidget);
label->setText(QString("GV%1").arg(i+1));
label->setAutoFillBackground(true);
if ((i % 2) ==0 ) {
label->setStyleSheet("QLabel { background-color: rgb(220, 220, 220) }");
}
gvarsLayout->addWidget(label, i+1, 0);
for (int fm=0; fm<fmodes; fm++) {
QLabel * value = new QLabel(tabWidget);
value->setAutoFillBackground(true);
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
if ((i % 2) ==0 ) {
value->setStyleSheet("QLabel { background-color: rgb(220, 220, 220) }");
}
gvarValues << value;
gvarsLayout->addWidget(value, i+1, fm+1);
}
}
}
}
void SimulatorDialog::onButtonPressed(int value)
{
if (value == Qt::Key_Print) {
@ -533,6 +548,7 @@ void SimulatorDialog::onTimerEvent()
beepVal = 0;
QApplication::beep();
}
}
updateDebugOutput();
@ -540,11 +556,11 @@ void SimulatorDialog::onTimerEvent()
void SimulatorDialog::centerSticks()
{
if (leftStick->scene())
nodeLeft->stepToCenter();
if (vJoyLeft)
vJoyLeft->centerStick();
if (rightStick->scene())
nodeRight->stepToCenter();
if (vJoyRight)
vJoyRight->centerStick();
}
void SimulatorDialog::start(QByteArray & eeprom)
@ -569,18 +585,32 @@ void SimulatorDialog::start(const char * filename)
void SimulatorDialog::setTrims()
{
typedef VirtualJoystickWidget VJW;
static Trims lastTrims;
Trims trims;
simulator->getTrims(trims);
if (trims.values[VJW::TRIM_AXIS_L_X] != lastTrims.values[VJW::TRIM_AXIS_L_X])
vJoyLeft->setTrimValue(VJW::TRIM_AXIS_L_X, trims.values[VJW::TRIM_AXIS_L_X]);
if (trims.values[VJW::TRIM_AXIS_L_Y] != lastTrims.values[VJW::TRIM_AXIS_L_Y])
vJoyLeft->setTrimValue(VJW::TRIM_AXIS_L_Y, trims.values[VJW::TRIM_AXIS_L_Y]);
if (trims.values[VJW::TRIM_AXIS_R_Y] != lastTrims.values[VJW::TRIM_AXIS_R_Y])
vJoyRight->setTrimValue(VJW::TRIM_AXIS_R_Y, trims.values[VJW::TRIM_AXIS_R_Y]);
if (trims.values[VJW::TRIM_AXIS_R_X] != lastTrims.values[VJW::TRIM_AXIS_R_X])
vJoyRight->setTrimValue(VJW::TRIM_AXIS_R_X, trims.values[VJW::TRIM_AXIS_R_X]);
if (trims.extended != lastTrims.extended) {
int trimMin = -125, trimMax = +125;
if (trims.extended) {
trimMin = -500;
trimMax = +500;
}
trimHLeft->setRange(trimMin, trimMax); trimHLeft->setValue(trims.values[0]);
trimVLeft->setRange(trimMin, trimMax); trimVLeft->setValue(trims.values[1]);
trimVRight->setRange(trimMin, trimMax); trimVRight->setValue(trims.values[2]);
trimHRight->setRange(trimMin, trimMax); trimHRight->setValue(trims.values[3]);
vJoyLeft->setTrimRange(VJW::TRIM_AXIS_L_X, trimMin, trimMax);
vJoyLeft->setTrimRange(VJW::TRIM_AXIS_L_Y, trimMin, trimMax);
vJoyRight->setTrimRange(VJW::TRIM_AXIS_R_Y, trimMin, trimMax);
vJoyRight->setTrimRange(VJW::TRIM_AXIS_R_X, trimMin, trimMax);
}
lastTrims = trims;
}
inline int chVal(int val)
@ -588,26 +618,6 @@ inline int chVal(int val)
return qMin(1024, qMax(-1024, val));
}
void SimulatorDialog::on_trimHLeft_valueChanged(int value)
{
simulator->setTrim(0, value);
}
void SimulatorDialog::on_trimVLeft_valueChanged(int value)
{
simulator->setTrim(1, value);
}
void SimulatorDialog::on_trimVRight_valueChanged(int value)
{
simulator->setTrim(2, value);
}
void SimulatorDialog::on_trimHRight_valueChanged(int value)
{
simulator->setTrim(3, value);
}
void SimulatorDialog::setValues()
{
TxOutputs outputs;
@ -622,20 +632,11 @@ void SimulatorDialog::setValues()
}
}
leftXPerc->setText(QString("X %1%").arg((qreal)nodeLeft->getX()*100+trims.values[0]/5, 2, 'f', 0));
leftYPerc->setText(QString("Y %1%").arg((qreal)nodeLeft->getY()*-100+trims.values[1]/5, 2, 'f', 0));
rightXPerc->setText(QString("X %1%").arg((qreal)nodeRight->getX()*100+trims.values[3]/5, 2, 'f', 0));
rightYPerc->setText(QString("Y %1%").arg((qreal)nodeRight->getY()*-100+trims.values[2]/5, 2, 'f', 0));
QString CSWITCH_ON = "QLabel { background-color: #4CC417 }";
QString CSWITCH_OFF = "QLabel { }";
for (int i=0; i<GetCurrentFirmware()->getCapability(LogicalSwitches); i++) {
logicalSwitchLabels[i]->setStyleSheet(outputs.vsw[i] ? CSWITCH_ON : CSWITCH_OFF);
if (!logicalSwitchLabels2.isEmpty()) {
logicalSwitchLabels2[i]->setStyleSheet(outputs.vsw[i] ? CSWITCH_ON : CSWITCH_OFF);
}
}
for (unsigned int gv=0; gv<numGvars; gv++) {
@ -649,103 +650,6 @@ void SimulatorDialog::setValues()
}
}
void SimulatorDialog::setupSticks()
{
QGraphicsScene *leftScene = new QGraphicsScene(leftStick);
leftScene->setItemIndexMethod(QGraphicsScene::NoIndex);
leftStick->setScene(leftScene);
// leftStick->scene()->addLine(0,10,20,30);
QGraphicsScene *rightScene = new QGraphicsScene(rightStick);
rightScene->setItemIndexMethod(QGraphicsScene::NoIndex);
rightStick->setScene(rightScene);
// rightStick->scene()->addLine(0,10,20,30);
nodeLeft = new Node();
nodeLeft->setPos(-GBALL_SIZE/2,-GBALL_SIZE/2);
nodeLeft->setBallSize(GBALL_SIZE);
leftScene->addItem(nodeLeft);
nodeRight = new Node();
nodeRight->setPos(-GBALL_SIZE/2,-GBALL_SIZE/2);
nodeRight->setBallSize(GBALL_SIZE);
rightScene->addItem(nodeRight);
}
void SimulatorDialog::resizeEvent(QResizeEvent *event)
{
if (leftStick->scene()) {
QRect qr = leftStick->contentsRect();
qreal w = (qreal)qr.width() - GBALL_SIZE;
qreal h = (qreal)qr.height() - GBALL_SIZE;
qreal cx = (qreal)qr.width()/2;
qreal cy = (qreal)qr.height()/2;
leftStick->scene()->setSceneRect(-cx,-cy,w,h);
QPointF p = nodeLeft->pos();
p.setX(qMin(cx, qMax(p.x(), -cx)));
p.setY(qMin(cy, qMax(p.y(), -cy)));
nodeLeft->setPos(p);
}
if (rightStick->scene()) {
QRect qr = rightStick->contentsRect();
qreal w = (qreal)qr.width() - GBALL_SIZE;
qreal h = (qreal)qr.height() - GBALL_SIZE;
qreal cx = (qreal)qr.width()/2;
qreal cy = (qreal)qr.height()/2;
rightStick->scene()->setSceneRect(-cx,-cy,w,h);
QPointF p = nodeRight->pos();
p.setX(qMin(cx, qMax(p.x(), -cx)));
p.setY(qMin(cy, qMax(p.y(), -cy)));
nodeRight->setPos(p);
}
QDialog::resizeEvent(event);
}
void SimulatorDialog::on_holdLeftX_clicked(bool checked)
{
nodeLeft->setCenteringX(!checked);
}
void SimulatorDialog::on_holdLeftY_clicked(bool checked)
{
nodeLeft->setCenteringY(!checked);
}
void SimulatorDialog::on_holdRightX_clicked(bool checked)
{
nodeRight->setCenteringX(!checked);
}
void SimulatorDialog::on_holdRightY_clicked(bool checked)
{
nodeRight->setCenteringY(!checked);
}
void SimulatorDialog::on_FixLeftX_clicked(bool checked)
{
nodeLeft->setFixedX(checked);
}
void SimulatorDialog::on_FixLeftY_clicked(bool checked)
{
nodeLeft->setFixedY(checked);
}
void SimulatorDialog::on_FixRightX_clicked(bool checked)
{
nodeRight->setFixedX(checked);
}
void SimulatorDialog::on_FixRightY_clicked(bool checked)
{
nodeRight->setFixedY(checked);
}
#ifdef JOYSTICKS
void SimulatorDialog::onjoystickAxisValueChanged(int axis, int value)
{
@ -767,16 +671,16 @@ void SimulatorDialog::onjoystickAxisValueChanged(int axis, int value)
stickval*=-1;
}
if (stick==1 ) {
nodeRight->setY(-stickval/1024.0);
vJoyRight->setStickY(-stickval/1024.0);
}
else if (stick==2) {
nodeRight->setX(stickval/1024.0);
vJoyRight->setStickX(stickval/1024.0);
}
else if (stick==3) {
nodeLeft->setY(-stickval/1024.0);
vJoyLeft->setStickY(-stickval/1024.0);
}
else if (stick==4) {
nodeLeft->setX(stickval/1024.0);
vJoyLeft->setStickX(stickval/1024.0);
}
else if (stick >= 5 && stick < 5+pots.count()) {
pots[stick-5]->setValue(stickval);

View file

@ -47,6 +47,7 @@ namespace Ui {
// TODO rename + move?
class LcdWidget;
class SliderWidget;
class VirtualJoystickWidget;
#define SIMULATOR_FLAGS_NOTX 1
#define SIMULATOR_FLAGS_STICK_MODE_LEFT 2
@ -73,6 +74,7 @@ class SimulatorDialog : public QDialog
void start(QByteArray & eeprom);
virtual void traceCallback(const char * text);
protected:
template <class T> void initUi(T * ui);
virtual void setLightOn(bool enable) { }
@ -80,24 +82,19 @@ class SimulatorDialog : public QDialog
unsigned int flags;
LcdWidget * lcd;
QGraphicsView * leftStick, * rightStick;
QVector<QDial *> pots;
QVector<QLabel *> potLabels;
QVector<QLabel *> potValues;
QVector<QSlider *> sliders;
SliderWidget * trimHLeft, * trimVLeft, * trimHRight, * trimVRight;
QLabel * leftXPerc, * rightXPerc, * leftYPerc, * rightYPerc;
QTabWidget * tabWidget;
QVector<QLabel *> logicalSwitchLabels;
QVector<QLabel *> logicalSwitchLabels2;
QVector<QSlider *> channelSliders;
QVector<QLabel *> channelValues;
QVector<QLabel *> gvarValues;
void init();
Node *nodeLeft;
Node *nodeRight;
VirtualJoystickWidget *vJoyLeft;
VirtualJoystickWidget *vJoyRight;
QTimer *timer;
QString windowName;
unsigned int backLight;
@ -114,18 +111,18 @@ class SimulatorDialog : public QDialog
SimulatorInterface *simulator;
unsigned int lastPhase;
void setupSticks();
void setupTimer();
void resizeEvent(QResizeEvent *event = 0);
QFrame * createLogicalSwitch(QWidget * parent, int switchNo, QVector<QLabel *> & labels);
void setupOutputsDisplay();
void setupGVarsDisplay();
virtual void getValues() = 0;
void setValues();
void centerSticks();
void setTrims();
void setValues();
virtual void getValues() = 0;
int getValue(qint8 i);
bool getSwitch(int swtch, bool nc, qint8 level=0);
void setTrims();
QFrame * createLogicalSwitch(QWidget * parent, int switchNo, QVector<QLabel *> & labels);
int beepVal;
@ -141,8 +138,8 @@ class SimulatorDialog : public QDialog
QList<QString> traceList;
void updateDebugOutput();
protected:
virtual void closeEvent(QCloseEvent *);
virtual void showEvent(QShowEvent *event);
virtual void mousePressEvent(QMouseEvent *);
virtual void mouseReleaseEvent(QMouseEvent *);
virtual void wheelEvent(QWheelEvent *);
@ -155,21 +152,10 @@ class SimulatorDialog : public QDialog
private slots:
void onButtonPressed(int value);
void on_FixRightY_clicked(bool checked);
void on_FixRightX_clicked(bool checked);
void on_FixLeftY_clicked(bool checked);
void on_FixLeftX_clicked(bool checked);
void on_holdRightY_clicked(bool checked);
void on_holdRightX_clicked(bool checked);
void on_holdLeftY_clicked(bool checked);
void on_holdLeftX_clicked(bool checked);
void on_trimHLeft_valueChanged(int);
void on_trimVLeft_valueChanged(int);
void on_trimHRight_valueChanged(int);
void on_trimVRight_valueChanged(int);
void onTimerEvent();
void onTrimPressed();
void onTrimPressed(int which);
void onTrimReleased();
void onTrimSliderMoved(int which, int value);
void openTelemetrySimulator();
void openTrainerSimulator();
void openDebugOutput();

View file

@ -75,36 +75,12 @@ SimulatorDialog9X::SimulatorDialog9X(QWidget * parent, SimulatorInterface *simul
if (g.simuSW())
restoreSwitches();
ui->trimHR_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHR_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVR_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVR_D->setText(QString::fromUtf8(ARROW_DOWN));
ui->trimHL_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHL_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVL_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVL_D->setText(QString::fromUtf8(ARROW_DOWN));
for (int i=0; i<pots.count(); i++) {
pots[i]->setProperty("index", i);
connect(pots[i], SIGNAL(valueChanged(int)), this, SLOT(dialChanged(int)));
}
connect(ui->leftbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->rightbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->trimHR_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHR_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
}
SimulatorDialog9X::~SimulatorDialog9X()
@ -154,10 +130,10 @@ void SimulatorDialog9X::getValues()
{
TxInputs inputs = {
{
int(1024*nodeLeft->getX()), // LEFT HORZ
int(-1024*nodeLeft->getY()), // LEFT VERT
int(-1024*nodeRight->getY()), // RGHT VERT
int(1024*nodeRight->getX()) // RGHT HORZ
int(1024 * vJoyLeft->getStickX()), // LEFT HORZ
int(-1024 * vJoyLeft->getStickY()), // LEFT VERT
int(-1024 * vJoyRight->getStickY()), // RGHT VERT
int(1024 * vJoyRight->getStickX()) // RGHT HORZ
},
{

View file

@ -45,33 +45,8 @@ SimulatorDialogFlamenco::SimulatorDialogFlamenco(QWidget * parent, SimulatorInte
if (g.simuSW())
restoreSwitches();
ui->trimHR_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHR_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVR_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVR_D->setText(QString::fromUtf8(ARROW_DOWN));
ui->trimHL_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHL_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVL_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVL_D->setText(QString::fromUtf8(ARROW_DOWN));
connect(ui->leftbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->rightbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->trimHR_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHR_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
}
SimulatorDialogFlamenco::~SimulatorDialogFlamenco()
@ -94,10 +69,10 @@ void SimulatorDialogFlamenco::getValues()
{
TxInputs inputs = {
{
int(1024*nodeLeft->getX()), // LEFT HORZ
int(-1024*nodeLeft->getY()), // LEFT VERT
int(-1024*nodeRight->getY()), // RGHT VERT
int(1024*nodeRight->getX()) // RGHT HORZ
int(1024 * vJoyLeft->getStickX()), // LEFT HORZ
int(-1024 * vJoyLeft->getStickY()), // LEFT VERT
int(-1024 * vJoyRight->getStickY()), // RGHT VERT
int(1024 * vJoyRight->getStickX()) // RGHT HORZ
},
{

View file

@ -63,33 +63,8 @@ SimulatorDialogHorus::SimulatorDialogHorus(QWidget * parent, SimulatorInterface
if (g.simuSW())
restoreSwitches();
ui->trimHR_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHR_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVR_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVR_D->setText(QString::fromUtf8(ARROW_DOWN));
ui->trimHL_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHL_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVL_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVL_D->setText(QString::fromUtf8(ARROW_DOWN));
connect(ui->leftbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->rightbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->trimHR_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHR_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->teleSim, SIGNAL(released()), this, SLOT(openTelemetrySimulator()));
connect(ui->trainerSim, SIGNAL(released()), this, SLOT(openTrainerSimulator()));
connect(ui->debugConsole, SIGNAL(released()), this, SLOT(openDebugOutput()));
@ -116,10 +91,10 @@ void SimulatorDialogHorus::getValues()
{
TxInputs inputs = {
{
int(1024*nodeLeft->getX()), // LEFT HORZ
int(-1024*nodeLeft->getY()), // LEFT VERT
int(-1024*nodeRight->getY()), // RGHT VERT
int(1024*nodeRight->getX()) // RGHT HORZ
int(1024 * vJoyLeft->getStickX()), // LEFT HORZ
int(-1024 * vJoyLeft->getStickY()), // LEFT VERT
int(-1024 * vJoyRight->getStickY()), // RGHT VERT
int(1024 * vJoyRight->getStickX()) // RGHT HORZ
},
{

View file

@ -70,33 +70,8 @@ SimulatorDialogTaranis::SimulatorDialogTaranis(QWidget * parent, SimulatorInterf
}
}
ui->trimHR_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHR_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVR_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVR_D->setText(QString::fromUtf8(ARROW_DOWN));
ui->trimHL_L->setText(QString::fromUtf8(ARROW_LEFT));
ui->trimHL_R->setText(QString::fromUtf8(ARROW_RIGHT));
ui->trimVL_U->setText(QString::fromUtf8(ARROW_UP));
ui->trimVL_D->setText(QString::fromUtf8(ARROW_DOWN));
connect(ui->leftbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->rightbuttons, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int)));
connect(ui->trimHR_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVR_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_R, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHL_L, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_U, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimVL_D, SIGNAL(pressed()), this, SLOT(onTrimPressed()));
connect(ui->trimHR_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHR_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVR_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_R, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimHL_L, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_U, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->trimVL_D, SIGNAL(released()), this, SLOT(onTrimReleased()));
connect(ui->teleSim, SIGNAL(released()), this, SLOT(openTelemetrySimulator()));
connect(ui->trainerSim, SIGNAL(released()), this, SLOT(openTrainerSimulator()));
connect(ui->debugConsole, SIGNAL(released()), this, SLOT(openDebugOutput()));
@ -131,10 +106,10 @@ void SimulatorDialogTaranis::getValues()
TxInputs inputs = {
{
int(1024*nodeLeft->getX()), // LEFT HORZ
int(-1024*nodeLeft->getY()), // LEFT VERT
int(-1024*nodeRight->getY()), // RGHT VERT
int(1024*nodeRight->getX()) // RGHT HORZ
int(1024 * vJoyLeft->getStickX()), // LEFT HORZ
int(-1024 * vJoyLeft->getStickY()), // LEFT VERT
int(-1024 * vJoyRight->getStickY()), // RGHT VERT
int(1024 * vJoyRight->getStickX()) // RGHT HORZ
},
{

View file

@ -31,7 +31,7 @@ class SliderWidget : public QSlider
public:
explicit SliderWidget(QFrame * parent = 0):
explicit SliderWidget(QWidget * parent = 0):
QSlider(parent)
{
}

View file

@ -0,0 +1,496 @@
/*
* 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.
*/
#include "virtualjoystickwidget.h"
#include "../constants.h"
#include "sliderwidget.h"
#include "modeledit/node.h"
#include "helpers.h"
VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool showTrims, bool showBtns, bool showValues, QSize size) :
QWidget(parent),
stickSide(side),
prefSize(size),
hTrimSlider(NULL),
vTrimSlider(NULL),
btnHoldX(NULL),
btnHoldY(NULL),
btnFixX(NULL),
btnFixY(NULL),
nodeLabelX(NULL),
nodeLabelY(NULL)
{
ar = (float)size.width() / size.height();
extraSize = QSize(0, 0);
// 5 col x 4 rows
layout = new QGridLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setSizePolicy(sizePolicy);
gv = new QGraphicsView(this);
gv->setSizePolicy(sizePolicy);
gv->setMinimumSize(QSize(150, 150));
gv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene = new QGraphicsScene(gv);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
gv->setScene(scene);
node = new Node();
node->setPos(-GBALL_SIZE / 2, -GBALL_SIZE / 2);
node->setBallSize(GBALL_SIZE);
scene->addItem(node);
int colvt, colbb, colvx, colvy;
if (stickSide == 'L') {
colvt = 3;
colbb = 1;
colvx = 1;
colvy = 3;
}
else {
colvt = 1;
colbb = 3;
colvx = 3;
colvy = 1;
}
if (showTrims) {
QWidget * hTrimWidget = createTrimWidget('H');
QWidget * vTrimWidget = createTrimWidget('V');
layout->addWidget(vTrimWidget, 1, colvt, 1, 1);
layout->addWidget(hTrimWidget, 2, 2, 1, 1);
hTrimSlider = hTrimWidget->findChild<SliderWidget *>();
vTrimSlider = vTrimWidget->findChild<SliderWidget *>();
extraSize += QSize(vTrimWidget->sizeHint().width(), hTrimWidget->sizeHint().height());
}
if (showBtns) {
QVBoxLayout * btnbox = new QVBoxLayout();
btnbox->setContentsMargins(9, 9, 9, 9);
btnbox->setSpacing(2);
btnbox->addWidget(btnHoldY = createButtonWidget(HOLD_Y));
btnbox->addWidget(btnFixY = createButtonWidget(FIX_Y));
btnbox->addWidget(btnFixX = createButtonWidget(FIX_X));
btnbox->addWidget(btnHoldX = createButtonWidget(HOLD_X));
layout->addLayout(btnbox, 1, colbb, 1, 1);
extraSize.setWidth(extraSize.width() + btnbox->sizeHint().width());
}
if (showValues) {
QLayout * valX = createNodeValueLayout('X', nodeLabelX);
QLayout * valY = createNodeValueLayout('Y', nodeLabelY);
layout->addLayout(valX, 2, colvx, 1, 1);
layout->addLayout(valY, 2, colvy, 1, 1);
}
layout->addItem(new QSpacerItem(0, 0), 0, 0, 1, 5); // r0 c0-4: top v spacer
layout->addItem(new QSpacerItem(0, 0), 1, 0, 2, 1); // r1-2 c0: left h spacer
layout->addWidget(gv, 1, 2, 1, 1); // r1 c2: stick widget
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); // r3 c0-4: bot v spacer
connect(node, SIGNAL(xChanged()), this, SLOT(updateNodeValueLabels()));
connect(node, SIGNAL(yChanged()), this, SLOT(updateNodeValueLabels()));
setSize(prefSize);
}
void VirtualJoystickWidget::setStickX(qreal x)
{
node->setX(x);
}
void VirtualJoystickWidget::setStickY(qreal y)
{
node->setY(y);
}
void VirtualJoystickWidget::setStickPos(QPointF xy)
{
node->setPos(xy);
}
void VirtualJoystickWidget::centerStick()
{
node->stepToCenter();
}
qreal VirtualJoystickWidget::getStickX()
{
return node->getX();
}
qreal VirtualJoystickWidget::getStickY()
{
return node->getY();
}
QPointF VirtualJoystickWidget::getStickPos()
{
return QPointF(node->getX(), node->getY());
}
void VirtualJoystickWidget::setTrimValue(int which, int value)
{
SliderWidget * slider = getTrimSlider(which);
if (slider) {
slider->setValue(value);
}
}
void VirtualJoystickWidget::setTrimRange(int which, int min, int max)
{
SliderWidget * slider = getTrimSlider(which);
if (slider) {
slider->setRange(min, max);
}
}
int VirtualJoystickWidget::getTrimValue(int which)
{
SliderWidget * slider = getTrimSlider(which);
if (slider) {
return slider->value();
}
return 0;
}
void VirtualJoystickWidget::setStickConstraint(int which, bool active)
{
if (btnHoldX == NULL)
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;
}
}
void VirtualJoystickWidget::setSize(QSize size)
{
float thisAspectRatio = (float)size.width() / size.height();
float newGvSz, spacerSz;
bool sized = false;
if (thisAspectRatio > ar) {
// constrain width
newGvSz = (height() - extraSize.height()) * ar; // new width
spacerSz = (width() - newGvSz - extraSize.width()) / 2; // + 0.5;
if (spacerSz >= 0.0f) {
layout->setRowStretch(0, 0);
layout->setColumnStretch(0, spacerSz);
layout->setColumnStretch(4, spacerSz);
layout->setRowStretch(3, 0);
sized = true;
}
}
if (!sized) {
// constrain height
newGvSz = (width() - extraSize.width()) * ar; // new height
spacerSz = (height() - newGvSz - extraSize.height()) / 2; // + 0.5;
spacerSz = qMax(spacerSz, 0.0f);
layout->setRowStretch(0, spacerSz);
layout->setColumnStretch(0, 0);
layout->setColumnStretch(4, 0);
layout->setRowStretch(3, spacerSz);
}
layout->setColumnStretch(2, newGvSz);
layout->setRowStretch(1, newGvSz);
prefSize = QSize(newGvSz + extraSize.width(), newGvSz + extraSize.height());
gv->resize(newGvSz, newGvSz);
gv->updateGeometry();
repositionNode();
//qDebug() << thisAspectRatio << size << newGvSz << spacerSz << extraSize << gv->geometry() << gv->contentsRect() << gv->frameRect() << getStickPos();
}
void VirtualJoystickWidget::repositionNode()
{
QRect qr = gv->contentsRect();
qreal w = (qreal)qr.width() - GBALL_SIZE;
qreal h = (qreal)qr.height() - GBALL_SIZE;
qreal cx = (qreal)qr.width()/2;
qreal cy = (qreal)qr.height()/2;
scene->setSceneRect(-cx,-cy,w,h);
QPointF p = node->pos();
p.setX(qMin(cx, qMax(p.x(), -cx)));
p.setY(qMin(cy, qMax(p.y(), -cy)));
node->setPos(p);
updateNodeValueLabels();
}
QWidget *VirtualJoystickWidget::createTrimWidget(QChar type)
{
QSizePolicy sp;
QString btnAlabel, btnBlabel;
QString btnAname = QString("%1TrimBtnA_%2").arg(type.toLower()).arg(stickSide);
QString btnBname = QString("%1TrimBtnB_%2").arg(type.toLower()).arg(stickSide);
QString sliderName = QString("%1TrimAdj_%2").arg(type.toLower()).arg(stickSide);
QWidget * trimWidget = new QWidget(this);
QBoxLayout * trimLayout = new QVBoxLayout(trimWidget);
SliderWidget * trimSlider = new SliderWidget(trimWidget);
QPushButton * trimBtnA = new QPushButton(trimWidget);
QPushButton * trimBtnB = new QPushButton(trimWidget);
if (type == 'H') {
sp = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
trimLayout->setDirection(QBoxLayout::LeftToRight);
trimSlider->setFixedHeight(23);
trimSlider->setOrientation(Qt::Horizontal);
trimBtnA->setText(ARROW_LEFT);
trimBtnB->setText(ARROW_RIGHT);
}
else {
sp = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
trimLayout->setDirection(QBoxLayout::TopToBottom);
trimSlider->setFixedWidth(23);
trimSlider->setOrientation(Qt::Vertical);
trimBtnA->setText(ARROW_UP);
trimBtnB->setText(ARROW_DOWN);
}
trimWidget->setSizePolicy(sp);
trimSlider->setObjectName(sliderName);
trimSlider->setProperty("trimType", getTrimSliderType(type));
trimSlider->setSizePolicy(sp);
trimSlider->setMinimum(-125);
trimSlider->setMaximum(125);
trimBtnA->setObjectName(btnAname);
trimBtnA->setProperty("btnType", getTrimButtonType(type, 0));
trimBtnA->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
trimBtnA->setMaximumSize(QSize(23, 23));
trimBtnA->setAutoDefault(false);
trimBtnB->setObjectName(btnBname);
trimBtnB->setProperty("btnType", getTrimButtonType(type, 1));
trimBtnB->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
trimBtnB->setMaximumSize(QSize(23, 23));
trimBtnB->setAutoDefault(false);
trimLayout->setSpacing(6);
trimLayout->setContentsMargins(8, 9, 8, 9);
trimLayout->addWidget(trimBtnA);
trimLayout->addWidget(trimSlider);
trimLayout->addWidget(trimBtnB);
connect(trimBtnA, SIGNAL(pressed()), SLOT(onTrimPressed()));
connect(trimBtnB, SIGNAL(pressed()), SLOT(onTrimPressed()));
connect(trimBtnA, SIGNAL(released()), SIGNAL(trimButtonReleased()));
connect(trimBtnB, SIGNAL(released()), SIGNAL(trimButtonReleased()));
connect(trimSlider, SIGNAL(valueChanged(int)), SLOT(onSliderChange(int)));
return trimWidget;
}
QPushButton * VirtualJoystickWidget::createButtonWidget(int type)
{
QString btnRole, btnLabel;
switch (type) {
case HOLD_Y:
btnLabel = tr("Hold Y");
break;
case FIX_Y:
btnLabel = tr("Fix Y");
break;
case FIX_X:
btnLabel = tr("Fix X");
break;
case HOLD_X:
default:
btnLabel = tr("Hold X");
break;
}
QPushButton * btn = new QPushButton(this);
btn->setObjectName(QString("%1_%2").arg(btnLabel.replace(" ", "_")).arg(stickSide));
btn->setProperty("btnType", type);
btn->setText(btnLabel);
QFont font;
font.setPointSize(8);
btn->setFont(font);
btn->setStyleSheet(QLatin1String( \
"QPushButton {"
" background-color: #EEEEEE;"
" border-style: outset;"
" border-width: 1px;"
" border-radius: 4px;"
" border-color: black;"
" padding: 2px;"
"}"
"QPushButton:checked {\n"
" background-color: #4CC417;\n"
" border-style: inset;\n"
"}" ));
btn->setCheckable(true);
connect(btn, SIGNAL(toggled(bool)), SLOT(onButtonChange(bool)));
return btn;
}
QLayout *VirtualJoystickWidget::createNodeValueLayout(QChar type, QLabel *& valLabel)
{
QLabel * lbl = new QLabel(QString("%1 %").arg(type), this);
lbl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
lbl->setAlignment(Qt::AlignCenter);
QLabel * val = new QLabel("0");
val->setObjectName(QString("val_%1").arg(type));
val->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
val->setAlignment(Qt::AlignCenter);
QVBoxLayout * layout = new QVBoxLayout();
layout->setContentsMargins(2, 2, 2, 2);
layout->setSpacing(2);
layout->addWidget(lbl);
layout->addWidget(val);
valLabel = val;
return layout;
}
int VirtualJoystickWidget::getTrimSliderType(QChar type)
{
if (stickSide == 'L') {
if (type == 'H')
return TRIM_AXIS_L_X;
else
return TRIM_AXIS_L_Y;
}
else {
if (type == 'H')
return TRIM_AXIS_R_X;
else
return TRIM_AXIS_R_Y;
}
}
int VirtualJoystickWidget::getTrimButtonType(QChar type, int pos)
{
if (stickSide == 'L') {
if (type == 'H') {
if (pos == 0)
return TRIM_LH_L;
else
return TRIM_LH_R;
}
else {
if (pos == 0)
return TRIM_LV_UP;
else
return TRIM_LV_DN;
}
}
// right side
else {
if (type == 'H') {
if (pos == 0)
return TRIM_RH_L;
else
return TRIM_RH_R;
}
else {
if (pos == 0)
return TRIM_RV_UP;
else
return TRIM_RV_DN;
}
}
}
SliderWidget *VirtualJoystickWidget::getTrimSlider(int which)
{
if (which == TRIM_AXIS_L_X || which == TRIM_AXIS_R_X)
return hTrimSlider;
else
return vTrimSlider;
}
void VirtualJoystickWidget::onTrimPressed()
{
if (!sender() || !sender()->property("btnType").isValid())
return;
emit trimButtonPressed(sender()->property("btnType").toInt());
}
void VirtualJoystickWidget::onSliderChange(int value)
{
if (!sender() || !sender()->property("trimType").isValid())
return;
emit trimSliderMoved(sender()->property("trimType").toInt(), value);
updateNodeValueLabels();
}
void VirtualJoystickWidget::onButtonChange(bool checked)
{
if (!sender() || !sender()->property("btnType").isValid())
return;
switch (sender()->property("btnType").toInt()) {
case HOLD_Y:
node->setCenteringY(!checked);
break;
case FIX_Y:
node->setFixedY(checked);
break;
case FIX_X:
node->setFixedX(checked);
break;
case HOLD_X:
node->setCenteringX(!checked);
break;
}
}
void VirtualJoystickWidget::updateNodeValueLabels()
{
if (nodeLabelX)
nodeLabelX->setText(QString("%1").arg((qreal)node->getX() * 100 + getTrimValue(0) / 5, 2, 'f', 0));
if (nodeLabelY)
nodeLabelY->setText(QString("%1").arg((qreal)node->getY() * -100 + getTrimValue(1) / 5, 2, 'f', 0));
}

View file

@ -0,0 +1,121 @@
/*
* 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 VIRTUALJOYSTICKWIDGET_H
#define VIRTUALJOYSTICKWIDGET_H
#include <QWidget>
#include <QResizeEvent>
#include <QBoxLayout>
#include <QGridLayout>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPushButton>
#include <QLabel>
class Node;
class SliderWidget;
#define GBALL_SIZE 20
#define RESX 1024
class VirtualJoystickWidget : public QWidget
{
Q_OBJECT
public:
enum TrimAxes {
TRIM_AXIS_L_X = 0,
TRIM_AXIS_L_Y,
TRIM_AXIS_R_Y,
TRIM_AXIS_R_X,
};
enum ConstraintTypes {
HOLD_X = 0,
HOLD_Y,
FIX_X,
FIX_Y
};
explicit VirtualJoystickWidget(QWidget * parent = NULL, QChar side = 'L', bool showTrims = true, bool showBtns = true, bool showValues = true, QSize size = QSize(245, 245));
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);
virtual QSize sizeHint() const
{
return prefSize;
}
virtual void resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
setSize(event->size());
}
signals:
void trimButtonPressed(int which);
void trimButtonReleased();
void trimSliderMoved(int which, int value);
protected slots:
void onTrimPressed();
void onSliderChange(int value);
void onButtonChange(bool checked);
void updateNodeValueLabels();
protected:
void setSize(QSize size);
void repositionNode();
QWidget * createTrimWidget(QChar type);
QPushButton * createButtonWidget(int type);
QLayout * createNodeValueLayout(QChar type, QLabel *& valLabel);
int getTrimSliderType(QChar type);
int getTrimButtonType(QChar type, int pos);
SliderWidget * getTrimSlider(int which);
QChar stickSide;
QSize prefSize;
QGridLayout * layout;
QGraphicsView * gv;
QGraphicsScene * scene;
Node * node;
SliderWidget * hTrimSlider, * vTrimSlider;
QPushButton * btnHoldX, * btnHoldY;
QPushButton * btnFixX, * btnFixY;
QLabel * nodeLabelX, * nodeLabelY;
QSize extraSize;
float ar; // aspect ratio
};
#endif // VIRTUALJOYSTICKWIDGET_H