I have this ImGui menu:
I want to move the "Del" button to the red selected area in the previous image.
This is that part of the menu snippet:
class Waypoint {
public:
int x, y, z;
std::string action;
std::string display;
Waypoint(std::string action, int x, int y, int z) {
this->action = action;
this->x = x;
this->y = y;
this->z = z;
this->display = action + " " + std::to_string(x) + " " + std::to_string(y) + " " + std::to_string(z);
}
};
static int listbox_item_current = 0;
Waypoint wp1("ROPE", 100, 100, 7);
Waypoint wp2("WALK", 100, 100, 6);
Waypoint wp3("WALK", 110, 131, 6);
std::vector<Waypoint> listbox_items{ wp1, wp2, wp3 };
if (ImGui::CollapsingHeader("Cavebot")) {
ImGui::ListBox(
"##listbox::Cavebot",
&listbox_item_current,
waypoint_getter,
listbox_items.data(),
listbox_items.size()
);
ImGui::SameLine();
if (ImGui::Button("Clean"))
listbox_items.clear();
ImGui::SameLine();
if (ImGui::Button("Del"))
listbox_items.erase(listbox_items.begin() + listbox_item_current);
How can I move the "Del" button to be below the "Clean" button?
EDIT:
Testing removing ImGui::SameLine(); between both buttons:
I normally use ImGui::SetCursorPos() for this, as suggested by #thedemons. But there is also ImGui::BeginGroup();.
Remove the last ImGui::SameLine(); and wrap the two buttons in Begin/EndGroup. Here's a simplified example:
ImGui::Begin("Window");
ImGui::Button("x", ImVec2(200,100));
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Button("Alpha");
ImGui::Button("Beta");
ImGui::EndGroup();
ImGui::End();
You can use ImGui::SetCursorPos to set the item position to your desire.
ImVec2 currentCurPos = ImGui::GetCursorPos();
if (ImGui::Button("Clean"))
listbox_items.clear();
ImVec2 DelButtonPos(currentCurPos.x, currentCurPos.y + 25);
ImGui::SetCursorPos(DelButtonPos);
if (ImGui::Button("Del"))
listbox_items.erase(listbox_items.begin() + listbox_item_current);
Related
I'm creating a screen where users can add certain tiles to use in an editor, but when adding a tile the window does not correctly resize to fit the content. Except that when I drag the window or resize it even just a little then it snaps to the correct size immediately.
And when just dragging the window it snaps to the correct size.
I tried using resize(sizeHint()); which gave me an incorrect size and the following error, but the snapping to correct size still happens when resizing/dragging.
QWindowsWindow::setGeometry: Unable to set geometry 299x329+991+536 on QWidgetWindow/'TileSetterWindow'. Resulting geometry: 299x399+991+536 (frame: 8, 31, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 259x329, maximum size: 16777215x16777215).
I also tried using updateGeometry() and update(), but it didn't seem to do much if anything.
When setting the window to fixedSize it will immediately resize, but then the user cannot resize the window anymore. What am I doing wrong here and where do I start to solve it?
Edit
Minimal verifiable example and the .ui file.
selected_layout is of type Flowlayout
The flowlayout_placeholder_1 is only there because I can't place a flowlayout directly into the designer.
Edit2
Here is a minimal Visual Studio example. I use Visual Studio for Qt development. I tried creating a project in Qt Creator, but I didn't get that to work.
Edit3
Added a little video (80 KB).
Edit4
Here is the updated Visual Studio example. It has the new changes proposed by jpo38. It fixes the issue of the bad resizing. Though now trying to downsize the windows causes issues. They don't correctly fill up vertical space anymore if you try to reduce the horizontal space even though there is room for more rows.
Great MCVE, exactly what's needed to easily investigate the issue.
Looks like this FlowLayout class was not designed to have it's minimum size change on user action. Layout gets updated 'by chance' by QWidget kernel when the window is moved.
I could make it work smartly by modifying FlowLayout::minimumSize() behaviour, here are the changes I did:
Added QSize minSize; attribute to FlowLayout class
Modifed FlowLayout::minimumSize() to simply return this attribute
Added a third parameter QSize* pMinSize to doLayout function. This will be used to update this minSize attribute
Modified doLayout to save computed size to pMinSize parameter if specified
Had FlowLayout::setGeometry pass minSize attribute to doLayout and invalidate the layout if min size changed
The layout then behaves as expected.
int FlowLayout::heightForWidth(int width) const {
const int height = doLayout(QRect(0, 0, width, 0), true,NULL); // jpo38: set added parameter to NULL here
return height;
}
void FlowLayout::setGeometry(const QRect &rect) {
QLayout::setGeometry(rect);
// jpo38: update minSize from here, force layout to consider it if it changed
QSize oldSize = minSize;
doLayout(rect, false,&minSize);
if ( oldSize != minSize )
{
// force layout to consider new minimum size!
invalidate();
}
}
QSize FlowLayout::minimumSize() const {
// jpo38: Simply return computed min size
return minSize;
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly,QSize* pMinSize) const {
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
// jpo38: store max X
int maxX = 0;
for (auto&& item : itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
// jpo38: update max X based on current position
maxX = qMax( maxX, x + item->sizeHint().width() - rect.x() + left );
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
// jpo38: save height/width as max height/xidth in pMinSize is specified
int height = y + lineHeight - rect.y() + bottom;
if ( pMinSize )
{
pMinSize->setHeight( height );
pMinSize->setWidth( maxX );
}
return height;
}
I was having the same exact issue (albeit on PySide2 rather than C++).
#jpo38's answer above did not work directly, but it un-stuck me by giving me a new approach.
What worked was storing the last geometry, and using that geometry's width to calculate the minimum height.
Here is an untested C++ implementation based on the code in jpo38's answer (I don't code much in C++ so apologies in advance if some syntax is wrong):
int FlowLayout::heightForWidth(int width) const {
const int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
void FlowLayout::setGeometry(const QRect &rect) {
QLayout::setGeometry(rect);
// e-l: update lastSize from here
lastSize = rect.size();
doLayout(rect, false);
}
QSize FlowLayout::minimumSize() const {
// e-l: Call heightForWidth from here, my doLayout is doing things a bit differently with regards to margins, so might have to add or not add the margins here to the height
QSize size;
for (const QLayoutItem *item : qAsConst(itemList))
size = size.expandedTo(item->minimumSize());
const QMargins margins = contentsMargins();
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
size.setHeight(heightForWidth(qMax(lastSize.width(), size.width())));
return size;
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const {
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
for (auto&& item : itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
int height = y + lineHeight - rect.y() + bottom;
return height;
}
I'm coding some custom widgets in FLTK, one is a single digit spinner for touch screen which is a value with one arrow above and another below.
class Spinner: public Fl_Group
{
private:
::std::unique_ptr<Fl_Button> button_up_;
::std::unique_ptr<Fl_Button> button_down_;
::std::unique_ptr<Fl_Output> value_;
public:
static unsigned int const WIDTH = 36;
static unsigned int const HEIGHT = 100;
...
};
Spinner::Spinner(int x, int y, size_t val)
:Fl_Group(x, y, WIDTH, HEIGHT)
,button_up_(new Fl_Button(x + 2, y + 1, 32, 17))
,button_down_(new Fl_Button(x + 2, y + 82, 32, 17))
,value_(new Fl_Output(x + 2, y + 18, 32, 64))
{
char v[] = {'0', 0};
if (val > 9) val = 9;
v[0] = val + '0';
button_up_->callback(reinterpret_cast<Fl_Callback*>(&Spinner::upcbk), this);
button_down_->callback(reinterpret_cast<Fl_Callback*>(&Spinner::downcbk), this);
button_up_->image(up_arrow);
button_down_->image(down_arrow);
button_up_->color(FL_BLACK);
button_down_->color(FL_BLACK);
button_up_->box(FL_FLAT_BOX);
button_down_->box(FL_FLAT_BOX);
value_->box(FL_BORDER_BOX);
value_->color(FL_BLACK);
value_->labelcolor(FL_RED);
value_->selection_color (FL_RED);
value_->textsize(46);
value_->textcolor(FL_RED);
value_->value(v);
value_->redraw();
end();
}
void Spinner::upcbk(Fl_Widget*, Spinner* spnr)
{
cout << "Spinner::upcbk()" << endl;
spnr->increment();
}
void Spinner::downcbk(Fl_Widget*, Spinner* spnr)
{
cout << "Spinner::downcbk()" << endl;
spnr->decrement();
}
When I instantiate one Spinner object in the window it works ok and each callback gets called when its corresponding arrow is clicked.
The other custom widget is a Spinner agregation:
class UintSpinner: public Fl_Group
{
private:
uint16_t value_;
uint16_t const max_;
uint16_t const min_;
uint16_t const order_;
char fmt_[6];
::std::vector< ::std::unique_ptr <Spinner> > spinners_;
...
};
UintSpinner::UintSpinner(int x, int y, uint16_t minval, uint16_t maxval
,uint16_t val)
:Fl_Group(x, y, Spinner::WIDTH * static_cast<uint16_t>(log10(max_)) + 1, Spinner::HEIGHT)
,value_(val < minval ? minval : (val > maxval ? maxval : val))
,max_(maxval)
,min_(minval)
,order_(static_cast<uint16_t>(log10(max_)) + 1)
{
strncpy(fmt_, "%00hu", 6);
fmt_[2] = static_cast<char>(order_) + '0';
char buffer[6];
snprintf(buffer, 6, fmt_, val);
for (ssize_t i = order_ - 1; i >= 0; i--) {
spinners_.emplace_back(new Spinner(x + i * Spinner::WIDTH, y, buffer[i] - '0'));
spinners_.back()->callback(&UintSpinner::spinner_cb, this);
}
}
void UintSpinner::spinner_cb(Fl_Widget* cb, void* p)
{
cout << "UintSpinner::spinner_cb()" << endl;
reinterpret_cast<UintSpinner*>(p)->spinnercb();
}
In this case If I istantiate an object of UintSpinner class with max value of 244 it correctly renders three individual spinners but neither Spinner::upcbk nor Spinner::downcbk get called when I hit one of the arrows.
I tried to use stack variables for Fl_Button and Fl_Output inside the Spinner class and the behavior is the same. I also tried to create a vector of Spinners instead of a vector of pointers but it does not compile because the Fl_Group seems not moveable.
Am I doing something wrong? Why using the exactly the same code in Spinner class their internal members callbacks works when using that instance directly and not when using the instance inside another custom widget?
Best regards and thank you for your help.
Ok, I found the solution.
In the UintSpinner constructor, when calling the base Fl_Group constructor, I pass a width parameter with an undefined value (a copy-paste error). According to Erco's FLTK Cheat Page -> Fl_Group Event vs. Draw Clipping when the children of a Fl_Group expands outside its bounding box, by default the render is not clipped but its mouse events are. So changing the constructor to:
UintSpinner::UintSpinner(int x, int y, uint16_t minval, uint16_t maxval
,uint16_t val)
:Fl_Group(x, y, Spinner::WIDTH * static_cast<uint16_t>(log10(maxval) + 1), Spinner::HEIGHT)
,value_(val < minval ? minval : (val > maxval ? maxval : val))
,max_(maxval)
,min_(minval)
,order_(static_cast<uint16_t>(log10(max_)) + 1)
{
...
solve the issue.
I want to display a QGraphicsRectItem in my QChartView. But the rectangle is displayed behind the lines series in the chart.
I've tried to do a setZValue(10), for example, on my QGraphicsRectItem and setZValue(0) on my QChart but it is still displayed behind.
Obviously I want the informations in the rectangle to be displayed in front of the series of the chart.
Constructor
StatisticsChartView::StatisticsChartView(QWidget *parent, QChart *chart)
: QChartView(chart, parent)
{
/* Create new chart */
_chart = new QChart();
chart = _chart;
_chart->setAnimationOptions(QChart::AllAnimations);
/* Default granularity */
m_iGranularity = DEFAULT_GRANULARITY;
/* Creating ellipse item which will display a circle when the mouse goes over the series */
m_ellipse = new QGraphicsEllipseItem(_chart);
penEllipse.setColor(QColor(0, 0, 0));
penBorder.setWidth(1);
m_ellipse->setPen(penEllipse);
/* Creating text item which will display the x and y value of the mouse position */
m_coordX = new QGraphicsSimpleTextItem(_chart);
m_coordY = new QGraphicsSimpleTextItem(_chart);
penBorder.setColor(QColor(0, 0, 0));
penBorder.setWidth(1);
m_coordX->setPen(penBorder);
m_coordY->setPen(penBorder);
m_rectHovered = new QGraphicsRectItem(_chart);
m_rectHovered->setBrush(QBrush(Qt::yellow));
m_coordHoveredX = new QGraphicsSimpleTextItem(m_rectHovered);
m_coordHoveredY = new QGraphicsSimpleTextItem(m_rectHovered);
penBorder.setColor(QColor(0, 0, 0));
penBorder.setWidth(1);
m_coordHoveredX->setPen(penBorder);
m_coordHoveredY->setPen(penBorder);
m_lineItemX = new QGraphicsLineItem(_chart);
m_lineItemY = new QGraphicsLineItem(_chart);
penLine.setColor(QColor(0, 0, 0));
penLine.setStyle(Qt::DotLine);
m_lineItemX->setPen(penLine);
m_lineItemY->setPen(penLine);
/* Enable zooming in the rectangle drawn with the left click of the mouse, zoom out with right click */
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
mousePressed = 0;
seriesHovered = false;
setMouseTracking(true);
_chart->setAcceptHoverEvents(true);
_chart->setZValue(50);
m_ellipse->setZValue(10); //so it is displayed over the series
m_coordHoveredX->setZValue(20); //so it is displayed over others
setRenderHint(QPainter::Antialiasing);
setChart(_chart);
}
Creation of series
void StatisticsChartView::drawCurve(bool bDrawScale)
{
int w = WIDTH;
int h = HEIGHT;
/* Creating series */
QLineSeries *lineFalse = new QLineSeries();
QLineSeries *lineAutomatic = new QLineSeries();
QLineSeries *lineOk = new QLineSeries();
QLineSeries *lineFalsePositive = new QLineSeries();
QLineSeries *lineManualTreatement = new QLineSeries();
QLineSeries *lineFalseNegative = new QLineSeries();
QList<QLineSeries*> lineSeriesList;
lineSeriesList << lineFalse << lineAutomatic << lineOk << lineFalsePositive << lineManualTreatement << lineFalseNegative;
QList<QString> nameSeriesList;
nameSeriesList << "False" << "Automatic" << "Ok" << "FalsePositive" << "ManualTreatement" << "FalseNegative";
QList<QVector<GraphPoint>> graphPointList;
graphPointList << gpFalse << gpDetected << gpOk << gpDetectedNotOk << gpManualTreatement << gpFalseNegative;
double graphX = 100.0 / (m_iGranularity);
bool pointsVisible = true;
for (int n = 0; n < lineSeriesList.count(); ++n)
{
/* Adding points to line series */
for (int i = 0; i < m_iGranularity + 1; ++i)
{
lineSeriesList[n]->append(i * graphX, (float)(graphPointList[n][i]).fValue * 100);
lineSeriesList[n]->setPointsVisible(pointsVisible);
lineSeriesList[n]->setName(nameSeriesList[n]);
}
}
_chart->legend()->setVisible(true);
_chart->legend()->setAlignment(Qt::AlignBottom);
/* Setting axis X and Y */
axisX = new QValueAxis();
axisY = new QValueAxis();
axisX->setRange(0, 100);
axisY->setRange(0, 100);
/* Adding line series to the chart and attaching them to the same axis */
for (int j = 0; j < lineSeriesList.count(); ++j)
{
_chart->addSeries(lineSeriesList[j]);
_chart->setAxisX(axisX, lineSeriesList[j]);
_chart->setAxisY(axisY, lineSeriesList[j]);
connect(lineSeriesList[j], SIGNAL(hovered(QPointF, bool)), this, SLOT(onSeriesHovered(QPointF, bool)));
}
_chart->resize(w, h);
return;
}
Drawing rectangle on chart
void StatisticsChartView::onSeriesHovered(QPointF point, bool state)
{
seriesHovered = state;
/* Updating the size of the rectangle */
if (mousePressed == 0 && seriesHovered == true)
{
/* x and y position on the graph */
qreal x = _chart->mapToPosition(point).x();
qreal y = _chart->mapToPosition(point).y();
/* x and y value on the graph from 0 to 100 for ou graph */
qreal xVal = point.x();
qreal yVal = point.y();
qreal maxX = axisX->max();
qreal minX = axisX->min();
qreal maxY = axisY->max();
qreal minY = axisY->min();
/* We don't want to display value outside of the axis range */
if (xVal <= maxX && xVal >= minX && yVal <= maxY && yVal >= minY)
{
m_coordHoveredX->setVisible(true);
m_coordHoveredY->setVisible(true);
m_rectHovered->setVisible(true);
m_ellipse->setVisible(true);
m_rectHovered->setRect(x - 31, y - 31, 30, 30);
qreal rectX = m_rectHovered->rect().x();
qreal rectY = m_rectHovered->rect().y();
qreal rectW = m_rectHovered->rect().width();
qreal rectH = m_rectHovered->rect().height();
/* We're setting the labels and nicely adjusting to chart axis labels (adjusting so the dot lines are centered on the label) */
m_coordHoveredX->setPos(rectX + rectW / 4 - 3, rectY + 1);
m_coordHoveredY->setPos(rectX + rectW / 4 - 3, rectY + rectH / 2 + 1);
/* Setting value to displayed with four digit max, float, 1 decimal */
m_coordHoveredX->setText(QString("%1").arg(xVal, 4, 'f', 1, '0'));
m_coordHoveredY->setText(QString("%1").arg(yVal, 4, 'f', 1, '0'));
m_ellipse->setRect(QRectF::QRectF(x, y, 10, 10));
m_ellipse->setPos(x, y);
m_ellipse->setBrush(QBrush(Qt::red));
}
else
{
/* We're not displaying information if out of the chart */
m_coordHoveredX->setVisible(false);
m_coordHoveredY->setVisible(false);
m_rectHovered->setVisible(false);
m_ellipse->setVisible(false);
}
}
else
{
/* We're not displaying information if series aren't hovered */
m_coordHoveredX->setVisible(false);
m_coordHoveredY->setVisible(false);
m_rectHovered->setVisible(false);
m_ellipse->setVisible(false);
}
}
You should try using a series especially for your rectangle.
Setting it as the last series, on your chart, to be above the other lines. And adding a legend or a callout for the text.
I'm trying to draw text "on cylinder". It means, that I have five rows of text. Top row is rotated by the X axis for 10 degrees. The second for 5 degrees. The middle is not rotated at all. The four's row is rotated for -5 degrees. The five's is rotated for -10 degrees.
Rows 1, 2, 3 draws OK, but something is wrong with 4,5 rows. What am I doing wrong ?
I provides an image for understanding a problem and code snippet:
for( int i = 0; i < iterationsCount; ++i )
{
const QRect r( x2, y2, textWidth, itemHeight );
const QString text = sections.at( section ).values.at( index );
int rsc = 0;
p->save();
rsc = widgetHeight / 2 - y;
p->setTransform(QTransform().rotate(rsc, Qt::XAxis));
if( type == Section::DaySectionShort ||
type == Section::DaySectionLong )
{
QStringList values = text.split( QLatin1Char( ' ' ) );
p->setPen(
lighterColor( opt.palette.color( QPalette::WindowText ), 75 ) );
p->drawText( r, Qt::AlignLeft | Qt::TextSingleLine, values.at( 0 ) );
p->setPen( opt.palette.color( QPalette::WindowText ) );
p->drawText( r, Qt::AlignLeft | Qt::TextSingleLine, values.at( 1 ) );
}
else
{
p->drawText( r, Qt::AlignLeft | Qt::TextSingleLine, text );
}
p->setTransform(QTransform().rotate(-rsc, Qt::XAxis));
index = nextIndex( index, sections.at( section ).values.size() );
y += itemHeight + itemTopMargin;
p->restore();
}
My problem
As you have not provided a minimal complete code that reproduces the problem, I cannot guess what is wrong there. But the most probable reason is incorrect rsc calculation. At least the following draft works:
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class MyWidget: public QWidget
{
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
QFont f = font();
f.setPointSize(15);
setFont(f);
}
protected:
void paintEvent(QPaintEvent *event) override
{
QWidget::paintEvent(event);
QPainter p(this);
const int itemHeight = fontMetrics().height();
const int itemTopMargin = 15;
const int xOffset = 15;
int y = itemHeight;
for (size_t i = 0; i < 5; ++i) {
// The angle is in range [-40, 40]. Remove " * 4" for [-10, 10].
const int rsc = (10 - 5 * i) * 4;
qDebug() << i << ":\t" << rsc << "\t" << y;
/*
Rotation must be performed relative to central point of the
drawn item. Transformations below are applied in reverse order.
At first translate item to make it's center in (0, 0). At
second rotate it relative to X axis. At third move the item to
desired position.
*/
QTransform transform;
transform.translate(xOffset, y + itemHeight / 2);
transform.rotate(rsc, Qt::XAxis);
transform.translate(0, - itemHeight / 2);
p.setTransform(transform);
p.drawText(QPoint(), QString("(Item no. %1)").arg(i + 1));
y += itemHeight + itemTopMargin;
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MyWidget widget;
widget.setMaximumSize(200, 250);
widget.show();
return app.exec();
}
The transformation used here is complicated because of need to rotate each item relative to it's central y, not y = 0. This also may be the case.
Font and angles are increased to see considered effects better.
A friend of mine and I are currently trying to make a game in C++ using Qt. Our current problem is that we need to fetch text from a QGraphicsTextItem on a button mousePressEvent. In the game menu it is possible to choose how many players there are, therefore we've placed a QGraphicsTextItem in a for-loop to make it possible for all the users to type in their names. Because of the for-loop, we don't have names for every single text item object so we can store the names. We've managed to store all the memory addresses to the objects using QMap, but we don't know how to get the text of of this. We don't even know if this is the best way to do it.
GameInfo.h
class GameInfo {
public:
GameInfo();
int players;
QStringList names = (QStringList() // Default player names. This array should be overwritten by the custom names
<< "Player 1"
<< "Player 2"
<< "Player 3"
<< "Player 4"
<< "Player 5"
<< "Player 6"
<< "Player 7");
QMap<int, QGraphicsTextItem**> textBoxMap; // This is where we store all the addresses
};
Game.cpp
QGraphicsRectItem * overviewBox = new QGraphicsRectItem();
overviewBox->setRect(0, 0, 782, 686);
scene->addItem(overviewBox);
int faceNo = 0;
// Create the player selection section
for(int i = 1; i <= players; i++) { // "players" is defined another place in the code, and is an integer between 1 and 6
Container * selContainer = new Container();
selContainer->Selection(i, faceNo);
selContainer->setPos(50, 70 + 110 * (i - 1));
scene->addItem(selContainer);
Container * ovContainer = new Container(overviewBox);
ovContainer->Overview(i, faceNo);
ovContainer->setPos(0, 0 + 110 * (i - 1));
info->textBoxMap.insert(i, &selContainer->textBox->playerText); // This is where we save the addresses
}
Selection.cpp
extern Game * game;
Container::Container(QGraphicsItem * parent): QGraphicsRectItem(parent) {
}
void Container::Selection(int nPlayers, int sPiceNo, QGraphicsItem *parent) {
QString numName = QString::number(nPlayers);
setRect(0, 0, 672, 110);
this->setPen(Qt::NoPen); // Removes border
int posY = this->rect().height() / 2;
QSignalMapper * signalMapper = new QSignalMapper(this);
arrowL = new Arrow(0, posY - 32, 0, this);
piece = new Piece(sPiceNo, 96, posY - 32, 1, 1, this);
arrowR = new Arrow(192, posY - 32, 1, this);
textBox = new TextBox(game->info->names[nPlayers - 1], true, this);
textBox->setPos(288, posY - 32);
lockBtn = new Button("Lock", 96, 32, this);
connect(lockBtn, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(lockBtn, nPlayers);
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(lock(int)));
lockBtn->setPos(640, posY - 16);
}
void Container::Overview(int ovPlayers, int ovPiceNo, QGraphicsItem * parent) {
// Some code...
}
void Container::lock(int nPlayer) {
qDebug() << game->info->textBoxMap[nPlayer];
qDebug() << game->info->names[nPlayer - 1];
game->info->names[nPlayer - 1] = **game->info->textBoxMap[nPlayer].toPlainText(); // This line causes an error
}
The error that occurs because of the last line looks like this:
error: no match for 'operator=' (operand types are 'QString' and 'QGraphicsTextItem')
game->info->names[nPlayer - 1] = **game->info->textBoxMap[nPlayer];
^
TextBox.cpp
TextBox::TextBox(QString text, bool editable, QGraphicsItem * parent): QGraphicsRectItem(parent) {
this->editable = editable;
// Draw the textbox
setRect(0, 0, 320, 64);
if(!editable) {
this->setPen(Qt::NoPen); // Removes border
}
else if(editable) {
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(QColor(255, 255, 255, 255));
setBrush(brush);
}
// Draw the text
playerText = new QGraphicsTextItem(text, this);
int fontId = QFontDatabase::addApplicationFont(":/fonts/built_titling_bd.ttf");
QString family = QFontDatabase::applicationFontFamilies(fontId).at(0);
QFont built(family, 25);
playerText->setFont(built);
int xPos = 0;
int yPos = rect().height() / 2 - playerText->boundingRect().height() / 2;
playerText->setPos(xPos,yPos);
}
My question is how do i fetch the text from the QGraphicsTextItem?
You should try to learn a bit more about C++ before trying to develop a game imo. (Having public variables is against OO's and C++ paradigm)
But here is what you are looking for:
http://doc.qt.io/qt-5/qgraphicstextitem.html#toPlainText
EDIT:
If you are not able to debug some line of code, I could only recommend to try to seperate your code in order to have a minimum of call in a single line. I haven't try the code bellow, but that how you should try to debug your code:
void Container::lock(int nPlayer)
{
qDebug() << game->info->textBoxMap[nPlayer];
qDebug() << game->info->names[nPlayer - 1];
QGraphicsTextItem **value = game->info->textBoxMap.value(nPlayer, nullptr);
game->info->names[nPlayer - 1] =(*value)->toPlainText();
}