Using the GroupBoxes in Qt - c++

I'm a student programmer and I am using Qt to build a GUI application. I'm trying to ensure that some check boxes are checked in order to proceed. These check boxes enable or disable the group box itself and are part of the QGroupBox class. Accepted combinations could be either or both. The problem I am running into is getting the boolean value(at least that's what I think it is) from the QGroupBox member function QGroupBox::setChecked(bool) and use it to determine whether or not to display an error message. I have tried several methods and reference Qts documentation hoping for a good example. Because the QGroupBox I'm trying to use is a member of my ui class I tried creating a new instance of QGroupBox and setting it to the values of the ui. Then; using an if statement, find out whether or not the boxes are check or not. Here's my code for this:
QGroupBox activeParticleInjection = ui->groupBoxParticleInjection;
QGroupBox activeFluidInjection = ui->groupBoxFluidInjection;
if (activeParticleInjection::setChecked(false) && activeFluidInjection(false));
{
QMessageBox noInjectionSelectedError;
noInjectionSelectedError.setText("Error: No injection type selected");
noInjectionSelectedError.exec();
}
else
{
transData.particleInjectionActive = activeParticleInjection::setChecked();
transData.fluidInjectionActive = activeFluidInjection::setChecked();
This doesn't work; starting with the way I'm trying to pass the Ui properties to the new instance of QGroupBox. I know that is question is relatively generic question but I tried passing the ui checkbox directly and that caused even more issues. I looked through the documentation and that led me to the way im trying to do it know; with no luck. I was hoping for some feedback on a better method of handling QGroupBox. Being a student sometimes its hard to see the answer especially when dealing with such unique members as the ones put together in QT.
Prior to changes I was using this method to build this process; and I received errors for the way my if parameters were setup. Compile error was : no matching function for call to 'QGroupBox::isChecked(bool)'
if (ui->groupBoxFluidInjection->isChecked(false) && ui->groupBoxParticleInjection->isChecked(false));
{
QMessageBox noInjectionSelectedError;
noInjectionSelectedError.setText("Error: No injection type selected");
noInjectionSelectedError.exec();
}
else
{
transData.particleInjectionActive = ui->groupBoxParticleInjection->isChecked();
transData.fluidInjectionActive = ui->groupBoxFluidInjection->isChecked();
}
I have been using these sites for most help:
QGroupBox
QCheckBox

You're mixing several things:
The QGroupBox instances in ui are pointers, so you must assign them to a QGroupBox* (pointer), not a QGroupBox (object on the stack).
:: is used with methods only if the method you call is static (class method), which is not the case here.
setChecked() is a setter setting the checked state of the group box. It doesn't return anything (void), so you cannot use them as conditions. What you want there is the getter bool QGroupBox::isChecked().
Your code snippet cleansed:
QGroupBox* activeParticleInjection = ui->groupBoxParticleInjection;
QGroupBox* activeFluidInjection = ui->groupBoxFluidInjection;
if (!activeParticleInjection->isChecked() && !activeFluidInjection->isChecked())
{
QMessageBox::critical(this, tr("Error"), tr("No injection type selected"));
}
else
{
transData.particleInjectionActive = activeParticleInjection->isChecked();
transData.fluidInjectionActive = activeFluidInjection->isChecked();
}

Not really sure what you tried to do in your code, but that's actually a lot easier to achieve. Also don't try to create copies (which you do in your example). Work with references or pointers in that case!
QGroupBox group("my group box"); // of course this might be a class member, too
group.setCheckable(true);
// do other things...
if(group.isChecked())
{
// do whatever if it's checked
}
else
{
// do stuff if it isn't checked
}
To check for at least one of multiple groups (or check boxes) being checked:
if(group1.isChecked() || group2.isChecked())

Related

Qt: when can I access dynamic properties from qtcreator?

I have a couple of widgets in QtCreator that are promoted from the same class. However, I'd like them to have subtle differences between the two, so I'd like to pass some differences in the UI file that the promoted class can use to distinguish itself. Dynamic properties seem like the way to go so in the UI editor I've assigned a dynamic property to each promoted widget. In the code I tried accessing the property, but noticed it seems to only be available post construction (probably because Qt is calling setProperty() after the object is created.
MyWidget::MyWidget(QWidget* parent) : QGLWidget(parent)
{
this->property("someProperty").toString(); // returns blank
}
void MyWidget::initializeGL()
{
this->property("someProperty").toString(); // returns string set in UI file
}
So my question how do people use these properties for constructor-type stuff? I could just do that in initializeGL, but that seems odd since these properties might not be related to initializing OpenGL. I imagine I could also connect to the property changed signal and do it there. Is that the common way to handle this?
If the generated code for setupUi() from your .ui file does something like this:
MyWidget *w = new MyWidget;
w->setProperty(...);
then your constructor is accessing a meta property that does not yet exist.
You can reimplement QObject::event() to capture QDynamicPropertyChangeEvents, letting you act once the property is initialized.
bool MyWidget::event(QEvent *ev)
{
if (ev->type() == QEvent::DynamicPropertyChange) {
if (QDynamicPropertyChangeEvent *propEv = static_cast<QDynamicPropertyChangeEvent *>(ev)) {
if (propEv->propertyName() == "someProperty")
...
}
}
}
Bear in mind that this code will be called every time there is a dynamic property change.
A better approach may be to create a function to perform the necessary initilization on the widget after setupUi() etc. are called and the dynamic property is created.
void setupMyWidget(MyWidget *w)
{
QString s = w->property("someProperty").toString();
...
}
Typically, dynamic properties are assigned a default value in the constructor so that they are always available and non-null later.
setProperty("someProperty", defaultValue);

How to break the tab order chain of widgets in Qt?

In Qt you can define the tab order by using the Qt Designer or by using C++. The relationships between widgets are set relatively to each other, so there is no index or such thing. What I want right now is to "break" the circular chain of widgets so that I get a beginning and an end of the chain.
A circular tab order would be:
A - B
| |
D - C
I want (note missing link between A and D):
A - B
|
D - C
which is more like a line instead of a circle:
A - B - C - D
So the user "stops" at one end and has to go back using the other direction.
Update: I have another idea now. What if i reimplement:
bool QWidget::focusNextPrevChild(bool next)
According to the documentation one can use this to implement custom focus behavior.
In my dynamic scenario where buttons in the GUI are adjusted at run-time I will have to overload the function and set, for example, an internal flag allowFocusNext and allowFocusPrev which then ignores the focus request if necessary. I will report back here, when I have tried it. Meanwhile any comments are welcome!? :-)
I found a solution, but it is a bit hacky. The QWidget::setTabOrder will not allow to chain a widget with itself, so this approach won't help (even if you are using focus proxies)
However, you can define a "Focus Forwarder":
class FocusForwarder : public QWidget
{
public:
explicit FocusForwarder(QWidget *proxy) :
QWidget((QWidget *) proxy->parent()),
m_proxy(proxy)
{
setFocusPolicy(Qt::TabFocus);
}
protected:
void focusInEvent(QFocusEvent *) {
m_proxy->setFocus();
}
private:
QWidget *m_proxy;
};
And add them at the beginning and end of you chain:
FocusForwarder *w1 = new FocusForwarder(ui->bA);
FocusForwarder *w2 = new FocusForwarder(ui->bD);
QWidget::setTabOrder(w1, ui->bA);
QWidget::setTabOrder(ui->bA, ui->bB);
QWidget::setTabOrder(ui->bB, ui->bC);
QWidget::setTabOrder(ui->bC, ui->bD);
QWidget::setTabOrder(ui->bD, w2);
Details
For setTabOrder to work, the widgets must be in the same window. To ensure this, the Forwarder is placed in the proxy's parent (in the initializer list).
For this mechanism, the focus direction (Tab or Shit+Tab) does not matter. As soon as a FocusFowarder receives the focus, it will "forward" it to its proxy.
The direction is handled by Qt internally. You just add "sentinels" around your chain.
Use in QtDesigner
When you want to use it in QtDesigner, you'd create a Widget and promote it to the forwarder. As you cannot set the proxy directly, you could add a dynamic property for the proxy's name, like this:
class FocusForwarderDesigner : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString proxyName READ proxyName WRITE setProxyName)
public:
QString proxyName() {
return (m_proxy) ? m_proxy->objectName() : QString::null;
}
void setProxyName(QString name) {
m_proxy = parent()->findChild<QWidget *>(name);
}
explicit FocusForwarderDesigner(QWidget *parent = NULL) :
QWidget(parent) {}
protected:
void focusInEvent(QFocusEvent *) {
if (m_proxy) m_proxy->setFocus();
}
private:
QWidget *m_proxy;
}
In the designer, you would add a string-property with name proxyName and set it to the proxy's name. Don't forget to set the focus policy to Tab Focus in designer.
After some additional thoughts I post an answer to my own question because it is a working solution but it is not ideal. Therefore, I'm still searching for a better one! As a note, my application mainly relies on mouse wheel interactions for changing the focus of widgets.
In my question I mentioned that overriding:
bool focusNextPrevChild(bool next)
could lead to a working system. The "receiving" widget would simply ignore the focus by returning "true" if it is marked as "last item" or "first item" and the "next" parameter would lead to a circular behavior. Although this works for the tab and space+tab key combinations, there are cases where focusNextPrevChild is not called explicitly. In my case it is not called for focus changes related to mouse wheel events.
What I do instead is overriding:
void wheelEvent(QWheelEvent* event)
This gives me direct control over all the focus events related to the mouse wheel. My overridden function looks like this:
void SelectionIconButton::wheelEvent(QWheelEvent* event)
{
bool next = event->delta() > 0;
if (m_IsLastInFocusChain && next) {
event->accept();
return;
}
if (m_IsFirstInFocusChain && !next) {
event->accept();
return;
}
QPushButton::wheelEvent(event);
}
So this system's requirements are:
Each widget has to somehow implement two bools and handle their
state.
Each of those widgets has to be configured either at startup
or in dynamic screens during appliation use
Listening only to
wheelEvent does not allow me to handle tab key and space+tab key
combinations
You see that this solution works but it involves some effort to apply it to a large application. I was thinking about a more general solution. Maybe a global list that is updated when a screen is changing. This global list would then somehow decide if a focus change is allowed or not. Unfortunately, this again is problematic with mouse wheel events because some widgets are "active" and the wheel event does not even want to change focus but alter the value in an input field, for example, instead.
Edit:
I might have to add that the default implementation of QWidget::wheelEvent() and QPushButton::wheelEvent() and many more Qt-Widgets just ignore the event by setting event->ignore().
In my application all those ignored events are caught at a high level widget which then interprets the QWheelEvent and uses its delta to call focusPreNextChild() the right amount of time.

