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.
Related
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.
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.
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);
}
}
I am trying to "cancel" key codes in QTextEdit or QPlainTextEdit. When I say cancel, what I mean is, I want to turn the "entered" character into "nothing" depending on the key entered. Example: if the user hits "a" or "b" on the keyboard, I would not want to have "a" or "b" displayed / entered into the text, instead, the input would be ignored and turned into nothing / won't be processed.
With C++ Builder, you have a KeyDown_Event and a "Key" parameter. Once you detect the entered key code, if you don't like it, you can set the "Key" parameter to 0, so you set "Key = 0" and the key stroke would not be displayed. How do I achieve the same thing in Qt?
Let me explain with code:
if (e->key() == 67)
// do not send "c" to the QTextEdit (In C++ Bullder, you would do Key = 0)
if (e->key() == 65)
// do not send "a" to the QTextEdit (In C++ Bullder, you would do Key = 0)
How do I do this in Qt?
I tired doing e->setAccepted(false) and e->Ignore() but it made no difference. I think by the time e->ignore() is executed, the "char" is already inserted into the text box. With C++ Builder, you can intercept this with the KeyDown event and cancel it. I can't seem to find a way with Qt.
Thx
Similar to void QObject::installEventFilter ( QObject * filterObj ) example:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
textEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched == textEdit && event->type() == QEvent::KeyPress) {
QKeyEvent *e = static_cast < QKeyEvent * >(event);
if (e->key() == Qt::Key_A) {
return true;
}
}
return QMainWindow::eventFilter(watched, event);
}
UPDATE
As IInspectable noticed, this won't help you with filtering Ctrl+C/Ctrl+V method. If you need these either, you'll need to connect to QTextEdit::textChanged signal and updated the text manually. Something like this:
static QString oldString;
QString s = textEdit->toPlainText();
if (s == oldString)
return;
int pos = textEdit->textCursor().position();
s.remove('a', Qt::CaseInsensitive);
oldString = s;
textEdit.setPlainText(s);
QTextCursor cursor = textEdit->textCursor();
cursor.setPosition(pos);
textEdit->setTextCursor(cursor);
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;