Qt How to know when a checkbox is selected when created dynamically? - qt

I am trying to dynamically create a checkbox for each restaurant in a vector. I was able to dynamically create checkboxes, but I would then like to know which ones were selected. Here is my code:
for(unsigned int i = 0; i < restaurantList.size(); i++)
{
QCheckBox *thisCheckBox = new QCheckBox(restaurantList.at(i).GetName());
CTui->verticalLayout->addWidget(thisCheckBox);
}
I currently have 5 restaurants filled in the vector, and as of now I have it look something like this:
() McDonalds
() Papa John's
() Burger King
() Dominos
() Taco Bell
When they click an "OK" button, I would like to know which ones were selected. They are all called thisCheckBox, so I don't know how to get each specific one. How can I do this? Any help is much appreciated!

A simple way to achieve your goal is to amend the creation loop like this:
// Add this to your widget's class declaration:
std::vector<QCheckBox*> checkBoxes;
then
for (auto& r : restaurantList) {
auto cb = new QCheckBox(r.GetName());
checkBoxes.push_back(cb);
connect(cb, &QCheckBox::stateChanged, this, &YourWidget::onCheckBoxClicked);
CTui->verticalLayout->addWidget(cb);
}
then add a handler function to YourWidget (whatever it may be) like this:
void YourWidget::onCheckBoxClicked(int state)
{
// If you wanted to work with the specific checkbox clicked:
// Use QObject::sender() to retrieve a pointer to the QCheckBox,
// and cast it from QObject* to QCheckBox*:
// auto cb = qobject_cast<QCheckBox*>(sender());
// Otherwise, if you don't care which, and want to work with them all:
// Loop over all your checkboxes, doing whatever you want:
for (auto cb : checkBoxes) {
qDebug() << "The checkbox with text" << cb->text() << "has state" << state;
}
}
Add this to the slots in the declaration of YourWidget.

Related

How to change the label property of a QTabWidget on double click?

How do I recognize a double click on a tab in order to change its label?
Preferrably I could edit the label in place but alternatively I could also get a string from another input box. Any suggestions?
The tab gets added and the label specified currently like:
QString tab_label = QString("Shell (") + QString::number(session->id(), 16) + ")";
addTab(session->widget(), tab_label);
and I'd want to be able to edit the label after creation.
Oh and I should mention here that I'm a Qt newbie, too!
EDIT1
full method:
int SessionStack::addSession(Session::SessionType type)
{
Session* session = new Session(type, this);
connect(session, SIGNAL(titleChanged(int,QString)), this, SIGNAL(titleChanged(int,QString)));
connect(session, SIGNAL(terminalManuallyActivated(Terminal*)), this, SLOT(handleManualTerminalActivation(Terminal*)));
connect(session, SIGNAL(activityDetected(Terminal*)), m_window, SLOT(handleTerminalActivity(Terminal*)));
connect(session, SIGNAL(silenceDetected(Terminal*)), m_window, SLOT(handleTerminalSilence(Terminal*)));
connect(session, SIGNAL(destroyed(int)), this, SLOT(cleanup(int)));
m_sessions.insert(session->id(), session);
QString tab_label = QString("Shell (") + QString::number(session->id(), 16) + ")";
addTab(session->widget(), tab_label);
emit sessionAdded(session->id());
raiseSession(session->id());
return session->id();
}
There's a QTabBar::tabBarDoubleClicked signal, you just need to connect it to a slot to detect a double click. Also you'll need some widget to actually edit the tab's text. If you want it "out of place" (say, you open a dialog) then it should be enough to do something like:
connect(tabWidget->tabBar(), &QTabBar::tabBarDoubleClicked,
this, MyWidget::editTabBarLabel);
void MyWidget::editTabBarLabel(int tabIndex)
{
if (tabIndex < 0)
return;
// open dialog asking for the new label,
// f.i. via QInputDialog::getText
// set the new label bakc
}
If instead you want some in-place modification you'll need to more or less heavily modify QTabBar to do so.
The simplest option would be opening a QLineEdit on the right tab. To get the geometry of a tab via QTabBar:.tabrect, so that you can place the line edit in the same geometry. You'll very likely fall short on that path (see below) and you'll need to subclass QTabBar and use initStyleOption for the given tab, then set the lineedit's geometry to the right subrect (for instance, do not cover the "side widgets" of a tab).
Random pseudo braindumped code:
void MyTabBar::editTabBarLabel(int tabIndex)
{
if (tabIndex < 0)
return;
if (!m_lineEdit) {
m_lineEdit = new QLineEdit(this);
// once done, commit the change, or abort it, anyhow
// destroy/hide (?) the line edit
connect(m_lineEdit, &QLineEdit::editingFinished,
this, &MyTabBar::renameLabel);
} else {
// we're actually editing something else, what to do?
// commit the other change and start editing here?
}
m_editedTabIndex = tabIndex; // remember which one we're editing
m_lineEdit->setText(tabText(tabIndex));
// not "entirely" accurate, you need better subrect control here,
// cf. QStyle and https://doc.qt.io/qt-5/style-reference.html#widget-walkthrough
// that's why this should really be a QTabBar subclass, because
// you'll need to invoke initStyleOption and then fetch the subrects from the style
m_lineEdit->setGeometry(tabRect(tabIndex));
m_lineEdit->show();
}
// also track resize of the tabbar, relayout, tab reorder, tab insertion and removal, etc.
// move the QLineEdit accordingly

