How to enable a dynamic whatsthis-string in Qt using QWhatsThis? - c++

In order to have a dynamic what's this string for a widget in Qt the following almost works (following the documentation http://doc.qt.io/qt-5/qtwidgets-widgets-tooltips-example.html ):
class MyEdit : public QLineEdit {
Q_OBJECT
public:
bool event(QEvent*e) {
if (e && e->type() == QEvent::WhatsThis) {
if (QHelpEvent *helpEvent = reinterpret_cast<QHelpEvent *>(e)) {
QWhatsThis::showText(helpEvent->globalPos(), "My text...");
return true;
}
}
return QLineEdit::event(e);
}
};
Activating what's this for the window and clicking on the widget shows "My text" (the actual text is more complicated).
Issues:
Activating what's this for the window and hovering over this widget shows a dead cursor
Shift-F1 does not work inside the widget.
The first issue could be worked around by calling setWhatsThis("Dummy text"); with a non-empty string, but it feels like a hack and Shift-F1 in this widget shows "Dummy text".
Is there a non-hack way of handling it - especially so that it will not be broken by updates?

In case someone else stumbles on this issue the following seems to handle the Shift-F1:
class MyEdit : public QLineEdit {
Q_OBJECT
public:
bool event(QEvent*e) {
if (e && e->type() == QEvent::WhatsThis) {
if (QHelpEvent *helpEvent = reinterpret_cast<QHelpEvent *>(e)) {
QWhatsThis::showText(helpEvent->globalPos(), "My text...");
return true;
}
}
if (e && e->type() == QEvent::KeyPress) {
if (QKeyEvent *qk = reinterpret_cast<QKeyEvent *>(e)) {
if (qk->key() == Qt::Key_F1 && (qk->modifiers()&Qt::ShiftModifier)) {
QWhatsThis::showText(w.mapToGlobal(w.inputMethodQuery(Qt::ImCursorRectangle).toRect().center()), "My text...");
qk->accept();
return true;
}
}
}
return QLineEdit::event(e);
}
};
and combined with calling setWhatsThis("Dummy text"); also the hovering-part. However, it is not an elegant solution.

Related

QT c++: drop in multi line edite

