I started my first steps programming in Qt and can't find an answer.
I have some widget, let's say it's named "tab".
I want to add to it's layout new widget.
I can do it by writing:
QLabel *label = new QLabel(tab);
I want to do the same thing , but instead of saying "tab" I want to use widget name stored in QString variable. Something like this (but it doesn't work, can't convert QString to QWidget):
QString name = "tab";
QLabel *label = new label(zakladka);
Is it possible?
QObjects have a property objectName. So if you name your objects and have them be part of a hierarchy of QObject (i.e. you know they are children of a parent object) you can make use of findChild
QLabel* label = new QLabel(parentWidget->findChild(name));
Related
I am using a QWidget in which I would like to put some separator lines.
As separator lines I am using this
QFrame *seperatorLine = new QFrame(_toolBar);
seperatorLine->setFrameStyle(QFrame::Sunken | QFrame::VLine);
I need several separator lines and I was curious whether I need to create a new QFrame every time or whether there is a way to reuse one (or use a copy-constructor).
At the moment the line is only at the last position I added it to the QWidget.
QObject and thus QWidget derived class cannot access a copy constructor.
Instead of that, you can encapuslate your QFrame property in a little factory method:
QFrame* createSeparator(QWidget* parent=0) {
QFrame *separatorLine = new QFrame(parent);
separatorLine->setFrameStyle(QFrame::Sunken | QFrame::VLine);
return separatorLine;
}
I prefer this method over subclassing QFrame to tweak several properties of a QFrame instance
A fancy way to "clone" a QObject would be to create a new object and assign all declared properties. Of course it is only useful if you want to transfer values:
CustomObject* CustomObject::clone() {
int count = metaObject()->propertyCount();
CustomObject* clone = new CustomObject(this->parent());
for (int i = 0; i < count; i++) {
const char* prop = metaObject()->property(i).name();
clone->setProperty(prop, property(prop));
}
return clone;
}
This might be a simple question for most of your experts but I've been banging my head on this for the last few hours.
I have this layout which I declare in the header file like this:
private:
QVBoxLayout *vlayout;
I then initialize it in the constructor:
QVBoxLayout *vlayout = new QVBoxLayout();
ui->mainscroll_menu->setWidgetResizable(true);
ui->mainscroll_menu->widget()->setLayout(vlayout);
vlayout->setSpacing(1);
vlayout->setAlignment(Qt::AlignTop);
vlayout->setMargin(0);
I then try to use it inside a slot for example:
void MainWidget::on_pushButton_clicked()
{
vlayout->addWidget(menuitem);
}
This will compile ok but it will fail during runtime and crash my application. Upon running the debugging, it shows that it crashes when I try to use vlayout.
If I initialize the layout inside the slot, it will then work without crashing but that comes with other issues as I have to initialize the layout for each and everytime I want to use it.
What am I doing wrong?
The problem is this line in the constructor:
QVBoxLayout *vlayout = new QVBoxLayout();
This is creating a local variable in the constructor that is shadowing the member variable by the same name. When you try to use the member variable in any other method, you end up using an uninitialized value - hence the crash.
The fix is to simply change the line to:
vlayout = new QVBoxLayout();
Is this even possible in Qt? I have a QListWidget setup with a delegate for specific painting, and I'm attempting to have the delegate paint differently based on variables in my QListWidget's parent. I figure I can rig something up as long as I have a pointer to either, but I need to somehow get them while in the delegate's paint().
I've tried the "hack" to store a pointer in a QVariant, but it doesn't seem to be working, and I'd rather not take this approach. If I could get to the pointer to item the delegate represents, and just call item->listWidget(), this would be perfect, but it doesn't seem possible while inside the delegate.
Is there any workaround to accomplish this?
Also, just because.. sample of trying to get the void* trick to work - if this is the only way to do this, perhaps someone may see what I have wrong in it.
//Parent of QListWidget
....
QListWidgetItem *item = new QListWidgetItem();
....
QVariant v = qVariantFromValue((void *) pStitchSymbolCustom);
item->setData(Qt::UserRole + 6, v);
....
//Implemented QAbstractItemDelegate
....
MyClass* p_pointer =
(MyClass*)(index.data(Qt::UserRole + 6).value<void *>());
I'm not sure, if i understood your question correctly, but if you want to access the QListWidget wich contains your delegate, then i think, that the easiest way is to set the parent of the delegate as the listwidget, and then get the listwidget at any point of the delegate:
QAbstractItemDelegate * delegate = new MyItemDelegate(myListWidget);
myListWidget.setItemDelegate(delegate);
and then in the delegate code:
QListWidget * listWidget = qobject_cast<QListWidget*>(parent());
//You can do whatever you want your list here
If your object tree is deep, and you want the code to be generic and not care about where the desired parent is, you should ascend the tree automatically:
QListWidget * listWidget = 0;
QObject * object = parent();
while (object && ! listWidget) {
// qobject_cast will succeed once the parent of the correct type is reached
listWidget = qobject_cast<QListWidget*>(object);
object = object->parent();
}
I am trying to use QStandardItemModel to do the same thing as QStringListModel (just for practice):
http://programmingexamples.net/wiki/Qt/ModelView/StandardItemModel
However, one cell shows up, but it is empty, as opposed to containing "text" as I would expect. Can anyone explain this? Is this the right way to go about using the QStandardItemModel (i.e. constructing QStandardItems and feeding them to the model?)
Actually, I'm surprised you aren't getting a crash. You are creating item0 on the stack, then passing a pointer to it to the QList. When that method leaves scope, item0 is deleted, and your list contains a pointer to the rotting area of memory that used to hold a QStandardItem.
{
QStandardItem item0("test");
QList<QStandardItem*> items;
items.insert(0, &item0); // Doesn't transfer ownership
model->appendRow (items);
} // Out of scope! Oh no!
Typically you would just create the new item, then add it using something like QStandardItemModel::setItem, like this:
QStandardItem *item0 = new QStandardItem("test");
model->setItem(0, 0, item); // transfers ownership of item0 to the model
I am undertaking a game using a combination of c++ in visual studios 2010 and Qt 4.7 (both windows). The game is a clone of battleship and is console input based. I have created my gui how I want it to look, and on the Qt side in Qt designer, my gui consists of a grid layout 10x10, using labels to hold pixmaps of game cells:
I have painstakingly named each label to represent its position in the 2d array (ie. fleet map => F_00 => F[0,0] => F[i],[j]). I can manually choose what pixmap I would like to display using the properties editor, but I would like something dynamic.
I use an update mapboard class to redraw the game board after a player fires, which keeps storing over a char array. I would like to update my pixmaps for each, using a generic getupdatearray type function. As we traverse the array it will update the pixmap currently associated with individual labels to match their cousins from the array. (say F[5][6] = 'X' for hit, then when the loops got to that position in the array it would update the grid of pixmaps at F_56 to equal hit.png, replacing the empty.png.
I have an idea how to make the loop that would accomplish this, but unsure how i would go about getting the pixmap for each label to be more along the lines of a runtime feature versus the now compile time (static) feature. I have read about QPainter and another Qt class that deals with images, but still having a hard go at it.
Question to any of you, how do I update these pixmaps based on a 2d array?
loop structure - i can figure out
condition statements - i can figure out
qt specific syntax dealing with labels- newbie so no idea atm.
Here's some pseudocode of the kind of thing I am trying to do with map.h:
#include <QtCore>
#include <QtGui>
// WARNING: PSEUDOCODE, DOES NOT COMPILE
// AT A LOSS ON HOW TO SELECT THE CORRECT LABEL
// MAYBE A CHILD CLASS FOR THAT?
class map {
public:
char updateboard(char mapname, char b[][10]){
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
char C = b[i][j];
if (C == 'M'){//miss
Qlabel mapname_[i][j](<img src='images/missspace.png'/>);
mapname_[i][j].show();
}
else if(C == 'X'){//hit
Qlabel mapname_[i][j](<img src='images/hitspace.png'/>);
mapname_[i][j].show();
}
else if(C == ' '){//undiscovered space
Qlabel mapname_[i][j](<img src='image/emptyspace.png'/>);
mapname_[i][j].show();
}
}
}
}
};
Then in my mainwindow.cpp, I include map.h and say:
// calls class function update board
// takes updated array values and replaces old pixmap with new
map.updateboard(T,b[][10]); // target map update
map.updateboard(F,v[][10]); // fleet map update
Thanks in Advance
UPDATE:
I've gotten to the point where I can swap pixmaps with buttons presses, but I would like to create something more dynamic. I wanted to use a Qstring in which I place the name of the label I want to change using by appending x y values using:
TR_position.append(QString::number(xvalue));
When I try to call it using:
ui->TR_position->setPixmap(QPixmap(":/images/missspace.png"));
...it obviously doesnt work. Is there a way to type case it, or use the contents of the string as the Qlabel name?
You manually entered and named 200 label widgets? Let no one call you lazy. :)
Per your update, you now know how to use QLabel::setPixmap(). What you think you need is getting a QLabel pointer from a name, which would be a combination of two things:
QWidget::findChild to get a QWidget* from a QString
qobject_cast to get a QLabel* from a QWidget
If you go down this path, what you'd wind up with is something like:
QWidget* cellWidget = ui->findChild(TR_position);
QLabel* cellLabel = qobject_cast<QLabel*>(cellWidget);
cellLabel->setPixmap(QPixmap(":/images/missspace.png"));
But BEWARE! There are many things wrong with this approach.
It's brittle: What if there doesn't happen to be any widget with that name (mysterious crash)? Or even worse, what if there are multiple widgets with that name and this code marches along blissfully ignorant of that odd condition that is likely a bug?
It's poor OOP: While there are some decent cases to use dynamic casting (or "downcasting"), it usually indicates a flaw in a design. You know that all QLabels are QWidgets, but not all QWidgets are QLabels...so that qobject_cast call might return NULL. It's just one more point of failure. Sometimes you can't avoid this, but really there is no reason your program needs to be structured in such a way.
It's terribly slow: Searching for a widget by its name is essentially a naive recursive search. If you've set aside a separate widget frame for each grid and only search that, Qt will have to do 100 string compares to find the last element (so 50 in the average case). Imagine clearing the grid with a loop...now you're talking about 100*50 string compares!
All these things are avoidable. Just as it's possible to use loops to set the images on the controls by name, it's possible to use loops to create the widgets in the first place. You basically would leave the area for the game board blank in the design tool, and then dynamically create the controls with code...attach them to the layout with code...and save pointers to them in 2D array. (You wouldn't access them by label name at that point, you'd index them just as you are indexing your board.)
You could create your own class derived from QLabel (such as a GameCell class) which contained the information for your board cell and methods related to it. Then you wouldn't need an array of label widgets in parallel to an array representing your board. You'd simply have one array of objects that took care of both aspects of the implementation.
UPDATE: Since you asked in the comments for specifics, here's a GameCell class:
class GameCell : public QLabel
{
Q_OBJECT
public:
enum State { Undiscovered, Hit, Miss };
GameCell (QWidget *parent = 0) : QLabel (parent),
currentState (Undiscovered)
{
syncBitmap();
}
State getState() const { return currentState; }
void setState(State newState) {
if (currentState != newState) {
currentState = newState;
syncBitmap();
}
}
private:
void syncBitmap() { // you'd use setPixmap instead of setText
switch (currentState) {
case Undiscovered: setText("U"); break;
case Hit: setText("H"); break;
case Miss: setText("M"); break;
}
}
State currentState;
};
This does double duty by behaving like a QWidget as well as maintaining a piece of internal state. Then a GameMap widget can use a QGridLayout of these GameCells:
class GameMap : public QWidget {
Q_OBJECT
public:
static const int Rows = 10;
static const int Columns = 10;
GameMap (QWidget* parent = 0) :
QWidget (parent)
{
layout = new QGridLayout (this);
for (int column = 0; column < Columns; column++) {
for (int row = 0; row < Rows; row++) {
GameCell* cell = new GameCell (this);
cells[column][row] = cell;
layout->addWidget(cell, row, column);
}
}
}
private:
GameCell* cells[Columns][Rows];
QGridLayout* layout;
};
If you wanted to, you could just leave spaces in your layout in the designer you wanted to fill in with the GameMap widget. Or you can push on and do the whole thing programmatically. For the sake of simplicity I'll just put two boards next to each other with a vertical separator on the surface of a dialog:
class Game : public QDialog
{
Q_OBJECT
public:
Game (QWidget *parent = 0)
: QDialog(parent)
{
targetMap = new GameMap (this);
fleetMap = new GameMap (this);
verticalSeparator = new QFrame (this);
verticalSeparator->setFrameShape(QFrame::VLine);
verticalSeparator->setFrameShadow(QFrame::Sunken);
layout = new QHBoxLayout (this);
layout->addWidget(targetMap);
layout->addWidget(verticalSeparator);
layout->addWidget(fleetMap);
setLayout(layout);
setWindowTitle(tr("Battleship"));
}
private:
GameMap* targetMap;
QFrame* verticalSeparator;
GameMap* fleetMap;
QHBoxLayout* layout;
};
I'm not going to write a whole game here or make it look fancy. That's just the gist, showing how to get 200 labels up in a programmatic fashion:
With my code, getting a GameCell from an (x,y) coordinate doesn't require an average of 50 string compares. Due to the formalized and predictable nature of 2D arrays, indexing into cells[x][y] only requires a single multiply operation and a single addition operation. There's no downcasting, and you can simply write:
cells[x][y].setState(GameCell::Miss);
ADDENDUM: Creating a QWidget for each grid cell isn't necessarily the way to go in the first place. Some might consider that "heavyweight". If your game were being played out on a large virtual space of tiles then it could be much too slow. You might find it useful to look into QGraphicsGridLayout, which could be a more appropriate approach in the long run:
http://doc.qt.io/qt-5/qtwidgets-graphicsview-basicgraphicslayouts-example.html
Using QWidgets won't be much of an issue with a 10x10 grid, however, so if you want to just stick with that then you can. If you're going to do it that way, then at least you shouldn't be placing them all by hand!