I create a QDockWidget and I do a setwidget with my own QMenu instance. My QMenu has n number of QActions displayed sequentially one after another and there is a blank space at the end of all action items. Now, when I click on this blank space, my QDockwidget minimizes to an icon. Just the close and detach buttons are visible. Any reason for this strange behaviour?
The code looks something like this :
QDockWidget *dw = new QDockWidget(this);
QMenu * menu = new QMenu(this)
menu->addAction(1);
menu->addAction(2);
.
.
dw->setWidget(menu);
I tried to reproduce the issue of OP. In absence of an MCVE, I made my own testQDockWidgetMenu.cc:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup UI
QMainWindow qWinMain;
QLabel qLbl("Choose a menu item");
qWinMain.setCentralWidget(&qLbl);
QDockWidget qDock;
qDock.setWindowTitle("Dockable Menu");
QMenu qMenu("Menu");
#define ADD_ITEM(I) \
qMenu.addAction( \
"Menu Item "#I, [&]() { qLbl.setText("Menu item "#I" chosen."); })
ADD_ITEM(1);
ADD_ITEM(2);
ADD_ITEM(3);
#undef ADD_ITEM
qDock.setWidget(&qMenu);
qWinMain.addDockWidget(Qt::TopDockWidgetArea, &qDock);
qWinMain.show();
// runtime loop
return app.exec();
}
A minimal Qt project testQDockWidgetMenu.pro:
SOURCES = testQDockWidgetMenu.cc
QT += widgets
Compiled and run in cygwin64 on Windows 10:
$ qmake-qt5 testQDockWidgetMenu.pro
$ make && ./testQDockWidgetMenu
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQDockWidgetMenu.o testQDockWidgetMenu.cc
g++ -o testQDockWidgetMenu.exe testQDockWidgetMenu.o -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread
Qt Version: 5.9.4
After I clicked into the blank space on the right I got this (like described by OP):
Oops! (I hadn't expected this.)
I believe this behavior is built-in into menus: As they are intended for popup-menus there is an auto-close when an out-side click is detected.
I scrolled a bit through the doc. of QMenu whether there is something to suppress this default behavior. Instead, I found:
void QMenu::aboutToHide()
This signal is emitted just before the menu is hidden from the user.
This function was introduced in Qt 4.2.
This is, at least, good for a workaround: If the auto-close intends to hide the menu just show it again. To check this, I added a signal handler:
// install signal handlers
QObject::connect(&qMenu, &QMenu::aboutToHide,
[&]{
QWidget *const pQParent = dynamic_cast<QWidget*>(qMenu.parent());
if (pQParent && pQParent->isVisible()) {
qMenu.show();
qDebug() << "Prevent hiding qMenu";
}
});
(inserted just above return app.exec();)
Compiled and run again: Et voilà. No auto-close anymore.
The debug output in console shows that the signal handler does what it is intended for:
Qt Version: 5.9.4
Prevent hiding qMenu
Prevent hiding qMenu
Prevent hiding qMenu
Prevent hiding qMenu
Prevent hiding qMenu
Prevent hiding qMenu
Prevent hiding qMenu
Note:
The check for if (pQParent && pQParent->isVisible()) could appear a bit paranoid and might be unnecessary.
I tried without and it worked as well. Though, I noted that the debug message appeared also e.g. when I closed the whole dock. Not that this changed the visual appearance – if a parent widget is invisible the children are as well.
So, I came to the conclusion that the paranoid check doesn't hurt.
Eventually after circling around, I choose to use QToolbar instead of QMenu which solved the probem
Related
I implemented example (chapter 2) from "Mastering Qt 5" book but the code crashes when adding widget to centralWidget's layout:
ui->centralWidget->layout()->addWidget(&mCpuWidget)
I suspect that the centralWidget does not have layout, hence it crashes but I don't know how to fix that?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
mCpuWidget(this)
{
ui->setupUi(this);
SysInfo::instance().init();
ui->centralWidget->layout()->addWidget(&mCpuWidget);
}
Here are two more classes which might help figure out the problem. Some of you might have the book too with all of the code (hence I mentioned it).
CpuWidget::CpuWidget(QWidget* parent):
SysInfoWidget(parent),
mSeries (new QPieSeries (this))
{
mSeries->setHoleSize(0.35);
mSeries->append("CPU Load", 30.0);
mSeries->append("CPU Free", 70.0);
QChart* chart = chartView().chart();
chart->addSeries(mSeries);
chart->setTitle("CPU Average Load");
}
This class creates and sets layout (QVBoxLayout)
SysInfoWidget::SysInfoWidget(QWidget *parent, int startDelayMs, int updateSeriesDelayMs) :
QWidget(parent),
mChartView(this)
{
mRefreshTimer.setInterval(updateSeriesDelayMs);
connect(&mRefreshTimer, &QTimer::timeout,
this, &SysInfoWidget::updateSeries);
QTimer::singleShot(startDelayMs,
[this] {mRefreshTimer.start();});
mChartView.setRenderHint(QPainter::Antialiasing);
mChartView.chart()->legend()->setVisible(false);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(&mChartView);
setLayout(layout);
}
I'm the co-author of the book "Mastering Qt 5" !
I guess your suspicion about the layout is correct:
ui->centralWidget->layout()->addWidget(&mCpuWidget);
Without any layout defined the returned item is null so you can't call the method layout().
If you have some errors during your learning you should refer to the final source code hosted on github here: https://github.com/PacktPublishing/Mastering-Qt-5
Take a look to the the file "Mastering-Qt-5/Chapter_02/MainWindow.ui":
<ui version="4.0">
...
<widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="horizontalLayout"/>
</widget>
...
</ui>
As you can see for this project, a horizontalLayout of type QHBoxLayout is defined in the centralWidget. You can easily edit a ".ui" file with a text editor from Qt Creator with the following steps:
Right-click on "MainWindow.ui" in the Project hierarchy view
Select "Open-With"
Finally "Plain text editor"
Select "Form editor" when you want to come back to the WYSIWYG editor.
As suggested in other answers, the way to do it from C++ with the following line is also correct:
ui->centralWidget->setLayout(new QHBoxLayout());
Thank you for highlighting the lack of information about the layout here. I created an issue to add an errata about this topic.
Unless I have missed something in the code you have provided you haven't actually set your central widget. By default calling QMainWindow::centralWidget() returns a NULL pointer. You need to first call QMainWindow::setCentralWidget(QWidget* yourCentralWidget) before you call it. And yes, you also need to add a layout to it if you want to use layout()->addWidget(...). You can create an instance of a generic QWidget, set its layout, set is a central widget and then work with it.
You can fix your problem either by adding a layout in C++:
ui->setupUi(this);
SysInfo::instance().init();
ui->centralWidget->setLayout(new QVBoxLayout()); // Or any other layout class
ui->centralWidget->layout()->addWidget(&mCpuWidget);
Or in the UI Designer by using those buttons:
Note that for the buttons to be active you need to have at least 1 widget in your central widget and then select your central widget. You can then write:
ui->setupUi(this);
SysInfo::instance().init();
// One way
ui->centralWidget->layout()->addWidget(&mCpuWidget);
// Another way
ui->layout->addWidget(&mCpuWidget);
Finally you could also move your CpuWidget to the ui file using the "Promote to..." option in the contextual menu. In this case you would not need mCpuWidget but you could access it using something like ui->cpuWidget.
For more info read the Qt Designer manual:
Using Layouts in Qt Designer
Using Custom Widgets with Qt Designer
One of the methods available in the QTabWidget class is setTabEnabled - I can't see of a way to set this in QtDesigner (I know I can set it in the code) - is there a specific reason why this method (and presumably others?) aren't settable in QtDesigner?
Just change enabled property of respective widget inside QTabWidget.
according to documentation this suppose to work. I've taken a look on QTabWidget code and it looks like either documentation is wrong or there is a bug in code. I will not explain why just provide workaround on it.
Create this method:
void tabBarWorkaround(QTabWidget *tabWidget) {
for (int i=0; i<tabWidget->count(); ++i) {
tabWidget->setTabEnabled(i, tabWidget->widget(i)->isEnabled());
}
}
And use it in construction time:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
tabBarWorkaround(ui->tabWidget);
}
From this moment changing enabled property of page in designer will work like you want.
I've test this with Qt 5.2.1 and it works.
I've realised that there are two separate possible events:
unchecking the 'enabled' checkbox for a tab widget in QtDesigner actually invokes self.<tabname>.setEnabled(False), which seems to disable all child widgets within the tab, but does not disable the tab button itself(!)
Disabling the tab button so that it's not clickable is not possible in QtDesigner, but is possible in code (as discussed in the previous answer): self._uiform.<tab frame>.setTabEnabled(<tab index>,False)
The QStatusBar has just one line each time and I cannot track the history or save the history in a log file.
So I want to have a dock widget in my mainwindow that is able to display the messages I need in a multi-line way and auto-scrolling way and then automatically saved to a log file.
My question is how to do this in Qt?
If what you are looking for is something similar to the "Application Output" pane of QtCreator, then a simple QPlainTextEdit can do the job. You can call QPlainTextEdit::setReadOnly(true) if you don't want the user to be able to edit its content (i.e. only your application can write to it).
If you want auto-scrolling and automatically saving to a log file, you will need to subclass it though. Here's a little something to get you started:
#include <QCoreApplication>
class MyLogWindow : public QPlainTextEdit
{
Q_OBJECT
/* snip */
public:
void appendMessage(const QString& text);
private:
QFile m_logFile;
};
void MyLogWindow::appendMessage(const QString& text)
{
this->appendPlainText(text); // Adds the message to the widget
this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()); // Scrolls to the bottom
m_logFile.write(text); // Logs to file
// optional if you want to see the changes
// after appendPlainText() immediately
// useful if you use this->appendMessage() in a loop
QCoreApplication::processEvents();
}
I leave it to you to deal with the boilerplate (opening and closing the file, dealing with newlines, etc.).
Now, simply put an instance of MyLogWindow in a dock in your QMainWindow, and call MyLogWindow::appendMessage() every time you need to log something to have it displayed, scrolled and saved automatically.
I am trying to move my QSystemTrayIcon module to a separate thread. I am getting the below error while running the application. The Initialize() method addAction causing the error.
QWidget: Must construct a QApplication before a QPaintDevice
My sample code snippets:
Declared the global variable
QMenu myMenu;
Member Variable in the header file
QAction* openFile;
In the constructor
openFile = new QAction(parent);
Initialize()
{
myMenu.addAction(openFile);
}
void myApp::run()
{
Initialize()
}
You must not use any GUI-related classes outside your main thread, i.e. the one QApplication was created in. Hence you cannot move your QSystemTrayIcon stuff in a separate thread. And no, there is no workaround.
You are approaching this problem backwards. The reason that "The TrayIcon menus are not displaying if some functionality is running at the background Menus will display after the process completion." is because you are blocking the GUI thread when you wait for things. Do not block the GUI thread by waiting on things. Most of Qt provides signals that fire when things get accomplished (when they succeed or fail). Those are the nonblocking APIs that you should be using.
I am developing an application in Qt that rebuilds its menus very often. However, when we call clear(), and re-add the actions that we want in the menu, "Special Characters..." appears to remain in the menu. Is there any way to remove, or move this action to the bottom of the QMenu?
Here is the code that rebuilds the menu:
void MainWindow::initMenus(Tab* tab)
{
menuBar()->clear();
menuFile->clear();
menuEdit->clear();
menuSettings->clear();
menuHelp->clear();
ui_toolBar->clear();
menuBar()->addMenu(menuFile);
menuBar()->addMenu(menuEdit);
menuFile->addAction(actionNew);
menuFile->addAction(actionOpen);
if(tab) tab->addActionsFile(menuFile);
menuFile->addSeparator();
menuFile->addAction(actionNext);
menuFile->addAction(actionPrevious);
menuFile->addAction(actionClose);
menuFile->addSeparator();
menuFile->addAction(actionQuit);
if(tab) {
tab->addActionsEdit(menuEdit);
menuEdit->addSeparator();
tab->addActionsHelp(menuHelp);
menuHelp->addSeparator();
}
menuEdit->addAction(actionEditor_Settings);
menuHelp->addSeparator();
menuHelp->addAction(actionAbout);
if(tab) tab->addOtherActions(menuBar());
menuBar()->addMenu(menuHelp);
ui_toolBar->addAction(actionNew);
ui_toolBar->addAction(actionOpen);
if(tab) tab->addToolbarActions(ui_toolBar);
}
It is supplied a tab, which can add its own actions to the menu as well using those functions.
This is a feature of Mac OS X that isn't easily disabled. You'll notice that nearly every application on Mac OS has it. It's automatically added to the Edit menu by the OS to allow the input of international characters.
It seems from your question, but isn't quite clear, that when you initially create the Edit menu, the Special Characters... menu item is initially the last menu item, but becomes the first menu item once editMenu->clear() has been called. One route you could go is instead of clear()ing the menus, you can delete the menus and recreate them completely. Your edit menu looks pretty static, though. Maybe it doesn't have to be recreated at all.
Now, that said, if you're really sure you need to get rid of this menu item, there are a couple of ways to accomplish that.
The first, and least desirable one is to simply not have an "Edit" menu. If there's no menu titled "Edit", Mac OS will not add a "Special Characters" menu item.
The second method requires a little platform specific Objective-C code. Obviously, this should only be built into your project on Mac OS.
MenuDeleter.m:
#include <Foundation/NSUserDefaults.h>
void deleteSpecialCharacters()
{
[[NSUserDefaults standardUserDefaults]
setBool:YES forKey:#"NSDisabledCharacterPaletteMenuItem"];
}
MenuDeleter.h
#ifndef MENUDELETER_H_
#define MENUDELETER_H_
void deleteSpecialCharacters();
#endif
And finally, in main.cpp:
#include <QApplicaiton>
#include "MenuDeleter.h"
int main(int argc, char **argv)
{
#ifdef Q_OS_MAC
deleteSpecialCharacters();
#endif
QApplication app(argc, argv);
....
return app.exec();
}
So that's how to make it go away completely. But the question is, do you really want to prevent the user from being able to input special characters into your app?
This answer may be more applicable to a COCOA OSX app, but I was able to remove these menu items in Objective-C by getting an NSMenu handle to the Edit Menu itself in the applicationDidFinishLaunching function , looping through the NSMenuItems in the itemArray and removing them by calling removeItem.