Im beginner in qt and i do my first project. I am encountering a problem.
I put somes edit line in a scroll area. All of this edit text should countains path to files. To make this app more userfriendly i decided to implement a drag and drop. By this way, users can just take a file from their explorer and drop it to line edit which will be fill with the path of the file.
My problem is: When i try to drop, all edit line where my mouse passed on, will be fill with the path of the file. If i change if statements by else if, its the first edit line that my mouse passed on which will be fill but not the one where my mouse is at the moment of the drop.
here the code:
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
e->accept()
}
void MainWindow::dropEvent(QDropEvent *e)
{
foreach (const QUrl &url, e->mimeData()->urls()) {
QString fileName = StringManagement::getDir(url.toLocalFile());
if(ui->lineEdit->underMouse())
ui->lineEdit->setText(fileName);
if(ui->lineEdit_2->underMouse())
ui->lineEdit_2->setText(fileName);
if(ui->lineEdit_5->underMouse())
ui->lineEdit_5->setText(fileName);
if(ui->lineEdit_9->underMouse())
ui->lineEdit_9->setText(fileName);
if(ui->lineEdit_10->underMouse())
ui->lineEdit_10->setText(fileName);
if(ui->lineEdit_11->underMouse())
ui->lineEdit_11->setText(fileName);
}
}
On other point that i dont really understand is:
<pre><code>void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
qInfo() << "enter";
e->accept();
}
void MainWindow::dragLeaveEvent(QDragLeaveEvent *e){
qInfo() << "leave";
e->accept();
}
when i put my mouse on an edit line and i stay on it, i will see both message in the console... i expected to see the first one when my mouse enter in and the second one when my mouse leave it.
thank you in advance for your helps.
Following your answer to my comment I'll try to help you.
I'm not an expert at Qt so I may be wrong but since there is no answer yet I'll try to give one.
I tried to reproduce your code and for the second question:
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
qInfo() << "enter";
e->accept();
}
void MainWindow::dragLeaveEvent(QDragLeaveEvent *e)
{
qInfo() << "leave";
e->accept();
}
I have the same behaviour if MainWindow and the lineEdits both manage drag and drop (setAcceptDrops(true)). I think that you "enter" when you enter the MainWindow and then "leave" when you enter the lineEdit since it manages itself the drag and drop.
If you set :
ui->lineEdit->setAcceptDrops(false);
Then you don't "leave" anymore.
For the first part
If I try to reproduce your code, I have a problem with the underMouse() function.
Maybe your problem comes from here?
If I implement my own underMouse() then everything is fine.
I hope someone else with better Qt knowledge will come to your help.
Ok i find a solution. I dont really like it because i dont find it really clean but that works. If others have cleaner solution im open to it. I put my solution here. Maybe that could help someone in the future. I finaly didn't use drop methode but i used an eventFilter which give me the posibility to have a better management of events.
<pre><code>MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
this->data = DataModel::GetInstance();
ui->setupUi(this);
setAcceptDrops(true);
//ui->lineEdit->dragEnabled();
//ui->lineEdit->setAcceptDrops();
installEventFilter(this);
ui->lineEdit->installEventFilter(this);
ui->lineEdit_2->installEventFilter(this);
ui->lineEdit_5->installEventFilter(this);
ui->lineEdit_9->installEventFilter(this);
ui->lineEdit_10->installEventFilter(this);
ui->lineEdit_11->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
qInfo() << "enter";
e->accept();
}
bool MainWindow::eventFilter(QObject* obj, QEvent* event){
if(event->type() == QEvent::DragEnter){
if(obj == ui->lineEdit){
this->flag = 1;
}
else if(obj == ui->lineEdit_2){
this->flag = 2;
}
else if(obj == ui->lineEdit_5){
this->flag = 3;
}
else if(obj == ui->lineEdit_9){
this->flag = 4;
}
else if(obj == ui->lineEdit_10){
this->flag = 5;
}
else if(obj == ui->lineEdit_11){
this->flag = 6;
}
qInfo()<<"flag" <<this->flag;
}
if(event->type() == QEvent::Drop){
qInfo()<< obj;
QDropEvent *drop = static_cast<QDropEvent *>(event);
foreach (const QUrl &url, drop->mimeData()->urls()) {
QString fileName = StringManagement::getDir(url.toLocalFile());
qInfo()<<"flag drop" <<this->flag;
if(this->flag == 1){
ui->lineEdit->setText(fileName);
}
else if(this->flag == 2){
ui->lineEdit_2->setText(fileName);
}
else if(this->flag == 3){
ui->lineEdit_5->setText(fileName);
}
else if(this->flag == 4){
ui->lineEdit_9->setText(fileName);
}
else if(this->flag == 5){
ui->lineEdit_10->setText(fileName);
}
else if(this->flag == 6){
ui->lineEdit_11->setText(fileName);
}
return true;
}
}
}
</code></pre>
I dont manage return in the filter for now but the idea is here.

Checkbox change other checkbox statement