Simple acces of C++ object data from QML

I'm making a board game in Qt/C++ using qml. All the important game data is represented in a single class. My intention is to have the qml sheets access this one object and draw the game board depending on the data.
What is the simplest approach to exposing the C++ data members to QML?
Now I know the question has been asked, I've seen the answers and the Qt Documentation. I am, however, not satisfied. What I've seen, the way to do this seems to be to make Q_PROPERTY out of every single variable I want to access from QML. This looks to me tedious and unnecessary, not to mention it will stretch the code to 3x it's original length, making it significantly worse to read. Also, in most cases I won't need write function to the data members, for example.
And why bother with Q_PROPERTY overhead when I could just write Q_INVOKABLE getters for just the situations I need?
Here's an example of how simple I hoped it would be when I read in the Qt Project documentation: "This enables C++ data and functions to be accessible directly from QML, often with little or no modification."
class game : public QObject
{
Q_OBJECT
public:
explicit game(QObject *parent = 0);
colors NPC[3]; // colors being an enum declared elsewhere
player players[4]; // player is a non-QObject class containing player stats
}
...
game gMain;
QDeclarativeContext *context = viewer.rootContext();
context->setContextProperty("Game",&gMain);
QML in my ideal world:
Image {
id : Image1
source: { if (Game.NPC[0] === 0) {
if (Game.players[1].LifeCount > 0) {
return "pics/FigG.png"
}
else {
return "pics/StoneG.png"
}
}
Now how close to that can I actually get with QML and how do I go about it?
I'm especially interested in handling simple C++ style arrays and enums (have a lot of those in the game) - would I need to write helper functions, e.g. int Game.GetNPCAt(int i) instead of using just Game.NPC[i] ?
I realize that the way I DON'T want to do it is the tried and trusted, and for good reason... however in my situation (small one-man project) it seems like using a cannon to kill a fly (although the GUI building part in qml is amazingly simple and quite a joy to use) - also having to wrap around every data member including the simplest like an int seems... ridiculously excessive.
Maybe I have missed something somewhere, In which case I humbly apologize. Thank you for any thoughts on the matter.
In order:
Q_PROPERTY: When you look at the page that you quoted, they discuss using the Q_PROPERTY method to expose properties to QML. If you don't use Q_PROPERTY, it is my understanding that your variables won't be registered by QMLViewer (or what have you). The Q_PROPERTY needs a Q_INVOKABLE to get/set your variables. If you don't use Q_PROPERTY, though, your class properties will not appear in QML.
Setting the image source: If you may remember, QML is a forge between CSS and JavaScript. If you're just looking to make the image's source change depending on a condition outside of your Image element, you can just create a JavaScript function to achieve what you have quoted:
Image {
id: Image1
function getImage()
{
if (Game.NPC[0] === 0)
{
if (Game.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
However, the function won't run by itself: you'll have to associate it with a signal, which I would create in your C++ class (put the function under a label named signals: (NOT within public -- see here on how to write signals)). Based on your example, I'm guessing that your C++ object is called Game.
Game {
id: gameKeeper //or whatever you want to name it
onUpdate: Image1.getImage() //replace onUpdate with your signal
}
Image {
id: Image1
function getImage()
{
if (gameKeeper.NPC[0] === 0)
{
if (gameKeeper.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
In theory, you should be able to reference arrays this way with JavaScript (I'm not all that familiar with JS myself).
Array handling: On the C++ side, it looks like the best way to do it is through a QList. Fortunately, a QList iterates very similar to a normal array. I found this, which should help -- just ignore the second dimension.
Hope this helps.

Weird bug in Qt application

In my application, I have my re-implemented QGraphicsView checking for a mouseReleaseEvent(), and then telling the item at the position the mouse is at to handle the event.
The QGraphicsItem for my view is made up of two other QGraphicsItems, and I check which one of the two is being clicked on (or rather having the button released on), and handle the respective events.
In my Widget's constructor, I set one of the items as selected by default, using the same methods I used when the items detect a release.
When I debugged, I found that for the LabelItem, select is called without a problem from the constructor (and the result is clear when I first start the application). But, when I click on the items, the application terminates. I saw that I was getting into the select function, but not leaving it. So the problem is here.
Which is very weird, because the select function is just a single line setter.
void LabelItem::select()
{
selected = true;
}
This is the mouseReleaseEvent;
void LayerView::mouseReleaseEvent(QMouseEvent *event)
{
LayerItem *l;
if(event->button() == Qt::LeftButton)
{
l = (LayerItem *) itemAt(event->pos());
if(l->inLabel(event->pos()))
{ //No problem upto this point, if label is clicked on
l->setSelection(true); //in setSelection, I call select() or unselect() of LabelItem,
//which is a child of LayerItem, and the problem is there.
//In the constructor for my main widget, I use setSelection
//for the bottom most LayerItem, and have no issues.
emit selected(l->getId());
}
else if(l->inCheckBox(event->pos()))
{
bool t = l->toggleCheckState();
emit toggled(l->getId(), t);
}
}
}
When I commented the line out in the function, I had no errors. I have not debugged for the other QGraphicsItem, CheckBoxItem, but the application terminates for its events as well. I think the problem might be related, so I'm concentrating on select, for now.
I have absolutely no clue as to what could have caused this and why this is happening. From my past experience, I'm pretty sure it's something simple which I'm stupidly not thinking of, but I can't figure out what.
Help would really be appreciated.
If the LabelItem is on top of the LayerItem, itemAt will most likely return the LabelItem because it is the topmost item under the mouse. Unless the LabelItem is set to not accept any mouse button with l->setAcceptedMouseButtons(0).
Try to use qgraphicsitem_cast to test the type of the item. Each derived class must redefine QGraphicsItem::type() to return a distinct value for the cast function to be able to identify the type.
You also could handle the clicks in the items themselves by redefining their QGraphicsItem::mouseReleaseEvent() method, it would remove the need for the evil cast, but you have to remove the function LayerView::mouseReleaseEvent() or at least recall the base class implementation, QGraphicsView::mouseReleaseEvent(), to allow the item(s) to receive the event.
I have seen these odd behaviours: It was mostly binary incompatibility - the c++ side looks correct, and the crash just does not make sense. As you stated: In your code the "selected" variable cannot be the cause. Do you might have changed the declaration and forgot the recompile all linked objects. Just clean and recompile all object files. Worked for me in 99% of the cases.

Using images in QListWidget, is this possible?

I am using QT to create a chat messenger client. To display the list of online users, I'm using a QListWidget, as created like this:
listWidget = new QListWidget(horizontalLayoutWidget);
listWidget->setObjectName("userList");
QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Expanding);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(listWidget->sizePolicy().hasHeightForWidth());
listWidget->setSizePolicy(sizePolicy1);
listWidget->setMinimumSize(QSize(30, 0));
listWidget->setMaximumSize(QSize(150, 16777215));
listWidget->setBaseSize(QSize(100, 0));
listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
Users are shown by constantly refreshing the list, like this: (Note: There are different channels, with different userlists, so refreshing it is the most efficient thing to do, as far as I know.)
void FMessenger::refreshUserlist()
{
if (currentPanel == 0)
return;
listWidget = this->findChild<QListWidget *>(QString("userList"));
listWidget->clear();
QList<FCharacter*> charList = currentPanel->charList();
QListWidgetItem* charitem = 0;
FCharacter* character;
foreach(character, charList)
{
charitem = new QListWidgetItem(character->name());
// charitem->setIcon(QIcon(":/Images/status.png"));
listWidget->addItem(charitem);
}
}
This has always worked perfectly. The line that I commented out is the one I have problems with: my current goal is to be able to display a user's online status with an image, which represents whether they are busy, away, available, etc. Using setIcon() does absolutely nothing though, apparently; the items still show up as they used to, without icons.
I'm aware that this is probably not the way this function needs to be used, but I have found little documentation about it online, and absolutely no useful examples of implementations. My question is, can anybody help me with fixing this problem?
This is how you may conduct your debugging:
Try the constructor that has both icon and text as arguments.
Try to use that icon in another context to ensure it is displayable (construct a QIcon with same argument and use it elsewhere, e.g. QLabel!).
Use icon() from the QListWidgetItem to receive back the icon and then look at that QIcon.
Create a new QListWidget, change nothing, and ordinarily add some stock items in your MainWidget's constructor. See if the icons show up there.