How to gray out a menu item again in Qt - c++

I am working with qt and have created a menu bar like "File" with some sub menu items "Open", "Save", "SaveAs", "Close", "Quit". I also created actions like "actionNew", "actionOpen", ... and so on. I used the same actions for the tool bar and disable the menu and tool bar items into the constructor with the "disableItems()" function, that works fine and the tool bar and menu items are gray out. If I clicked the sub menu item "New", the tool bar and menu items are enabled and not gray out, that also works fine, but if I clicked the sub menu item "Close" only the tool bar items gray out and the menu items are still enabled :(. How can i fix it? Hope you can help me and sorry for my bad English ;)
MainWindow::MainWindow(QWidget *parent)
:QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* disable menu items and tool bar items */
disableItems();
}
void MainWindow::disableItems()
{
ui->actionSave->setEnabled(false);
ui->actionSaveAs->setEnabled(false);
ui->actionClose->setEnabled(false);
}
void MainWindow::enableItems()
{
ui->actionSave->setEnabled(true);
ui->actionSaveAs->setEnabled(true);
ui->actionClose->setEnabled(true);
}
void MainWindow::on_actionNew_triggered()
{
enableItems();
}
void MainWindow::on_actionClose_triggered()
{
disableItems();
}

I had the same problem.
I was puzzled because the code behaved differently from Linux 64 (where usually I develop), Windows (customer machine), Linux 32 (old development & backup machine). Then I realized the difference in version.
The only way to solve I found was to upgrade from default QtSDK that came with my Ubuntu distribution to the latest downloaded from here.
I suggest to check if the version you are using can be upgraded.
HTH
edit I noticed that they changed something more radical: after the upgrade the menubar is not more shared on 'top screen' but more traditionally inside the 'main window'. Probably the team overlooked a portability problem, that's reasonable considering the wide target they have.

Related

QtVirtualKeyboard focus problem when editing a cell in a QTableView