I need to connect some Checkboxes, so when I click one it becomes checked and other become unchecked. My code right now looks like this.
Connect in class constructor:
connect(cb_thickness1,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness1_isChecked()));
connect(cb_thickness2,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness2_isChecked()));
connect(cb_thickness3,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness3_isChecked()));
and slots
void MainWind::cb_thickness1_isChecked()
{
if(cb_thickness2->isChecked())
cb_thickness2->setChecked(false);
if(cb_thickness3->isChecked())
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness2_isChecked()
{
if(cb_thickness1->isChecked())
cb_thickness1->setChecked(false);
if(cb_thickness3->isChecked())
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness3_isChecked()
{
if(cb_thickness1->isChecked())
cb_thickness1->setChecked(false);
if(cb_thickness2->isChecked())
cb_thickness2->setChecked(false);
}
Code doesn't work as expected. When I click to any ChBx first time, everything is OK, but when I click to other next time it only uncheck previous and does nothing with itself. Only on second click it become chekced.
Also I found one more bug, when I check to ChBox, and then uncheck it by clicking it againg, I can check 2 ChBxes. [pic 2]
Radio button is a great idea.
But if you really want to use check box, you can explicitly set cb_thickness1 checked in cb_thickness1_isChecked(), and do the same for other two check boxes.
void MainWind::cb_thickness1_isChecked()
{
cb_thickness1->setChecked(true);
cb_thickness2->setChecked(false);
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness2_isChecked()
{
cb_thickness1->setChecked(false);
cb_thickness2->setChecked(true);
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness3_isChecked()
{
cb_thickness1->setChecked(false);
cb_thickness2->setChecked(false);
cb_thickness3->setChecked(true);
}
I suggest to derive a class from QCheckBox (lets call it CustomCheckBox) and add a signal, private slot and public slot
signal:
void enabled();
private slot:
void CustomCheckBox::checkEnable(bool state)
{
if(state)
{
emit enabled();
}
}
public slot:
void CustomCheckBox::uncheck()
{
setChecked(false);
}
In the constructor add:
connect(this,SIGNAL(toggled(bool)),this,SLOT(checkEnable(bool)));
This way you can use simple connects.
CustomCheckBox *box1 = new CustomCheckBox();
CustomCheckBox *box2 = new CustomCheckBox();
connect(box1,SIGNAL(enabled()),box2,SLOT(uncheck()));
Feel free to improve this answer. :)
Verify that the state of the button is checked in the slot and then deactivate the other checkboxes like you already did. You can use the parameter of the stateChanged method by passing it to the slots.
Here is code that works:
Variant I:
connect(ui->checkBoxA, SIGNAL(stateChanged(int)), this, SLOT(checkBoxAChanged(int)));
connect(ui->checkBoxB, SIGNAL(stateChanged(int)), this, SLOT(checkBoxBChanged(int)));
connect(ui->checkBoxC, SIGNAL(stateChanged(int)), this, SLOT(checkBoxCChanged(int)));
void MainWindow::checkBoxAChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxB->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxBChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxA->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxCChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxB->setChecked(false);
ui->checkBoxA->setChecked(false);
}
}
Variant II:
connect(ui->checkBoxA, SIGNAL(clicked(bool)), this, SLOT(checkBoxAClicked(bool)));
connect(ui->checkBoxB, SIGNAL(clicked(bool)), this, SLOT(checkBoxBClicked(bool)));
connect(ui->checkBoxC, SIGNAL(clicked(bool)), this, SLOT(checkBoxCClicked(bool)));
void MainWindow::checkBoxAClicked(bool val)
{
if (val == true) {
ui->checkBoxB->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxBClicked(bool val)
{
if (val == true) {
ui->checkBoxA->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxCClicked(bool val)
{
if (val == true) {
ui->checkBoxB->setChecked(false);
ui->checkBoxA->setChecked(false);
}
}

Qt How to highlight menu? [duplicate]

This question already has answers here:
How to focus menuBar() with Qt
(2 answers)
Closed 4 years ago.
I need to do hide/show main menu function by pressing the Alt key (like in FireFox). I know how to hide and show it, but I can't highlight menu after showing it. I've tried menuBar()->actions()[0].hover(), activateWindow(), setActiveAction(), activate(QAction::Hover), but nothing helps. How can I do it? Maybe I should use WinApi functions instead of Qt?
bool MainWindow::event(QEvent *event){
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *ke = static_cast<QKeyEvent*>(event);
if (ke->key() == Qt::Key_Alt)
{
keyReleaseEvent(ke);
return true;
}
}
return QMainWindow::event(event);
}
Handle Alt
void MainWindow::keyReleaseEvent (QKeyEvent* event)
{
if (event->key() == Qt::Key_Alt){
if (menuBar()->isHidden()){
menuBar()->show();
menuBar()->setFocus(Qt::MenuBarFocusEvent); //here I trying to highlight the menu
}
else{
menuBar()->hide();
{
}
}
Duplicate of How to focus menuBar() with Qt (which I just answered)
Because the answer isn't accepted on the other question, I can't flag this one as a duplicate. Including answer below as well:
I wanted to do the same thing. My solution, complete example, as a gist:
https://gist.github.com/xim/ee56564f425151ea2fa70f730d644873
As it contains a lot of other junk, a minimal example:
class AutoHidingMenuBar : public QMenuBar {
Q_OBJECT
public:
AutoHidingMenuBar() : QMenuBar() {
setMaximumHeight(0);
connect(qApp, &QApplication::focusChanged, this, &AutoHidingMenuBar::focusChanged);
}
private slots:
void focusChanged(QWidget *from, QWidget *to) {
bool inFocus = hasFocus() || isAncestorOf(focus) || hasFocusedChild();
if (inFocus && maximumHeight() == 0) {
auto action = activeAction();
setMaximumHeight(100);
if (action) {
// XXX This is a bit of a hack. We could do
// QCoreApplication::processEvents();
// setActiveAction(action);
// with almost the same effect, but then we *open* the first menu on single alt press...
auto evt = new QMouseEvent(QEvent::MouseMove, actionGeometry(action).center(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::postEvent(this, evt);
}
} else if (!inFocus && maximumHeight() != 0)) {
setMaximumHeight(0);
}
}
private:
bool hasFocusedChild();
};
Try to debug it and see what is happening.
You can do something like:
if (menuBar()->hasFocus())
qDebug() << " I have the focus";
else
qDebug() << " I don't have the focus";
And see if it has the focus but works bad or if it has no focus which I think that is problem... somewhere it is losing it
A little trick that may help you is using:
QTimer::singleShot(0, menuBar(), SLOT(setFocus()));
on your MainWindow::keyReleaseEvent
And last thing... Note that set focus needs a focusPolicy, maybe this helps you.

How to check two QLineEdit If they are not empty

I have three controls, two QTextLine and one QPushButton.
When startup the program, the add button will be disabled, and must be two QTextLine not empty for enable the add button.
I have the following code, but it does not works fine:
void Question_Answer::on_newQuestion_txt_textChanged(const QString &arg1)
{
if(arg1.isEmpty())
{
ui->addNewQuestion_btn->setEnabled(false);
}
else
{
ui->addNewQuestion_btn->setEnabled(true);
}
}
void Question_Answer::on_newAnswer_txt_textChanged(const QString &arg1)
{
if(ui->newAnswer_txt->text().isEmpty())
{
ui->addNewQuestion_btn->setEnabled(false);
}
else
{
ui->addNewQuestion_btn->setEnabled(true);
}
}
Now, How to check if the two QTextLine are not empty, and also if any of them is empty, how to disable the add button.
Just connect a single slot to handle textChanged signals of both LineEdits
void Question_Answer::onTextChanged(const QString &arg1){
if(ui->newAnswer_txt->text().isEmpty() || ui->newQuestion_txt->text().isEmpty()){
ui->addNewQuestion_btn->setEnabled(false);
}else{
ui->addNewQuestion_btn->setEnabled(true);
}
}
In the header class:
// ...
private slots:
void onTextChanged();
// ...
In the source file:
// Setup the connections in the constructor.
void Question_Answer::onTextChanged()
{
const bool editable1 = ui->newAnswer_txt->text().size() > 0;
const bool editable2 = ui->newQuestion_txt->text().size() > 0;
ui->addNewQuestion_btn->setEnabled( editable1 && editable2 );
}

Quit application call twice the closeevent

I have wrote an application in Qt/c++ on OSX. When quitting the app, I'm catching the closeevent to display dialog box
void MainUI::closeEvent (QCloseEvent *event)
{
if( DeviceUnplugged == false) {
ExitDialog = new DialogExit;
ExitDialog->exec();
if(ExitDialog->result() == QDialog::Accepted) {
m_device.CloseDevice();
event->accept();
}
else {
event->ignore();
}
}
}
The dialog box is correctly displayed when closing using the red cross or using the menu "quit".
but when I'm closing the app using the right click on the icon in the dock, the dialog box appears twice the close event is called twice.
Any idea why ?
Yes, I think it is normal for Mac, at least I had this in my Qt application, too (only on Mac).
I used the following workaround:
void MainUI::closeEvent (QCloseEvent *event)
{
if (m_closing)
{
event->accept();
return;
}
if( DeviceUnplugged == false) {
ExitDialog = new DialogExit;
ExitDialog->exec();
if(ExitDialog->result() == QDialog::Accepted) {
m_device.CloseDevice();
m_closing = true;
event->accept();
}
else {
event->ignore();
}
}
}
By default, boolean variable m_closing should be initialized by false of course in your class. This way second time nothing will be done (processing will be skipped). This worked for me.
Looks like this is a QT bug:
See: https://bugreports.qt.io/browse/QTBUG-43344
Also had this problem when using qt-5.6_4 ,
In my case it happened when using CMD+Q but didn't happen when using the red x button.
Used a similar patch.
I avoided accept or ignore since this is a bug and I don't think we should "talk to it" :-)
Instead I simply return when called more then once.
static int numCalled = 0;
if (numCalled++ >= 1)
return;