How to click a button in a out-of-focus widget without changing the current focus to it - qt

I am trying to implement a input method with Qt Embedded.
There is a lookup table for choosing the candidate words for typing. "text input area" to the "lookup table" and the selected word cannot be sent to the "text input area".
Dose anyone have any idea to solve this problem? Thanks~
Here I give a simple example:
main.cpp
#include "InputWidget.h"
#include "ButtonWidget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
InputWidget *inputWidget=new InputWidget();
ButtonWidget *buttonWidget=new ButtonWidget();
inputWidget->show();
buttonWidget->show();
int ref=app.exec();
inputWidget->deleteLater();
buttonWidget->deleteLater();
return ref;
}
InputWidget.h
#include <QWidget>
#include <QPlainTextEdit>
#ifndef _InputWidget_H_
#define _InputWidget_H_
class InputWidget:public QWidget
{
Q_OBJECT
public:
InputWidget(QWidget *parent=0);
private:
QPlainTextEdit *inputArea;
};
#endif
InputWidget.cpp
#include "InputWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
InputWidget::InputWidget(QWidget *parent):QWidget(parent)
{
//input area setup
inputArea=new QPlainTextEdit(this);
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(inputArea);
setLayout(mainLayout);
}
ButtonWidget.h
#include <QWidget>
#include <QPushButton>
#ifndef _ButtonWidget_H_
#define _ButtonWidget_H_
class ButtonWidget:public QWidget
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent=0);
private:
QPushButton *selectedBtn;
public slots:
void changeBtnText();
};
#endif
ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
ButtonWidget::ButtonWidget(QWidget *parent):QWidget(parent)
{
//selectedBtn setup
selectedBtn=new QPushButton(tr("Click Me!!"),this);
connect(selectedBtn,SIGNAL(clicked()),this,SLOT(changeBtnText()));
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(selectedBtn);
setLayout(mainLayout);
}
void
ButtonWidget::changeBtnText()
{
selectedBtn->setText("I am clicked :)");
}
Those codes would generate a widget which has a PlainTextEdit "inputArea" and a widget which has a PushButton "selectedBtn".
First, I input some words in the "inputArea". The current foucs is on "inputArea" in the InputWidget.
But when I move mouse to ButtonWidget and click the "selectedBtn", the foucs is changed to "selectedBtn" in the ButtonWidget.
How do I click the "selectedBtn" but still keep the foucs on "inputArea"? Thanks~
Just like my comment described in laura's answer, InputWidget and ButtonWidget may have no identical parent and I cannot use QWidget's "setFocus" slot to change the current focus between them.

First of all, you will need to make the two widgets know about each other. Once you have done that (by setting the text widget into the button widget or by adding them both to the same parent widget), you can try to see if QWidget's setFocus slot can help you (look at the other slots too, some might be useful for this).
Perhaps implement something like this, in the ButtonWidget:
(in the header) declare a signal foo()
(in the constructor) connect button widget's foo signal to InputWidget's setFocus slot
(in the changeBtnText) after you've done everything you wanted, emit foo()
Note though that setFocus works if the window is active.

You might be able to get what you want by playing with the focusPolicy for your widget. Pay attention to the Qt::NoFocus option. I don't think it prevents mouse clicks on your widget, but you'll want to test to be sure.