Is there any way to add Chekboxes dynamically that I can access them and their checkstate out of their definition scope?

I want to add checkboxes dynamically. I found some links helpfull ,but each of them has a problem that I can't solve.
for example in this link we can create & add QCheckBoxes dynamically, but I can't access them out of their definition scope (for).
And another way is using QListWidgetItem with setCheckState. but It has a very big problem! when I click on the CheckState, it doesn't notice it and just focus on the Item that is focus is on it!!!! {in these links this problem is introduced but no solution: this and this }
Is there any way to add Chekboxes dynamically that I can access them and their checkstate out of their definition scope?
You need to save pointers to checkBoxes that you've created. For example to add QVector<QCheckBox> checkBoxes; to your widget and then append this pointers to vector.
Result code can look like this:
In header:
YourWidget : public QWidget {
....
QVector<QCheckBox*> checkboxes;
....
And in the source:
for (int i = 0; i < 5; i++) {
QCheckBox *box = new QCheckBox;
layout->addWidget(box);
checkboxes.append(box);
}
So then you can get acces to your checkBoxes: checkboxes[0]->setChecked(true); or smth
And don't forget to release memory allocated for checkBoxes in destructor of you widget (you don't need to do this if you added them to layout)
for (int i = 0; i < checkboxes.size(); i++) {
delete checkboxes[i];
}

How to set signals for each action in QMenu?

for(auto s :listofPossibleValues){
// item =s;
action = myMenu.addAction("Set Value to "+s);
connect(action,SIGNAL(triggered(bool)),this,SLOT(menuClicked()));
}
void MainWindow::menuClicked(){
value = new QStandardItem(item);
model->setItem(mainindex->row(),mainindex->column(),value);
}
I add actions and connect signals to the slot to my menu using the code above. Previously I was using the item to be the text. But it will only work for last item.
Does anyone at least know how to get the action that I clicked on?
How can I make it work for each individual item rather than just the last one?
Use the triggered signal of QMenu:
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(menuClicked(QAction*)));
Then, in menuClicked():
void MainWindow::menuClicked(QAction *action) {
// do something with action
}

Select a game object and perform actions using GUI.Button (EventTrigger)

