How to reach a Qt widget from another class - qt

I'm implementing a Tetris game. In Qt Designer I drew a Frame widget.
Then I organized a QtGlass inheriting from that Frame. So, in Qt Designer this looks like Object frame with QtGlass class. Now I would like to make the figure move within existing limits (walls etc). I'm trying to implement it like is shown below.
Well, I've come across the fact that I fail to reach my QtGlass object. So, I know that it has a method isMovementPossible(), but I don't know how to use it. My QtGlass instance seems to be called "frame", but if I use this name, I get the error "Unable to resolve identifire frame".
QtGlass.h
#ifndef QTGLASS_H
#define QTGLASS_H
#include <QFrame>
#include "Figure.h"
class QtGlass : public QFrame {
Q_OBJECT
public:
bool isMovementPossible();
protected:
Figure Falcon;
...
}
Figure.cpp
#include "Figure.h"
#include "QtGlass.h"
#include <QtGui>
#include <QtGui/QApplication>
void Figure::set_coordinates(int direction) {
previous_x = current_x;
previous_y = current_y;
switch (direction) {
case 1:
{//Qt::Key_Left:
current_x -= 1;
if (frame->isMovementPossible()) {
break;
}
current_x += 1;
break;
}
...
}

To be accessible within your Figure method the frame variable would have to be either a global variable or a member of your Figure class (or of a superclass).
If you need access to your QtGlass instance within a Figure instance then you will need to pass a reference (or pointer) to it. You can either pass that to the Figure when it is constructed (assuming the frame outlives the figure) or pass it as a parameter to the method that needs it.
For example, if your frame always outlives the figures within it then you could simply do something like
class QtGlass; // forward declare to avoid circular header include
class Figure
{
public:
Figure( const QtGlass& glass ) : frame( glass ) {}
void set_coordinates(int direction) {
// ...
if (frame.isMovementPossible()) {
break;
}
// ...
}
// other methods...
private:
const QtGlass& frame;
// other members...
};
and your QtGlass constructor could do
QtGlass::QtGlass( QWidget* parent )
: QFrame( parent )
, Falcon( *this )
{
}
Alternatively you could have a dedicated setter method for setting the frame on the figure if setting it at construction time is not convenient. The member would need to be a pointer in that case though (although the setter could still pass by reference).

Related

How may I fix my error prone on_foo_bar slots

I have eight list widgets in a tab widget. They have similar names, and Designer's "Go to slot" mechanism has made links to slots it names (in the "private slots" section of "mainwindow.h") like:
void on_SR0listWidget_itemClicked(QListWidgetItem *item);
I saw warnings that "Slots named on_foo_bar are error-prone," and now I need to change their names in order to discover if that's the cause of the weird behaviour I'm getting.
I tried simply refactoring the names, but that stopped the slot code from working. I used Designer's graphical "Edit Signal/Slot" mechanism and was able to connect a newly added list widget's "itemClicked(QListWidgetItem *item)" signal to a newly added slot, and it looks OK in the graphical representation, but there's no debug message (that I set up in the Slot function) when an item is clicked.
I also use those widgets' "entered" signals, so there will be at least 16 to fix. I would write a script if it could be done by parsing the relevant files.
One example of exactly how to rename one of my replacement slots and connect an "item clicked" or "entered" signal to it (and where it should go) would be a great help.
Signals/slots setup through the designer rely on the names of the widgets involved. This can lead to problems if the widget names are changed. There are times when using the designer method will lead to code that compiles but doesn't actually make the connections you expect. This is why you are getting that warning.
You can get more reliable behavior by connecting the signals and slots programmatically. For example, let's say you have a class header such as:
#include <QMainWindow>
namespace Ui {
class MyWindow;
};
class QListWidgetItem;
class MyWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyWindow(QWidget* parent = nullptr);
~MyWindow() override;
private:
void handleItemClicked(QListWidgetItem* item); // this is your slot
Ui::MyWindow* ui;
};
You can connect the signal/slot together in the cpp file like this:
#include "MyWindow.h"
#include "ui_MyWindow.h"
#include <QDebug>
MyWindow::MyWindow(QWidget* parent)
: QWidget(parent),
ui(new Ui::MyWindow()) {
ui->setupUi(this);
// connect() has many overloads, but in this case we are passing:
// 1. the object emitting the signal
// 2. the signal being emitted
// 3. the object whose slot we want to call
// 4. the slot to connect to
connect(
ui->listWidget, &QListWidget::itemClicked,
this, &MyWindow::handleItemClicked);
}
MyWindow::~MyWindow() {
delete ui;
}
void MyWindow::handleItemClicked(QListWidgetItem* item) {
qDebug() << "item clicked";
}
You can still use the designer to layout your UI - but prefer to manage connections directly in code rather than through the designer.