Thank laura and cjhuitt, your responses give me a big hint to solve my question.
Because InputWidget and ButtonWidget are two independent widgets, if we want to deal with the focus issue between them, we need a "global" control, i.e., use QApplication to do the focus job.
The key point is using QApplication's "focusChanged" slot and QWidget's "activateWindow". The following is my modification.
main.cpp
#include "InputWidget.h"
#include "ButtonWidget.h"
#include <QApplication>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
InputWidget *inputWidget=new InputWidget();
ButtonWidget *buttonWidget=new ButtonWidget(0,&app);
inputWidget->show();
buttonWidget->show();
int ref=app.exec();
inputWidget->deleteLater();
buttonWidget->deleteLater();
return ref;
}
ButtonWidget.h
#include <QWidget>
#include <QPushButton>
#include <QApplication>
#ifndef _ButtonWidget_H_
#define _ButtonWidget_H_
class ButtonWidget:public QWidget
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent=0,QApplication *app=0);
private:
QPushButton *selectedBtn;
public slots:
void changeBtnText();
void changeFocus(QWidget *oldWidget,QWidget *curWidget);
};
#endif
ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
ButtonWidget::ButtonWidget(QWidget *parent,QApplication *app):QWidget(parent)
{
//selectedBtn setup
selectedBtn=new QPushButton(tr("Click Me!!"),this);
connect(selectedBtn,SIGNAL(clicked()),this,SLOT(changeBtnText()));
//main layout
QVBoxLayout *mainLayout=new QVBoxLayout(this);
mainLayout->setContentsMargins(1,4,1,1);
mainLayout->addWidget(selectedBtn);
setLayout(mainLayout);
//deal with focus
connect(app,SIGNAL(focusChanged(QWidget*,QWidget*)),this,SLOT(changeFocus(QWidget*,QWidget*)));
}
void
ButtonWidget::changeBtnText()
{
selectedBtn->setText("I am clicked :)");
}
void
ButtonWidget::changeFocus(QWidget *oldWidget,QWidget *curWidget)
{
if(oldWidget && curWidget==this)
oldWidget->activateWindow();
}
InputWidget.cpp and InputWidget are not modified.
This solution can work in this example, but I am not sure that it can work in two independent Qt programs(they have their own QApplication), especially in Qt Embedded environment. I would continue doing some tests on it. If there are any results, I would also post them here.
Thank for this discussion, I have learned a lot and thank you all again!!

Related

Slot is not detected when QPushbutton is released [duplicate]

This question already has answers here:
QObject connection function
(3 answers)
Closed 4 years ago.
I am currently making a main menu for a game, but when the button is clicked it does not seem to call the relevant slot (The button does not do anything). I have tried to move the button to a thread to ensure that nothing is keeping it from running immediately. I have written a simpler program where the button works, so it would seem the problem is not with the button or signal itself. Therefore it must be something keeping it from running?
The source file is given as:
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QObject>
#include <QBrush>
#include"game.h"
#include <QMediaPlayer>
#include <QImage>
#include<QGraphicsPixmapItem>
#include <QPixmap>
#include<QGraphicsItem>
#include <QPushButton>
#include <QLineEdit>
#include <QPalette>
#include <QMessageBox>
#include <QLabel>
#include <QComboBox>
Game::Game(QWidget*parent)
{
//adding the background
QGraphicsScene *scene=new QGraphicsScene();//to create a new graphics scene
scene->setSceneRect( 0,0,1366,768);
setBackgroundBrush(QBrush(QImage(":/images/Back_1_withlogo.jpg")));
setScene(scene);
show();
setFixedSize(1366,768);
//adding logo
//setPixmap(QPixmap(":/images/Logo.png"));
//adding the soundtrack
music=new QMediaPlayer();
music->moveToThread(&MusicThread);
music->setMedia(QUrl("qrc:/sounds/Surreal-Game-Menu.mp3"));
music->play();
MusicThread.start();
//add GUI
this->setFixedSize(1366,768);
this->move(-7,-5);
//start button and username edit box
btnStart = new QPushButton(this);
btnStart->moveToThread(&btnThread);
UserName = new QLineEdit(this);
UserName->setFixedSize(100,25);
UserName->move(300,300);
UserName->show();
UserName->setMaxLength(12);
btnStart->setText("Start");
btnStart->setFixedSize(100,30);
btnStart->show();
btnStart->move(300,600);
connect(btnStart, SIGNAL (released()),this, SLOT (btnStart_clicked()));
btnThread.start();
//label for username edit box
QLabel *LblUsername= new QLabel(this);
LblUsername->setStyleSheet("QLabel { color : white; }");
LblUsername->setFixedSize(100,30);
LblUsername->move(230,300);
LblUsername->setText("Username:");
LblUsername->show();
QLabel *lblGameMode = new QLabel(this);
lblGameMode->setStyleSheet("QLabel { color : white; }");
lblGameMode->setFixedSize(100,30);
lblGameMode->move(190,450);
lblGameMode->setText("Select Game mode:");
lblGameMode->show();
//combobox to select players
GameMode=new QComboBox(this);
GameMode->move(300,450);
GameMode->setFixedSize(100,30);
QStringList Modelist=(QStringList()<<"singleplayer"<<"co-op"<<"multiplayer (3 players)");
GameMode->addItems(Modelist);
GameMode->show();
}
void Game::btnStart_clicked()
{
if (UserName->text()==NULL)
{
QMessageBox messageBox;
messageBox.critical(0,"Error","Please insert username!");
messageBox.setFixedSize(500,200);
}
else
{
numPlayers = GameMode->currentIndex();
SUsername= UserName->text();
UserName->setText("works");
}
}
And the header code is given as:
#ifndef GAME_H
#define GAME_H
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QWidget>
#include <QPushButton>
#include <QLineEdit>
#include <QMediaPlayer>
#include <QThread>
#include <QComboBox>
class Game:public QGraphicsView
{
public:
Game(QWidget*parent=0);
QGraphicsScene*scene;
private slots:
void btnStart_clicked();
private:
QPushButton *btnStart;
QLineEdit *UserName;
QMediaPlayer *music;
QThread MusicThread;
QString SUsername;
QThread btnThread;
QComboBox *GameMode;
int numPlayers;
};
#endif // GAME_H
I received the following error:
QObject::connect: No such slot QGraphicsView::btnStart_clicked() in game.cpp:57
add a Q_OBJECT macro as shown below:
class Game:public QGraphicsView
{
Q_OBJECT
public:
Game(QWidget*parent=0);
QGraphicsScene*scene;
private slots:
void btnStart_clicked();
private:
QPushButton *btnStart;
QLineEdit *UserName;
QMediaPlayer *music;
QThread MusicThread;
QString SUsername;
QThread btnThread;
QComboBox *GameMode;
int numPlayers;
};
This macro helps compiler to determine that the class is using signals and slots mechanism. Basically it creates a entry of defined signals and slots in MOC (meta object compiler) file.
Go through below link for info:
http://www.bogotobogo.com/Qt/Qt5_Q_OBJECT_Macro_Meta_Object.php
QObject::connect (btnStart, &QPushButton::clicked, this, &Game::btnStart_clicked); might work... if you want to use the release event try overriding virtual void mouseRelease or the eventFilter.
And of course... the Q_OBJECT marco.