I have been battling for the last couple of days to include the QtVirtualKeyboard into my QWidget based app that is running on a Raspberry Pi with a 7" touch screen display.
Here's what I've done so far :
Installed the plugin :
sudo apt-get install -y qtvirtualkeyboard-plugin
sudo apt-get install -y qml-module-qtquick-controls2
sudo apt-get install -y qtdeclarative5-dev
sudo apt-get install qml-module-qt-labs-folderlistmodel
Added the QT_IM_MODULE environment variable and set that to qtvirtualkeyboard
Added QT += quickwidgets to my .pro
Created a QQuickWidget to place my virtual keyboard.
.h
private:
QQuickWidget *m_quickWidget;
.cpp
// In constructor
QUrl source(QML_FILE_PATH + "virtualkeyboard.qml");
m_quickWidget->setSource(source);
m_quickWidget->setAttribute(Qt::WA_AcceptTouchEvents);
ui->verticalLayout->addWidget(m_quickWidget);
And finally my virtualkeyboard.qml file
import QtQuick 2.7
import QtQuick.VirtualKeyboard 2.1
Rectangle {
id: window
width: 600
height: 0
InputPanel {
id: inputPanel
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: window
height: inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
So up to now, visually everything is looking pretty good. When I open my app, the keyboard widget is not visible (window height: 0 in qml), and when I double-click a cell in my QTableView (which has flags Qt::ItemIsEnabled | Qt::ItemIsEditable), the keyboard widget shows up at the bottom of my vertical layout at the right position and size.
And now to my problems :
The main problem I have is when I double-click on my editable cell, my keyboard widget shows up and my cell seems to still have focus (blinking cursor is visible in the clicked cell). Up to now all is working well. But when I click a button on the virtual keyboard, the editable cell loses focus, the keyboard widget closes, and I get this error in my application console :
InputContext::sendKeyClick(): no focus to send key click - QGuiApplication::focusWindow() is: QWidgetWindow(0x1e68250, name="ConfigWindow") where ConfigWindow is the name of the base widget in my Designer form.
Another smaller yet seemingly unsolvable issue I have is that the keyboard only opens up when I double-click a cell in my QTableView. This is a weird one because i set the editTriggers to CurrentChanged in my designer. I know that works because if I single click my cell, the cursor starts blinking and if I use my physical keyboard which is connected to my raspberry, I can edit the text. (Of course, the physical keyboard is only available during the development of my app and won't be available in the finished product).
I hope I have been clear enough, but will gladly provide more details if required.
Any help on either of these issues would be greatly appreciated.
Cheers.
EDIT:
Some useful links I have come across:
Qt Virtual Keyboard in QQuickWidget
Resize qtvirtualkeyboard according to QObject
Qt Virtual Keyboard
Ok, so after another few days of all out war with the virtual keyboard, I have finally achieved the desired result.
After finding this gem of a guide, it turns out that because the widget containing my QTableView and QtVirtualKeyboard was a QDialog that was being displayed using the exec() method, it meant that the window properties wouldn't allow the keyboard to modify my data. And while the solution proposed in the guide didn't resolve my problem, making my widget inherit QWidget did set me off along the path to getting it all working properly.
I say this because once I changed my QDialog into a QWidget, I then had a console output error saying unknown:0 input method is not set every time I pressed a key.
The solution to this was to remove the Qt:Dialog flag from my setWindowFlags() method. And maybe most importantly, set the focus policy of my QQuickWidget to NoFocus like so:
// In constructor
QUrl source(QML_FILE_PATH + "virtualkeyboard.qml");
m_quickWidget->setSource(source);
m_quickWidget->setAttribute(Qt::WA_AcceptTouchEvents);
m_quickWidget->setFocusPolicy(Qt::NoFocus);
ui->verticalLayout->addWidget(m_quickWidget);
Hallelujah!! My QtVirtualKeyboard finally sends the clicked keys to my editable cell.
Finally, to open the keyboard with a single click on a cell in my table, I'm sure there is a better solution than this, but i connected a slot to my QTableView's pressed signal and manually set the visibility of the input method:
void ConfigWindow::on_tableView_pressed(const QModelIndex &index)
{
if ((index.column() == 0) || (index.column() == 1))
{
QApplication::inputMethod()->show();
}
}
Hope this helps anyone having the same trouble as me with this powerful yet painfully under-documented plugin.

Clicking QSystemTrayIcon brings up an empty context menu on Mate desktop

I'd like to create a tray icon for my application for showing the main window on clicking on it after the former was minimized.
Here's the implementation:
TrayIcon.h:
class TrayIcon_t : public QSystemTrayIcon {
Q_OBJECT
public:
TrayIcon_t();
};
TrayIcon.cpp:
TrayIcon_t::TrayIcon_t() {
setIcon(QIcon(":/icons/tray.ico"));
}
Main.cpp (part only, there's no more code related to the tray):
TrayIcon_t *tray = new TrayIcon_t;
QObject::connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), &MainWindow, SLOT(show()));
tray->show();
While on LXDE it works just fine, on MATE desktop it mostly opens a small menu (I think) containing no items and doesn't emit the signal required by the main window to be shown. See this picture.
Do you think I've encountered a bug in Qt 5.7.0?
Turns out that this is a bug. Reported it and became accepted at:
https://bugreports.qt.io/browse/QTBUG-55717

Adding and localizing menu items in the main menu of a Qt application menubar

So first of all here is a screenshot of the said menu of Evernote, localized in French:
[]
As you can see, all the menu items in the main menu (by main menu I mean the one whose name is the application name, like here it is Evernote) are localized in French. There are lots of menu items which the Evernote app itself brings, like Évaluez Evernote pour Mac (Rate Evernote for Mac), Information du compte... (Account Info...), etc. Plus there are the standard OS X provided menu items like Quit Evernote, Preferences, etc which are also localized.
My questions:
How do I add a new item in this main menu? How to access this menu to add items?
How do I localize these items based on my app localization, both OS X provided default ones and the ones I add?
In the Evernote menu, everything seems to be localized except the Services menu option (the submenu options are however localized!)? Can't this be localized as well?
What I have tried:
fMenuBar = fMainWindow->menuBar();
fMenuFile = fMenuBar->addMenu(QObject::tr(qPrintable(String_Class::FileMenu))); //"File" in English, translated into other languages
fAboutAppAct = new QAction(QObject::tr(qPrintable(String_Class::About_App)), fMainWindow); //prints "About App", localized in all languages
fMenuFile->addAction(fAboutAppAct);
fAboutAppAct->setMenuRole(QAction::AboutRole); //otherwise it sits with the other file menu options in the File menu
//reset UI language slot, called whenver UI language is reset. It retranslates all strings in all menus, except this
void AppMenu::reTranslateUISlot()
{
fAboutAppAct->setText(QObject::tr(qPrintable(String_Class::About_App)));
}
Maybe you could reimplement in MainWindow or in AppMenu the changeEvent.
void MainWindow::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange) {
this->retranslateUi(this);
quickStart->retranslateUi(quickStart);
//etc...
} else {
QMainWindow::changeEvent(event);
}
}
You could force Widgets to retranslate themselves. But you need to have registered some QTranslator first.
For example, in the constructor of MainWindow (or in some config dialog) if it's possible to change language at runtime (what I've done in my software):
CustomizeOptionsDialog::CustomizeOptionsDialog(QWidget *parent)
: QDialog(parent, Qt::Tool)
{
// Load the language of the application
customTranslator.load(languages.value( SettingsPrivate::instance()->language()) );
// Translate standard buttons (OK, Cancel, ...)
defaultQtTranslator.load("qt_" + SettingsPrivate::instance()->language(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
QApplication::installTranslator(&customTranslator);
QApplication::installTranslator(&defaultQtTranslator);
}
Where language() returns "fr", "gb" or "cs" (initialized from a signal emitted when one has chosen a new language in options).
/** Change language at runtime. */
void CustomizeOptionsDialog::changeLanguage(const QString &language)
{
QString lang = languages.value(language);
SettingsPrivate *settings = SettingsPrivate::instance();
// If the language is successfully loaded, tells every widget that they need to be redisplayed
if (!lang.isEmpty() && lang != settings->language() && customTranslator.load(lang)) {
settings->setLanguage(language);
defaultQtTranslator.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
QApplication::installTranslator(&customTranslator);
/// TODO: reload plugin UI
QApplication::installTranslator(&defaultQtTranslator);
} else {
labelStatusLanguage->setText(tr("No translation is available for this language :("));
}
}
I hope it's helping.
I still haven't found the complete answer to my problems. But here some of the observations I have made over the last few days:
To be able to add menu items in the main menu, you have to set the menu role accordingly, i.e after adding it wherever you want to (it won't matter, because it will move out), you set the menu role like this:
fYourAction->setMenuRole(QAction::ApplicationSpecificRole);
This will add the menu item in the main menu. If you add more than item in this way, they will appear in the order in which you set their menu roles.
There are few specific roles Qt already provides - i.e for the About <app> item, Quit <app> item, Preferences... item, etc. They are mentioned here.
For example, if your action has a text "Foo", and you add it somewhere as a menu item, and set the role like
fFooAction->setMenuRole(QAction:: PreferencesRole);
then it will automatically move to the main menu and show as Preferences..., what text you actually put in the action will be immaterial. Whatever slot you have attached to it as a response to the triggered() signal will still fire correctly, though. Same goes for QAction::AboutRole as well, whatever text you add in that action, it will move to the main menu and show as About <your_app_name>.
The problem with QAction::AboutRole or QAction:: PreferencesRole is like I said, they won't localize even if you try. They will get localized only when the system locale changes, if you change it just within your app by installing a new translator, it won't change. The workaround? Avoid them and use QAction::ApplicationSpecificRole for all items you want to appear in the main menu. Then they will get properly localized as per your custom translator, and will respect whatever text you provide in the action, i.e if you give foo as text in the action, it will appear as foo in the main menu, and get localized accordingly. Again, mind you, when you are adding multiple items, set the role of the items in order of their appearance, i.e to simulate the Evernote menu above, first set the menu role for the about_app action, then the preferences action. Where you are adding them will be of no importance since they will be moved to a new menu, so the order in which you set the menu role for the items will determine the order in which they appear in the main menu.
The problem with the above approach is that I don't know how to insert separators between the items I am adding in the main menu. It is easy to do that in the menus we add, since we have access to the menu object, but here we don't have access (we add the items somewhere else, and make them move to the main menu by setting the menu role), so I don't know yet how to add multiple separators in the main menu.

Restoring Qt app after minimized to dock, using dock shortcut on OS X

Single-clicking an application icon in the Dock launches that application or, if the application is already open, switches you to that application and brings forward all open windows in that application.
In my Qt app, I do not see this behavior. I have a dock shortcut, I minimize the app on dock, and if I click the minimized instance the app is restored.
If I click the application icon in the Dock (dock shortcut), nothing happens.
(I can right-click, and "Show All Windows" - but that is not similar to the behavior of all other apps)
I tried to place the code from this answer: https://stackoverflow.com/a/15363738/1217150
Placing qDebug statements,
if(test) qDebug("registered");
in the constructor, and
void MyApplictionClass::onClickOnDock()
{
qDebug("dock clicked");
}
On start-up I get both messages... but after minimizing, or any time after start-up, there is no reaction to dock shortcut being clicked or double clicked.
Full code insertion:
MyApplicationClass.h:
#include <objc/objc.h>
#include <objc/message.h>
class MyApplicationClass: public QApplication
{
....
bool dockClickHandler(id self, SEL _cmd, ...);
void onClickOnDock();
};
MyApplicationClass.cpp:
MyApplicationClass::MyApplicationClass()
{
....
objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls, sharedApplication);
if(appInst != NULL)
{
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
objc_object* delClass = objc_msgSend(delegate, sel_registerName("class"));
const char* tst = class_getName(delClass->isa);
bool test = class_addMethod((objc_class*)delClass,
sel_registerName("applicationShouldHandleReopen::hasVisibleWindows:"),
(IMP)dockClickHandler(appInst, sharedApplication), "B#:");
if(!test) qDebug("not registered");
else qDebug("registered");
}
.....
}
bool MyApplicationClass::dockClickHandler(id self, SEL _cmd, ...)
{
Q_UNUSED(self)
Q_UNUSED(_cmd)
onClickOnDock();
return true;
}
void MyApplicationClass::onClickOnDock()
{
qDebug("dock clicked");
}
Perhaps I did something wrong ? There are a few items I don't understand... The referred post had dockClickHandler not part of the class... I just don't know where they have it... And I had to give it arguments when I registered it, otherwise it refused to build. Still, it seems to do... something... just not the right thing. In that code, I do not see where the app will react to clicking the dock.
Is this the correct approach ? Or how can I get my app to restore when its dock shortcut is clicked ?
(OSX 10.6-10.9, Qt 4.8)
If you are refering to this: QT on OS X, how to detect clicking the app Dock Icon, the onClickOnDock should not be a member of the class.
You CAN put it as a normal function inside the MyApplicationClass.cpp file.
There is a hack at least for Qt 5.9.1, when you can implement onClickOnDock() without platform specific code at all. See https://stackoverflow.com/a/46488514/8695355

setCentralWidget() causing the QMainWindow to crash.. Why?

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
this->setupUi(this);
this->setupActions();
this->setWindowTitle(tr("CuteEdit"));
label = new QLabel(tr("No Open Files"));
this->setCentralWidget(label);
label->setAlignment(Qt::AlignCenter);
}
By above code, I get a GUI like this(Its a screenshot of whole screen, Only observe the window displayed in middle of page of ebook). (I used QT Designer)
Now, i want user to select File->Open.. A Dialog appears and file gets selected.. Its contents are to be displayed in *textEdit widget..
Function for that is below..
void MainWindow::loadFile()
{
QString filename = QFileDialog::getOpenFileName(this);
QFile file(filename);
if (file.open(QIODevice::ReadOnly|QIODevice::Text))
{
label->hide();
textEdit->setPlainText(file.readAll());
mFilePath = filename;
QMainWindow::statusBar()->showMessage(tr("File successfully loaded."), 3000);
}
}
The window crashes at line:-
textEdit->setPlainText(file.readAll());
But if i comment the line:-
this->setCentralWidget(label);
i mean i remove label as being the central widget, the program runs as expected.. Why?
And also, I am not clear about the concept of CentralWidget. Pls guide.
JimDaniel is right in his last edit. Take a look at the source code of setCentralWidget():
void QMainWindow::setCentralWidget(QWidget *widget)
{
Q_D(QMainWindow);
if (d->layout->centralWidget() && d->layout->centralWidget() != widget) {
d->layout->centralWidget()->hide();
d->layout->centralWidget()->deleteLater();
}
d->layout->setCentralWidget(widget);
}
Do you see that if your MainWindow already had centralWidget() Qt schedules this object for deletion by deleteLater()?
And centralWidget() is the root widget for all layouts and other widgets in QMainWindow. Not the widget which is centered on window. So each QMainWindow produced by master in Qt Creator already has this root widget. (Take a look at your ui_mainwindow.h as JimDaniel proposed and you will see).
And you schedule this root widget for deletion in your window constructor! Nonsense! =)
I think for you it's a good idea to start new year by reading some book on Qt. =)
Happy New Year!
Are you sure it's not label->hide() that's crashing the app? Perhaps Qt doesn't like you hiding the central widget. I use Qt but I don't mess with QMainWindow that often.
EDIT: I compiled your code. I can help you a bit. Not sure what the ultimate reason is as I don't use the form generator, but you probably shouldn't be resetting the central widget to your label, as it's also set by the designer, if you open the ui_mainwindow.h file and look in setupGui() you can see that it has a widget called centralWidget that's already set. Since you have used the designer for your GUI, I would use it all the way and put the label widget in there as well. That will likely fix your problems. Maybe someone else can be of more help...
I'm not sure I understood your problem, neither what the guys above said (which I guess are valid information) and it seems to be an old topic.
However, I think I had a problem that looks like this and solved it, so I want to contribute my solution in case it helps anyone.
I was trying to "reset" central widget using QLabels. I had three different ones, switch from first to second, then to third and failed to switch back to the first one.
This is my solution that worked:
Header file
QLabel *imageLabel;
Constructor
imageLabel = new QLabel("<img src='/folder/etc.jpg' />");
this->setCentralWidget(imageLabel);
Reset
imageLabel = NULL;
imageLabel = new QLabel("<img src='/folder/etc.jpg' />");
this->setCentralWidget(imageLabel);
Hope that helps
Aris