I am currently working on a strategy game and I want to preform actions using GUI.Button on game objects. I am using ray cast and mouse click to select the object however when I click on GUI.Button to take out another action the button disappears. I want to use that button to open up another GUI.Box to show some descriptions.
I know why the button is disappearing, it is because I am projecting the ray cast to my button clicks in the update function but how can I avoid this? I also know that I have to use EventTrigger however I am not familiar with javascript event trigger, I searched online but I couldn't find any helpful javascript.
Screenshots:
Here is my script:
#HideInInspector
var isCalled:int = 0;
#HideInInspector
var scWidth:int = 0;
#HideInInspector
var scHeight:int = 0;
function Start () {
scWidth = Screen.width;
scHeight = Screen.height;
}
function Update () {
if (Input.GetMouseButtonDown(0)) {
var ray : Ray = Camera.main.ScreenPointToRay (Input.mousePosition);
var hit : RaycastHit;
if (Physics.Raycast (ray, hit)) {
if (hit.collider.tag == "House") {
isCalled = 1;
} else{
isCalled = 0;
}
}
}
}
function OnGUI(){
if(isCalled==1)
GUI.Button(Rect(scWidth/2,(scHeight/2)+(scHeight/4),120,120), name);
}
}
If I understood you right, the problem is that when you click on button the raycast is fired up before button click and you select different object or no object at all and button disappears or reappears but for another object, what you need to do is check if you clicked on GUI and if yes don't project the raycast that selects the objects. Here is how you are gonna do it.
var selectedUI : GameObject = EventSystem.current.currentSelectedGameObject;
if (Input.GetMouseButtonDown(0) && selectedUI) {
var ray : Ray = Camera.main.ScreenPointToRay (Input.mousePosition);
var hit : RaycastHit;
if (Physics.Raycast (ray, hit)) {
if (hit.collider.tag == "House") {
isCalled = 1;
} else{
isCalled = 0;
}
}
}
I work in C#, so I might have done some syntax errors, but the logic is right.
There is a good trick when using legacy GUI system to avoid raycast when mouse is over GUI elements. Using tooltip to controll if you may cast your ray =)
Something like this (I also don't work with US so may I it needs some work):
var hover : String;
function OnGUI(){
if(GUI.Button (Rect (10,10,100,20), "My Button"));
hover = GUI.tooltip;
function Update () {
if(hover != "") {
// raycast logic
}
}
If you need to avoid raycast when your "popUp" window/panel is shown but you don't want a tooltip on it you may approach using a MouseOverGUI manager too.
It is just a static or singleton holding a boolean that you will set true when your mouse is over some rect that you don't want to cast rays using Rect.Contains(Event.current.mousePosition) and to false whenever it runs out of this rect.
Use this bool with or without tooltip var to allow raycast:
if(!MouseManager.MouseOverGUI) {
// raycast logic
}
Note that creating your rect on each OnGUI cycle will difficult to catch it and control the MouseOveGUI. Also it is a bad practice for performance because OnGUI runs more than once per frame so I'll suggest you to create you rects just once (recalculate it just when needed) and pass it to your GUI elements ;)
I think the easiest way is to create a GameObject(input blocker) which is not visible but have a collider. Place it in between the camera and level objects and deactivate it. When you select a GameObject using Raycast you enable that input blocker GameObject and place it around the area of selected GameObject and GUI. Then you have to ignore the selection if raycast is collided with Input Blocker. When you deselect the Gameobject then you deactivate input blocker
You should also maintain current state of the game for example:
enum State
{
NothingSelected = 0,
GameobjectSelected,
BigMenuOpen
}
State currentState;
So according to state you can define the behavior on mouse click. Like if Full screen menu is Open then raycast does not fire.

How can add action with QWidget.addAction()?

In Qt, I want to add some actions in a widget using QWidget.addAction().
I can easily do it with QToolBar.addAction(), but when I use QWidget.addAction(), it doesn't work.
How can I use QWidget.addAction()?
Here is my function:
void Reb::addActionToBar(QString *tabName, QAction *action)
{
//if tab exist, just add the action, else:
tab_widget->addTab(new QWidget(), *tabName);
for(int i = 0 ; i <= tab_widget->count() ; i++) {
if(tab_widget->tabText(i) == tabName) {
action.setParent(tab_widget->widget(i));
tab_widget->widget(i)->addAction(action);
}
}
}
And as you know tab_widget is a QTabWidget...
I have no error but i can't see my action in tab.
QWidget::addAction() does not do add the action to the UI - the only place where the widget's actions are shown is in the widget's context menu, given the right context menu policy.
QTabWidget has no means to display actions in its UI. Actions are usually displayed in toolbars or menubars, so you would need to add the action there.
As a side note, there is no need to pass QStrings by pointer, simply pass the QString by const reference:
void Reb::addActionToBar(const QString &tabName, QAction *action)
Also, your code has an off-by-one error, use i < tab_widget->count() instead of i <= tab_widget->count() to fix that.

Resources