Dynamically adding widgets to gridLayout Qt

I am having a bit of difficulty with some code. I am super rather new to Qt so it is entirely possible that I am simply ignorant to the problem I am having.
Basically, I am blocking out a program so that I can add the specifics of it later. I want to be able to create a grid of buttons, and when one of those buttons is pressed, another shape to replace it.
I am able to make my button grid, have it be scrollable, and have the button call it its position on the grid when pressed. However, when I try and use those coordinates to add another button to the grid, Qt crashes.
Here's my code so far:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <cmath>
#include <QLabel>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QApplication>
#include <QPushButton>
#include <QScrollArea>
#include <QDebug>
#include <QString>
#include <QSignalMapper>
#include <QStringList>
#include <QLayoutItem>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
populateViewGrid(); //I wanted to see if I could add in a scrollbar
//from outside the main window. Could this be causing
// the issue?
}
void MainWindow::populateViewGrid()
{
QScrollArea*scrollArea = new QScrollArea(this);
QWidget*central = new QWidget(this);
QGridLayout*gridLayout = new QGridLayout(central);
QSignalMapper *signalMapper = new QSignalMapper(central);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
QString position= QString("%1,%2").arg(i).arg(j);
QPushButton* button = new QPushButton("addTrack",central);
gridLayout->addWidget(button, i, j);
connect(button, SIGNAL(clicked()),signalMapper, SLOT(map()));
signalMapper->setMapping(button, position);
}
}
connect(signalMapper, SIGNAL(mapped(QString)),this, SLOT(addTrack(QString )));
central->setLayout(gridLayout);
scrollArea->setWidget(central);
setCentralWidget(scrollArea);
}
void MainWindow::addTrack(QString position)
{
QStringList query = position.split(",");
int x;
x=query.at(0).toInt();
int y;
y=query.at(1).toInt() ;
QPushButton *Ifthisworks=new QPushButton(this);
//This first line is where is crashes. I know this due to having the code
//laced with qDebugs. From all of my google searches and such, it seems that
// something simple should be wrong and I can't find it.
QLayoutItem * existingitem = gridLayout->itemAtPosition(x, y);
if(existingitem) {
gridLayout->removeItem(existingitem);
delete existingitem;
}
// before I included the above to remove the button from the grid point, the
//program would crash here.
gridLayout->addWidget(Ifthisworks, x, y);
}
MainWindow::~MainWindow()
{
delete ui;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <cmath>
#include <QLabel>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QApplication>
#include <QPushButton>
#include <QMainWindow>
#include <QScrollArea>
#include <QSignalMapper>
#include <QHash>
//unrelated question, do I need the above in my code? I know not all of them
//used, but do I need includes in this file as well?
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void populateViewGrid();
QGridLayout *gridLayout;
public slots:
void addTrack(QString);
private:
QScrollArea*scrollArea;
QWidget * central;
QPushButton *Ifthisworks;
QSignalMapper *signalMapper;
QPushButton *clockViews;
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
If you could help me understand how to not make Qt crash there and also add a button, that would be fantastic =)
So some background real quick incase you are looking at my code and are scratching your head. I'm a mechanical engineer who should have probably been an electrical or computer engineer and knows enough about coding to get myself into these kinds of messes. For the most part, I searched for what I wanted Qt to do and hacked it all together to hopefully make it work. Any bit of deeper understanding you can share would be more than welcome.
Thank you for your time.
You are initializing a local variable called gridLayout in your MainWindow::populateViewGrid() method:
QGridLayout*gridLayout = new QGridLayout(central);
Then in your MainWindow::addTrack(QString position) method, you are trying to access the member variable called gridLayout which is never initialized.
To fix this, simply initialize the member variable instead of creating a local variable in your MainWindow::populateViewGrid() method:
gridLayout = new QGridLayout(central);
You are doing the same mistake with your other member variables as well. Fix them the same way.
I would go for a different implementation.
Move gridlayout, signalmapper,... to be class members. I personally like also to keep a list of my widgets QList<QPushButton*> for manually deleting or keeping a button cache.
Make the signalmapper to map to index in list or QWidget*. For the example i`ll go with the QWidget* pointer.
QPushButton *newButton = new QPushButton("The new Thing");
QPushButton *oldButton = static_cast<QPushButton*>(widgetPointer);
gridLayout->replaceWidget(oldButton ,newButton);
buttonList->takeAt(buttonList->indexOf(oldButton))->deleteLater()); //if you keep a list..
buttonList->insert(buttonList->indexOf(oldButton),newButton);

QToolButton should have another icon than the default action

When using a QToolButton and the setDefaultAction(myAction) method, it will get the default actions properties
Reference:
If a tool button has a default action, the action defines the button's
properties like text, icon, tool tip, etc.
So I try to overwrite the icon, calling setIcon on the QToolButton.
myAction.setIcon(...);
myToolButton->setDefaultAction(myAction);
myToolButton->setIcon(...);
But it myToolButton still has the Icon of myAction.
Is there a way to hold the default action but overwrite it's icon, only for the QToolButton, not for the action itself? In otherwords: How can the QToolButtons have different properties than its default action?
If you take a look at the source for QToolButton::setDefaultAction(QAction* action), it uses setIcon(action->icon()) to set the icon from the action you give it. You can override that icon manually by calling setIcon with your desired separate icon after you call setDefaultAction.
Edit:
Here's a quick example you can try. Maybe you can compare it to what you're currently doing or post an example of your own?
MainWindow.cpp
#include "MainWindow.hpp"
#include <QAction>
#include <QToolButton>
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent) {
QAction* action = new QAction(QIcon(QStringLiteral("original.jpg")), QStringLiteral("Test Action"), this);
QToolButton* toolButton = new QToolButton(this);
toolButton->setDefaultAction(action);
toolButton->setIcon(QIcon(QStringLiteral("new.jpg")));
}
MainWindow.hpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QWidget>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
};
#endif // MAINWINDOW_H
main.cpp
#include "MainWindow.hpp"
#include <QApplication>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
QToolButton icon will reset to action icon when action changed.
We need re-override code like this;
connect(toolButton, &QAction::changed, [=](){
toolButton->setIcon(QIcon(QStringLiteral("new.jpg")));
});

How to hide a QDialog (not showing GUI) on exec()

I want to exec() a QDialog without GUI for my test - there's no need to show GUI.
In the main window, when I exec() a QDialog, it shows its GUI window. I connect a signal from thread in QDialog to a slot in QDialog, so QDialog.exec() is needed. QDialog.hide() is not working.
Is there any solution?
You should never exec() a dialog. This can lead to subtle bugs, because the exec() call can reenter code you don't plan on being reentered.
If you really insist on using exec(), then the simple solution is to post a CloseEvent to the dialog. The event will be processed once the dialog's event loop starts running.
MyDialog dialog;
QCoreApplication::postEvent(&dialog, new QCloseEvent());
dialog.exec();
This code is functionally equivalent to the following:
MyDialog dialog;
QMetaObject::invokeMethod(&dialog, "close", Qt::QueuedConnection);
dialog.exec();
An alternative, much safer way to accomplish this is not to use exec. Simply connect the dialog's accepted() and rejected() signals to slots in your code - after all, the dialog's acceptance or rejection happens asynchronously.
It's then easy enough simply not to show() the dialog in the test code path. You can also easily simulate the dialog being accepted or rejected by invoking either accept or reject slots.
I really don't understand exactly how you "connected" signals from a thread to dialog's slots, and so on. Feel free to paste the example below into your question, modify it to do the threaded "stuff", and show how it doesn't work. It'd also help to see how you implement the test harness.
The example below is all a single file, it'd help if you would keep it that way when producing a test case that illustrates your problem.
// main.cpp
#include <QApplication>
#include <QDialog>
#include <QLabel>
#include <QGridLayout>
#include <QPushButton>
#include <QDialogButtonBox>
class MyDialog : public QDialog {
QGridLayout m_layout;
QDialogButtonBox m_box;
public:
MyDialog(QWidget * parent = 0) : QDialog(parent), m_layout(this),
m_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
{
m_layout.addWidget(&m_box);
connect(&m_box, SIGNAL(accepted()), SLOT(accept()));
connect(&m_box, SIGNAL(rejected()), SLOT(reject()));
}
};
class MyGui : public QWidget {
Q_OBJECT
QGridLayout m_layout;
QLabel m_label;
QPushButton m_button;
MyDialog m_dialog;
Q_SLOT void on_button_clicked() {
m_dialog.show();
}
Q_SLOT void on_dialog_accepted() {
m_label.setText("The dialog was accepted");
}
Q_SLOT void on_dialog_rejected() {
m_label.setText("The dialog was rejected");
}
public:
MyGui() : m_layout(this), m_button("Show Dialog"), m_dialog(this) {
m_button.setObjectName("button");
m_dialog.setObjectName("dialog");
m_layout.addWidget(&m_label);
m_layout.addWidget(&m_button);
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyGui gui;
gui.show();
return app.exec();
}
#include "main.moc"

SLOT rejected(), accepted() in QMainWindow

I am a newbie in Qt-programming. I have read a book about GUI-programming with Qt. I have an trouble in creating a dialog. Here is sample code:
// gotocell.h
#ifndef GOTOCELL_H
#define GOTOCELL_H
#include <QDialog>
#include <QtWidgets>
#include "ui_gotocell.h"
class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
{
Q_OBJECT
public:
GoToCellDialog (QWidget *parent = 0);
private slots:
void on_lineEdit_textChanged();
};
#endif // GOTOCELL_H
// gotocell.cpp
#include <QtWidgets>
#include "gotocell.h"
#include <QtWidgets>
GoToCellDialog::GoToCellDialog (QWidget *parent):
QDialog (parent)
{
setupUi(this);
QRegExp regExp ("[A-Za-z][1-9][0-9]{0,2}");
lineEdit->setValidator(new QRegExpValidator(regExp, this));
connect (okButton, SIGNAL(clicked()), this, SLOT(accept()));
connect (cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
}
void GoToCellDialog::on_lineEdit_textChanged()
{
okButton->setEnabled(lineEdit->hasAcceptableInput());
}
// main.cpp
#include "gotocell.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GoToCellDialog *dialog = new GoToCellDialog;
dialog->show();
return a.exec();
}
but when I compiled, there is an error: no known conversion for argument 1 from 'GoToCellDialog* const' to 'QMainWindow*'at setupUi() function. I think because the designer in Qt Creator created a QMainWindow, not a QDialog. So I changed GoToCellDialog class to QMainWindow. But there is no slots whose name is "accepted", "rejected" in QMainWindow. Can anyone help me?
If you want to display a Dialog as main window you have two choices:
1. make the whole main window QDialog based
2. design the Dialog separately and set it as the main windows central Widget (QMainWindow->setCentralWidget()).
In both cases you still have the problem what semantics you give to the OK and Cancel buttons. Generally it may be a better idea to consider what the main window of the application should contain, and design the dialogs later.

Resources