How to make a QTextEdit look disabled - c++

At some point, I need to make a QTextEdit look like it was disabled.
I'm not against calling setEnabled(false), but then the QTextEdit does not receive QContextMenuEvent event anymore (and I need a right-click context menu to be available....because that's how the QTextEdit gets disabled by the user, so that's how I want him to enable it back).
So I do:
QColor mainWindowBgColor = palette().color(QPalette::Window);
// for the current widget
setStyleSheet(QString("background-color: %0").arg(mainWindowBgColor.name(QColor::HexRgb)));
This looks good, unless you right-click the widget and show it's context menu: The context menu appears but looks bad. Item highlighting does not work and then selected text is hardly visible (painted in white on a grey background).
How could I either:
call setEnabled(false) and have right-mouse click context menu be accessible
or use setStyleSheet but make sure it won't be usd by the context menu widget begin painted
or any alternative....

I would try subclassing QTextEdit and overriding contextMenuEvent. There I would show (exec) a standard menu, after changing its stylesheet:
#include <QTextEdit>
#include <QContextMenuEvent>
#include <QMenu>
class TextEdit : public QTextEdit
{
public:
TextEdit(QWidget* p) : QTextEdit(p){}
protected:
void contextMenuEvent(QContextMenuEvent * event)
{
QMenu * menu = createStandardContextMenu();
menu->setStyleSheet("background-color: gray");
menu->exec(QCursor::pos());
}
};
In the above example I set the menu background color to gray, but the goal of this example is to show one can override the menu style sheet, so to prevent the menu to use the one inherited from its parent.

Style sheets are inherited by all sub-widgets. To make a style sheet apply only to a certain widget (or a type of widgets), you have to specify accessors.
E.g.
setStyleSheet(QString(
"QTextEdit { background-color: %0 }"
).arg(mainWindowBgColor.name(QColor::HexRgb)));

Related

Qt generate UI based on template