Q_ENUMS with enum in header file

I want to use the elements of an enum as items in a QComboBox. I can do so if the enum is defined in the same class but I would like to use an enum defined in a header file. My goal is to use the enum directly from the header file, without altering the header file. Moreover, I would like my code to adapt to a changing enum, both in the name of the elements and in the number of them.
I have found this answer and this link that have helped me understand how to populate a QComboBox with an enum. The solution in the answer works for me but only if the enum is defined in the class.
The basic code I would like to implement looks like:
definitions.h
#ifndef _DEFINITIONS_H_
#define _DEFINITIONS_H_
typedef enum
{
FIRST = 0,
SECOND,
THIRD
} elements;
#endif
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "definitions.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
Q_ENUMS(elements);
public:
explicit MainWindow(QWidget *parent = 0);
QComboBox *comboBox;
};
#endif
mainwindow.cc
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
{
int index = metaObject()->indexOfEnumerator("elements");
QMetaEnum metaEnum = metaObject()->enumerator(index);
for (int i = 0; i < metaEnum.keyCount(); i++)
comboBox->addItem(metaEnum.valueToKey(i));
}
This code does not encounter any issue at compilation on runtime but has no effect at all. Defining the enum in MainWindow class works like a charm.
It does not look like a visibility issue because I can use the elements of the enum in my code and I have not encountered any compilation problem.
I have tried redefining my enum in mainwindow.h with typedef without success:
typedef elements new_elements
I have also tried replacing Q_ENUMS with Q_ENUM as suggested by the answer below but the result is the same.
Is it possible to use Q_ENUMS/Q_ENUM with an enum defined in a header file?
What should I do different?
You can't do that. Q_ENUMS need a meta object provided either by the QObject class or the Q_GADGET macro, that's why it works when the enum is declared in a class derived from a QObject.
Since Qt 5.8, a new macro was introduced Q_ENUM_NS that makes this possible:
This macro registers an enum type with the meta-object system. It must be placed after the enum declaration in a namespace that has the Q_NAMESPACE macro. It is the same as Q_ENUM but in a namespace.
You can do something like that:
namespace MyNamespace {
Q_NAMESPACE
enum elements {
FIRST = 0,
SECOND,
THIRD
};
Q_ENUM_NS(elements)
}
See this post for more information.
Updates
Regarding the last comment, you can create a new enumeration and associate each element with the value of the new one.
class MainWindow : public QMainWindow {
Q_OBJECT
public:
enum elements {
FIRST = ::elements::FIRST,
SECOND = ::elements::SECOND,
THIRD = ::elements::THIRD,
};
Q_ENUM(elements);
}
Then, for a function like:
void use_enum(elements e);
// Call it with the original one
use_enum(elements::FIRST);
// Call it with the new one, you just created
use_enum(MainWindow::elements::FIRST);
Check the Q_ENUM that registers automatically as metatype as it is said in the documentation What's New in Qt 5.5:
Added Q_ENUM to replace Q_ENUMS, which allows to get a QMetaEnum at compile time using QMetaEnum::fromType. Such enums are now automatically registered as metatypes, and can be converted to strings within QVariant, or printed as string by qDebug().

Passing word count into dialog qt

I am building a notepad and want to count the words in a dialog.
QString input = ui->textEdit->toPlainText();
int spaces = input.count(" ");
ui->NumWordsLabel->setNum(spaces);
This is my attempt so far.
However, I want to execute this code in my dialog so I need to pass the
ui->textEdit->toPlainText()
Into my dialog.
This is how I create my dialog...
void MainWindow::on_actionWord_Count_triggered()
{
word_count = new Word_count();
word_count->show();
}
How would I get the required information into the dialog?
Thanks.
Generally you can pass constructor arguments to pass data to your classes. For example:
Header file:
class Word_count : public QDialog
{
Q_OBJECT
public:
explicit Word_count(QString text, QObject *parent = 0);
...
}
Source file:
Word_count(QString text, QObject *parent)
: QDialog(parent)
{
ui->setup(this);
... figure out word count and set labels ...
}
How to use:
void MainWindow::on_actionWord_Count_triggered()
{
word_count = new Word_count(ui->textEdit->toPlainText());
word_count->show();
}
Important notes:
The QObject *parent argument should always be present in the constructor arguments. Make sure to only place the = 0 in the header file, or else you will get an error.
Your constructor should be marked explicit, unless you know you do not want that. Explicit prevents the C++ compiler from automatically casting to your type using a given constructor.
Pass the parent parameter to your inheriting class, whether that be QDialog, QWidget or QObject, using the constructor initializer list syntax. This is done in the source file example with : QDialog(parent).
You can add as many arguments as you need, but they should be before the parent argument. This is because the parent argument has a default value that can be implied. Because you must specify arguments in order, it can not be implied if there are required parameters after it.
This only will work for creating the dialog. If you want the dialog to dynamically update, you'll need to use a slot or method like suggested by others. Alternatively, if you don't want a dynamically updating dialog, consider using exec instead of show so that users must close your word count dialog before continuing with their work.
Add a slot like void setText( const QString& text ) to your Word_count class.
Then, you can emit a signal like void textChanged( const QString& text ) const from your MainWindowclass.
Don't forget to connect both.