I'm looking to generate a set of labels and buttons based on some kind of template, but I don't know how to do the template part.
I'll be using a tab widget which I already have set up, and in one the tabs, I want to have a two labels, a custom button, and a textbox. It'll be repeated around 40-50 times (dependent on a given value at startup) and have spacing as needed.
Once I have a template, I foresee calling it in a loop and setting the appropriate displayed text(Label_1, Label_2, etc) and connect statements where needed.
As I said, I don't know how to template parts of the UI so they can be placed in a kind of auto-generation.
I had thought of making one group, copying the xml, and somehow adding it but that doesn't seem to be a proper way. A little new to Qt.
This is roughly the layout I want to repeat. It has two labels, a lineedit, and one pushbutton.
There's no "good" way to do this in QtDesigner/QtCreator. At best you could copy/paste the set of controls 50 times and then in C++ code hide the ones you don't need. But I wouldn't recommend this.
Instead, just create the controls (labels/button/text box), and a layout to hold them, in C++ code, inside a loop which iterates however many times you need at runtime. Insert the controls layout into the tab widget page layout which you have set up in designer mode. It is not difficult, and will actually be more efficient than what QtDesigner produces since that tends to generate more code than you typically need in the first place.
As a starting point, you could look at the C++ code which is generated by the Qt UI Compiler (UIC) tool for your current design (it takes the XML from designer and turns it into C++ code). You can find this in the build folder for your project, typically named something like ui_ClassName.h, probably in a ui subfolder of the build tree.
UPDATE:
Another, possibly better, way to do this is to create the "template" QWidget class/form, which is going to be used multiple times, as a separate object. The "template" design could be created/maintained using QtCreator/Designer (or just directly in C++). The (possible) advantage here is that as the app requirements evolve, the template widget can be expanded with additional functionality or even re-used in other parts of the UI.
For example, I'd assume the text editor and button in the given mockup image will actually need to do something (eg. edit data and submit it). So some basic functionality can be built into the "template" widget, for example to emit a signal with the text contents of the line editor when the button is pressed.
I put together a quick example. I'm creating the simple MainWindow in pure C++ to simplify/shorten the example code. The "template" I'm calling an Editor. The Editor class and UI form I initially created with the QtCreator wizard (New -> Qt Designer Form Class). I then added the label/control widgets in designer mode. And in C++, a textEdited(const QString &text) signal in the header, and in the Editor() constructor a lambda connection to emit that signal when the button is pressed.
The Editor class code is straight out of the QtCreator wizard except for two edits I'm highlighting below. The designer form has two relevant controls: a QLineEdit (lineEdit) and a QPushButton (pushButton). I'll link to the full files below.
Editor.h
// in the Editor class declarations:
signals:
void textEdited(const QString &text) const;
Editor.cpp
// in the constructor, after ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, [this]() {
emit textEdited(ui->lineEdit->text());
});
Test harness, including MainWindow subclass and main()
#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QBoxLayout>
#include <QMessageBox>
#include "Editor.h"
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() : QMainWindow()
{
// set up a tab widget as the window central widget
QTabWidget *tabWidget = new QTabWidget(this);
setCentralWidget(tabWidget);
// the first/only page will contain all the editors in a vertical layout
QWidget *editorsPage = new QWidget(this);
editorsPage->setLayout(new QVBoxLayout());
// add the editors container page to tab widget
tabWidget->addTab(editorsPage, tr("Editors page"));
// Now create a number of editor widgets using our Editor class "template"
int layoutItems = 5; // number of editors needed, could be dynamic
for (int i=0; i < layoutItems; ++i) {
// Create an Editor instance with the tab page as parent
Editor *editor = new Editor(editorsPage);
// Add the editor widget to the tab page layout
editorsPage->layout()->addWidget(editor);
// A simple connection with the editor signal, as way of example.
connect(editor, &Editor::textEdited, this, [this](const QString &text) {
// just show a message box with the editor text
QMessageBox::information(this, tr("Text Edited"), text, QMessageBox::Ok);
});
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}
#include "main.moc"
Links The full Editor code:
Editor.h
Editor.cpp
Editor.ui
The XML in Qt Creator is for UIC in QMake to generator code for you.
For example, QMake translates your mainwindow.ui to ui_mainwindow.h, and within you will find void setupUi(QMainWindow *MainWindow) with the actual code that creates and places the widgets.
Look at this code, the docs, and create and place the widgets yourself by code.
For example, adding 5 checkboxes to a groupbox by code:
QVBoxLayout *l = new QVBoxLayout(this);
ui->groupBox_4->setLayout(l);
for(int i=0; i<5; i++){
QCheckBox *c = new QCheckBox(this);
l->addWidget(c);
}

How to resize a QLabel displayed by a QWidgetAction after changing it's text

I use a QWidgetAction to add a header to a context menu (that will also show up on Windows, no matter what style is used, in contrast to addSection(), which does not always actually display the title).
The action's widget is a QLabel. It's text is changed by each invocation of the context menu. The menu is setup in the constructor of my class, and the QWidgetAction is added like so (all m_ variables are member variables declared in the header):
m_contextMenu = new QMenu(this);
m_menuTitle = new QLabel;
m_menuTitle->setAlignment(Qt::AlignCenter);
m_menuTitle->setMargin(4);
QWidgetAction *titleAction = new QWidgetAction(m_contextMenu);
titleAction->setDefaultWidget(m_menuTitle);
m_contextMenu->addAction(titleAction);
m_contextMenu->addSeparator();
When the menu is requested, the text of the label is changed and the menu is displayed like so:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
m_contextMenu->exec(place_to_display);
When the label's text is set for the first time (with a short text the label's text is set to), everything is fine:
but when it's set to some longer text, the size remains the same and the text is cropped:
I tried to fix this, but the only working solution I found was to define the QActions displayed in the menu in the constructor, owned by this, setting the label's text, clearing the menu and adding the actions again, like so:
m_contextMenu->clear();
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
m_contextMenu->addAction(m_menuTitleAction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(m_editAction);
m_contextMenu->addAction(m_deleteAction);
m_contextMenu->exec(place_to_display);
Is there a way to resize the title without rebuilding the menu each time?
The solution is to send a resize event instead:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
QResizeEvent re(new_size, m_contextMenu->size());
qApp->sendEvent(m_contextMenu, &re);
This will set the QMenu's internal itemsDirty flag and will force geometry recalculation when the menu is shown. Note that the new size in the event does not matter, as the menu will aways resize based on its sizeHint()!
The QResizeEvent solution didn't really work for me (with a more complex widget), I found the generic solution by reading the QMenu and QAction source code.
m_widget->installEventFilter(this);
// and in my case m_widget->layout()->setSizeConstraint(QLayout::SetFixedSize);
bool SomeClass::eventFilter(QObject* watched, QEvent* event)
{
if (watched == m_widget && event->type() == QEvent::Resize) {
// Force QMenu to recalculate the geometry of this item
QActionEvent e(QEvent::ActionChanged, this);
qApp->sendEvent(m_contextMenu, &e);
}
...
}
QActionEvent triggers everything we need in QMenu: recalculating geometries, resizing the menu to its new size (if it's visible), etc.
This answer extends user362515's answer.
There is little more effort required if you change the size of a hidden widget action (e.g., because of its menu is currently collapsed).
Create a new class ActionWidget which derives publicly from QWidget.
Then override the showEvent method and implement it like this:
void ActionWidget::showEvent(QShowEvent* event)
{
QResizeEvent resize_event(QSize(), parentWidget()->size());
parentWidget()->adjustSize();
qApp->sendEvent(parentWidget(), &resize_event);
QWidget::showEvent(event);
}
Notice that adjustSize must be called on the parent widget of the action widget and the event must be sent to the parent widget.
Of course, you must also reimplement QWidgetAction::createWidget such that it returns an instance of the ActionWidget-class and make sure that ActionWidget reports a proper (updated) size hint.

QMenu item text disappears when icon added

I am attempting to add an icon to my QMenu using Qt Designer, however I realized that my text disappears when my icon is added. Is there any way for me to show my icon next to my text?
It was not supported in Qt 4, maybe it is in Qt5 I haven't checked.
In Designer itself there isn't much you can do. In the code one option is to customize the style to draw both the icon and text:
- sizeFromContents for QStyle::CT_MenuBarItem
- drawControl for QStyle::CE_MenuBarItem
- drawCustomControl for QStyleOptionMenuItem
This is not supported by default, mostly because it is not usual an operation that you wish to achieve in here. Of course, you could always use an image with text included, but that is also hackish, unless you paint the image dynamically and then load it later. Although even that would be quite a bit of work.
In order to do, you will need to fiddle with Qt a bit. This is the closest experiment that I would start off with, personally. I have not had time to check whether it actually works, but there should be something among these lines:
class CustomMenuBarWidget : public QWidget
{
public:
explicit CustomMenuBarWidget(QWidget *parent = Q_NULLPTR)
: QWidget(parent)
, menuBar(new QMenuBar())
{
}
virtual void paintEvent(QPaintEvent *event) {
QStyleOptionMenuItem styleOptionMenuItem;
QIcon icon("path/to/my/icon");
styleOptionMenuItem.icon = icon;
styleOptionMenuItem.text = "Hello World!";
QPainter painter(this);
menuBar->style()->drawControl(QStyle::CE_MenuBarItem, &styleOptionMenuItem, &painter, menuBar);
}
private:
QMenuBar *menuBar;
};
You could probably also have a look at QWidgetAction how to insert custom widgets into toolbars and menubars. I have never used that myself in any serious project, but might be useful to be aware of.

Qt show modal dialog (.ui) on menu item click

I want to make a simple 'About' modal dialog, called from Help->About application menu. I've created a modal dialog window with QT Creator (.ui file).
What code should be in menu 'About' slot?
Now I have this code, but it shows up a new modal dialog (not based on my about.ui):
void MainWindow::on_actionAbout_triggered()
{
about = new QDialog(0,0);
about->show();
}
Thanks!
You need to setup the dialog with the UI you from your .ui file. The Qt uic compiler generates a header file from your .ui file which you need to include in your code. Assumed that your .ui file is called about.ui, and the Dialog is named About, then uiccreates the file ui_about.h, containing a class Ui_About. There are different approaches to setup your UI, at simplest you can do
#include "ui_about.h"
...
void MainWindow::on_actionAbout_triggered()
{
about = new QDialog(0,0);
Ui_About aboutUi;
aboutUi.setupUi(about);
about->show();
}
A better approach is to use inheritance, since it encapsulates your dialogs better, so that you can implement any functionality specific to the particular dialog within the sub class:
AboutDialog.h:
#include <QDialog>
#include "ui_about.h"
class AboutDialog : public QDialog, public Ui::About {
Q_OBJECT
public:
AboutDialog( QWidget * parent = 0);
};
AboutDialog.cpp:
AboutDialog::AboutDialog( QWidget * parent) : QDialog(parent) {
setupUi(this);
// perform additional setup here ...
}
Usage:
#include "AboutDialog.h"
...
void MainWindow::on_actionAbout_triggered() {
about = new AboutDialog(this);
about->show();
}
In any case, the important code is to call the setupUi() method.
BTW: Your dialog in the code above is non-modal. To show a modal dialog, either set the windowModality flag of your dialog to Qt::ApplicationModal or use exec() instead of show().
For modal dialogs, you should use exec() method of QDialogs.
about = new QDialog(0, 0);
// The method does not return until user closes it.
about->exec();
// In this point, the dialog is closed.
Docs say:
The most common way to display a modal dialog is to call its exec() function. When the user closes the dialog, exec() will provide a useful return value.
Alternative way: You don't need a modal dialog. Let the dialog show modeless and connect its accepted() and rejected() signals to appropriate slots. Then you can put all your code in the accept slot instead of putting them right after show(). So, using this way, you wouldn't actually need a modal dialog.

Close button only for some tabs in Qt

I am using Qt for an assignment I have for college, and I want to use QTabWidget to display a chat window much like Pidgin's. I want to make the "group chat" tab always open and impossible to close and the rest of the "private channel" tabs closable.
QTabWidget's setTabsClosable(bool) is not helping.
Any ideas?
I found an easier solution, I think.
Simply access the relevant close button and resize it.
tabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0);
Find the bar (it is private, so use findChild()) and remove the buttons. Documentation claims that close buttons may be placed on left side too.
QTabBar *tabBar = ui->tabWidget->findChild<QTabBar *>();
tabBar->setTabButton(0, QTabBar::RightSide, 0);
tabBar->setTabButton(0, QTabBar::LeftSide, 0);
Hallo,
I guess this post won't help the author of this thread but perhaps someone else wanders over here.
In most cases a non-closable tab should not only ignore the closevent it also should not show a close symbol in its corner. A nice way to reach this is to modify the QTabBar which is inside the QTabWidget.
Example:
// let tabWidget be a QTabWidget with at least one page
QPushButton *closeButton = new QPushButton();
// set icon and size for PushButton, ...
// connect Signal clicked() from closeButton with Slot closeCurrentTab(), ...
// next line sets closeButton in right corner on tab with index 0
tabWidget->tabBar()->setTabButton(0, QTabBar::RightSide, closeButton);
Although tabBar() is indeed protected, Klaus pointed into the right direction. Simply subclass QTabWidget and implement a wrapper method.
You should reimplement your widget's event(Event *e) method, check the type of e, find out CloseEvents, and call parent class's event when you can allow tab to close, or e->ignore() when you do not want it.
Note, then you must parent's event() handle othr events, so do not accept(), reject() or forget them Ж)
I guess you can handle the tabCloseRequest signal and decide whether u'll close a given tab or not
http://doc.qt.io/archives/4.6/qtabwidget.html#tabCloseRequested
Edit: I created a small example to check it out. My example is a simple QtGui application with a mainwindow that has a tabwidget. I then added the tabCloseRequested slot. Here is the code
void MainWindow::on_tabWidget_tabCloseRequested(int index)
{
if(someCondition){
return;
} else if(anotherCondition){
ui->tabWidget->removeTab(index);
}
}
From this example only tabs where the condition doesn't apply will be closed.
The best way for adding a pushbutton to some tabs and not in other is to define a subclass of QTabWidget for taking the QTabBar that is a potected!
The code below is tested and it works:
//TabWidget.h
#ifndef TABWIDGET_H
#define TABWIDGET_H
#include <QTabWidget>
class TabWidget : public QTabWidget {
public:
TabWidget(QWidget *parent);
~TabWidget();
QTabBar *tabBar() const;
};
#endif /* TABWIDGET_H */
//TabWidget.cpp
#include "TabWidget.h"
TabWidget::TabWidget(QWidget * p=0) : QTabWidget(p) {}
TabWidget::~TabWidget() {}
QTabBar * TabWidget::tabBar() const {return QTabWidget::tabBar();}
For using this subclass and create a new tab with a custom button you have to following this instructions ( ui->tabWidget is a QTabWidget with setClosableTabs=false):
TabWidget *t = (TabWidget *) ui->tabWidget;
t->addTab(new QWidget, "empty");
QTabBar *tab = t->tabBar();
QPushButton *b = new QPushButton();
b->setText("x");
tab->setTabButton(tab->count() -1, QTabBar::RightSide, b);
connect(b,SIGNAL(...),this,SLOT(...));
Not sure, why nobody here mentioned the simplest working solution:
tabWidget->tabBar()->setTabButton(0, QTabBar::RightSide, 0);
This completely removes the close button, and the space taken by it.
Documentation