how to translate string added dynamicaly in Qapp with Qtranslator?

i trying to created a Qt application multilanguage with Qt linguist.
I place this code in an function of my MainWindow :
translator.load(":/lang/English");
qApp->installTranslator(&translator);
ui->retranslateUi(this);
With the QTranslator declare in my MainWindow.h and all my string i want translate enclose by tr() . But with that, all QObject added dynamicaly by the code of my MainWindow.cpp, for example the title of a QTableWidget, are not translated.
If i place an other translator in my main.cpp, all my string are translate but i must created language button in my application and so i can't place translator in main.cpp.
Do you have an idea to help me?
Thx for your answers.
Gat
When you add a translation in your application using qApp->installTranslator(& aTranslator) then all the following calls to QObject::tr() (and similar functions) will look up in the translator for a translated text. So you should call retranslateUi() after qApp->installTranslator(). Actually you might event not call it there, you may reimplement QWidget::changeEvent() and intercept any QEvent::LanguageChange event.
void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
// Someone called qApp->installTranslator() with a new translation.
// Let's update the user visible strings.
// This function was created by uic from the Designer form.
// It translates all user visible texts in the form.
ui->retranslateUi(this);
// This function must be written by you. It translates other user visible
// texts that are not in the form. See down for an example.
this->retranslateUi();
break;
default:
break;
}
}
ui->retranslateUi() just calls QObject::tr() for each user visible string in the ui. It is called automatically at the end of setupUi() and populates the form's widgets with translated text (have a look, it is defined by uic in ui_MainWindow.h file). You may want to take a similar approach with other user visible texts, like the title of a QTableWidget. All the strings are set in a function (named perhaps retranslateUi() for consistency) which is called at application starts (or, better, after the relevant widgets are created) and every time a new translations is loaded.
MainWindow::MainWindow(QWidget * parent)
: QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Creates other widgets, but do not set their user visible texts.
tableWidget = new QTableWidget(this);
...
someControl = new QLineEdit(this);
someOtherControl = new QSpinBox(this);
someModel = new MyModel(this);
...
// Ok, *now* we set their texts.
this->retranslateUi();
}
...
void MainWindow::retranslateUi()
{
// This function will be called (either manually or automatically by
// `changeEvent()`) when a new translator has installed, hence all the `tr()`
// calls will return the right translation for the last installed language.
QStringList labels;
labels.append(tr("First column"));
labels.append(tr("Second column"));
labels.append(tr("Third column"));
tableWidget->setHorizontalHeaderLabels(labels);
someControl->setText(tr("Control name"));
someOtherControl->setText(tr("Other control name"));
// Perhaps you have a model that need to be retranslated too
// (you need to write its `retranslateUi()` function):
someModel->retranslateUi();
...
}
Also, please note that if you are doing something like
void MainWindow::someFunction()
{
...
QTranslator translator;
translator.load(":/lang/English");
qApp->installTranslator(& translator);
...
}
as soon as that function returns the variable translator gets destroyed, so next time QObject::tr() is called the address stored with qApp->installTranslator(& translator) will be invalid. So you must allocate translator on the heap with new (and possibly deleting it with delete when you do not need it anymore). An exception is if you are doing that in main() before calling QCoreApplication::exec() since that function is blocking and will not return until application is closed.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
...
QTranslator translator;
translator.load(":/lang/English");
app.installTranslator(& translator);
...
app.exec(); // This function will return only on application's exit.
// Hence `translator` will outlive the application, there is
// no need to worry about it.
}

Qt: update pixmap grid layout with 2d array values

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!

Resources