Promote custom widget with custom constructor in QT Creator - qt

I know, this is basically the same question, but my problem goes further.
The following tree explains my structure:
QWidget
|
CustomWidget
| |
MyTable MyWidgetAroundIt
I have promoted MyTable in Qt Designer. So, I can add it to MyWidgetAroundIt. That worked quite well. The only problem is, CustomWidget requires it's parent to be a CustomWidget too, its constructor looks like:
CustomWidget(CustomWidget* parent) : QWidget(parent), _specialValue(parent->getSpecialValue)
This causes compile errors, as the designer generated code tries to initialize MyTable with a QWidget*, instead of the CustomWidget*. What could/should I do to prevent this and/or give the designer a hint about this requirement?

A widget whose parent can't be a QWidget is not a widget anymore. Your design breaks the Liskov Substitution Principle and has to be fixed.
You're free to enable special functionality if the widget happens to be of a certain type, but a widget must be usable with any widget for a parent.
Thus:
CustomWidget(QWidget* parent = nullptr) :
QWidget(parent)
{
auto customParent = qobject_cast<CustomWidget*>(parent);
if (customParent)
_specialValue = customParent->specialValue();
}
or:
class CustomWidget : public QWidget {
Q_OBJECT
CustomWidget *_customParent = qobject_cast<CustomWidget*>(parent());
SpecialType _specialValue = _customParent ? _customParent->specialValue() : SpecialType();
SpecialType specialValue() const { return _specialValue; }
public:
CustomWidget(QWidget * parent = nullptr) : QWidget(parent) {}
};

Related

How to access QTextEdit "layout" to add "status bar"?

This a modified question I have posted on this forum.
It is not a repost, for two reasons - I cannot edit the other post and I am trying to
resolve this issue from another angle. Besides the other post got derailed by people who mean well but did not really read the post.
I have a working C++ code which is using (QT) QTextEdit class to collect and process text data.
The class - as its name suggest - was designed to collect and analyze text.
The text is displayed in "view " area.
My task is to select ONE word of text and drag it to another GUI object.
I like to put the text being dragged into QT standard "status bar".
Normal QT GUI widgets are designed using QTDesigner. Integral part of such design process
is "layout".
As it stands , QTextEdit DOES NOT HAVE / USE "layout" or use it but it is NOT visible / accessible when QTextEdit is implemented - there is no need for it.
In order to add "status bar" I need to MODIFY the view to hold the current text editing
"layout" and add "status bar " layout.
I am unable to figure out how to get access to the QTextEdit class GUI layout.
I am asking for help to accomplish that- how to add "status bar" to EXISTING QTextEdit.
Please read the post carefully _ I need help with how to add "status bar" to EXISTING QTextEdit.
I do have an option to replace the QTextEdit with basic "widget" class but it "breaks " the working code and I rather not do that.
I did look into setting multiple inheritance - Qwidget and QTextEdit but did not work.
PLEASE Mr Higgins , editing my post for proper English grammar and composition DOES NOT solve the problem. So , please - don't.
I'm not entirely sure but your question seems to imply that inheriting from QTextEdit would be acceptable. If that's the case then you can probably make use of the fact that QTextEdit itself inherits QAbstractScrollArea and use the viewport margins to create an area in which to show a status bar of some sort.
Consider the following code...
#include <QApplication>
#include <QScrollBar>
#include <QStatusBar>
#include <QTextEdit>
namespace {
class text_edit: public QTextEdit {
using super = QTextEdit;
public:
explicit text_edit (QWidget *parent = nullptr)
: super(parent)
, m_status(this)
{
m_status.setStyleSheet("background-color: gray;");
m_status.showMessage("Status text goes here...");
show_status(true);
horizontalScrollBar()->installEventFilter(this);
verticalScrollBar()->installEventFilter(this);
setLineWrapMode(QTextEdit::NoWrap);
}
protected:
virtual bool eventFilter (QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::Show || event->type() == QEvent::Hide)
update_status_geometry();
return super::eventFilter(obj, event);
}
virtual void resizeEvent (QResizeEvent *event) override
{
super::resizeEvent(event);
update_status_geometry();
}
private:
void show_status (bool on)
{
if (on) {
setViewportMargins(0, 0, 0, m_status.height());
m_status.show();
} else {
setViewportMargins(0, 0, 0, 0);
m_status.hide();
}
}
void update_status_geometry ()
{
/*
* Calculate initial geometry of the QStatusBar based on its size hint.
*/
auto s = m_status.sizeHint();
s.rwidth() = width();
QRect geom(QPoint(0, 0), s);
geom.moveTop(height() - s.height());
/*
* Adjust the status bar geometry to allow for the scroll bars.
*/
if (horizontalScrollBar()->isVisible())
geom.moveTop(geom.top() - horizontalScrollBar()->height());
if (verticalScrollBar()->isVisible())
geom.setRight(geom.right() - verticalScrollBar()->width());
m_status.setGeometry(geom);
}
QStatusBar m_status;
};
}
int
main (int argc, char **argv)
{
QApplication app(argc, argv);
text_edit te;
te.show();
return app.exec();
}
Running the code above results in the following widget...

What is parent for in Qt?

Almost every QtWidgets class can have parent. And usually it's optional to set parent at object initialization. For example,If I create a class that inherits QWidget class, I will do the following on the constructor:
Widget::Widget(QWidget* parent): QWidget(parent) {
hbox = new QHBoxLayout(this);
yes_button = new QPushButton("&Yes");
no_button = new QPushButton("&No", this);
cancel_button = new QPushButton("&Cancel", hbox);
}
I can set or not set parent. I can set cancel_button to be a child of hbox. I can too set cancel_button to be a child of yes_button, but I think it's a bad thing to do.
What's the point of this? And, is it really necessary to set parent for every QWidget based class that I create?
Besides helping with draw order in GUI objects, it also helps with memory management, so that when you destroy a QObject, all of it's children are destroyed too. See http://doc.qt.io/qt-4.8/objecttrees.html for more details. When something changes in the parent (e.g. when it is resized), it can notify its children to update themselves too.
To answer your question, you're not required to set the parent for everything (that's why it's an optional parameter, after all), but most of the time it's better to set it correctly.
Firstly, a QWidget is a QObject, and QObjects are nodes in a QObject tree. The child nodes are memory-managed by the parent, unless you deallocate them before the parent has a chance to do so. Thus, memory management is one reason for widgets, or any other QObjects, to have a parent.
Secondly, visible parentless widgets are always top-level windows. Conversely, it's impossible to have a non-top-level widget that is parentless. When you show a parentless widget, it acquires its own window. The opposite is not necessarily true - it's possible to give a child widget a Qt::Window flag, and it becomes a top-level window as well.
The corollary is that any widget contained in other widgets has a parent - otherwise it'd be a top-level window. This parent might not be set explicitly by you, but it's set nevertheless.
I think that your question can be rephrased as: When do I need to explicitly give widget constructor a parent? The answer is:
Whenever the widget is a top level window that you intend to have a parent. Such windows are not subject to layout management, so there's no mechanism to set that parent for you. Top-level transient dialogs need to have parents so that they are properly positioned in relation to the parent window.
Whenever you have a child widget not subject to layout management.
Widgets subject to layout management are parented upon insertion into a layout:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
QWidget window;
QVBoxLayout layout(&window);
QLabel label("Hello");
QPushButton button("Goodbye");
layout.addWidget(&label);
layout.addWidget(&button);
QObject::connect(&button, &QPushButton::clicked, [&app]{ app.quit(); });
window.show();
return app.exec();
}
Finally, not all widgets or QObjects need to be explicitly created on the heap. Since all QObject-derived classes in Qt (and many other classes, too!) use the PIMPL idiom, when you allocate them individually on the heap, you're really doing the heap allocation twice. First you allocate the instance of the class - sometimes the instance is as small as a pointer or two - and then the class's constructor allocates its PIMPL. Explicit heap allocation is a case of premature pessimization.
To avoid this pessimization, your Widget should look as follows:
class Widget : public QWidget {
Q_OBJECT
QHBoxLayout m_layout;
QPushButton m_yesButton, m_noButton, m_cancelButton;
public:
Widget(QWidget * parent = 0);
};
Widget::Widget(QWidget * parent) :
QWidget(parent),
m_layout(this),
m_yesButton("&Yes"),
m_noButton("&No"),
m_cancelButton("&Cancel")
{
m_layout.addWidget(&m_yesButton);
m_layout.addWidget(&m_noButton);
m_layout.addWidget(&m_cancelButton);
}
If you wished to use the PIMPL idiom, you could do that, too:
// Widget.h - Interface
class WidgetPrivate;
class Widget : public QWidget {
{
Q_OBJECT
Q_DECLARE_PRIVATE(Widget)
QScopedPointer<WidgetPrivate> const d_ptr;
public:
Widget(QWidget * parent = 0);
~Widget();
};
// Widget.cpp - Implementation
class WidgetPrivate {
Q_DISABLE_COPY(WidgetPrivate)
Q_DECLARE_PUBLIC(Widget)
Widget * const q_ptr;
QHBoxLayout layout;
QPushButton yesButton, noButton, cancelButton;
public:
WidgetPrivate(Widget * q);
};
WidgetPrivate::WidgetPrivate(Widget * q) {
q_ptr(q),
layout(q),
yesButton("&Yes"),
noButton("&No"),
cancelButton("&Cancel")
{
layout.addWidget(&yesButton);
layout.addWidget(&noButton);
layout.addWidget(&cancelButton);
}
Widget::Widget(QWidget * parent) :
QWidget(parent),
d_ptr(new WidgetPrivate(this))
{}
Widget::~Widget() {}
// necessary, since WidgetPrivate is unknown to the interface!
Of course, you should be using QDialogButtonBox instead of all this :)

Hide QLineEdit blinking cursor

I am working on QT v5.2
I need to hide the blinking cursor (caret) of QLineEdit permanently.
But at the same time, I want the QLineEdit to be editable (so readOnly and/or setting editable false is not an option for me).
I am already changing the Background color of the QLineEdit when it is in focus, so I will know which QLineEdit widget is getting edited.
For my requirement, cursor (the blinking text cursor) display should not be there.
I have tried styleSheets, but I can't get the cursor hidden ( {color:transparent; text-shadow:0px 0px 0px black;} )
Can someone please let me know how can I achieve this?
There is no standard way to do that, but you can use setReadOnly method which hides the cursor. When you call this method it disables processing of keys so you'll need to force it.
Inherit from QLineEdit and reimplement keyPressEvent.
LineEdit::LineEdit(QWidget* parent)
: QLineEdit(parent)
{
setReadOnly(true);
}
void LineEdit::keyPressEvent(QKeyEvent* e)
{
setReadOnly(false);
__super::keyPressEvent(e);
setReadOnly(true);
}
As a workaround you can create a single line QTextEdit and set the width of the cursor to zero by setCursorWidth.
For a single line QTextEdit you should subclass QTextEdit and do the following:
Disable word wrap.
Disable the scroll bars (AlwaysOff).
setTabChangesFocus(true).
Set the sizePolicy to (QSizePolicy::Expanding, QSizePolicy::Fixed)
Reimplement keyPressEvent() to ignore the event when Enter/Return is hit
Reimplement sizeHint to return size depending on the font.
The implementation is :
#include <QTextEdit>
#include <QKeyEvent>
#include <QStyleOption>
#include <QApplication>
class TextEdit : public QTextEdit
{
public:
TextEdit()
{
setTabChangesFocus(true);
setWordWrapMode(QTextOption::NoWrap);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setFixedHeight(sizeHint().height());
}
void keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
event->ignore();
else
QTextEdit::keyPressEvent(event);
}
QSize sizeHint() const
{
QFontMetrics fm(font());
int h = qMax(fm.height(), 14) + 4;
int w = fm.width(QLatin1Char('x')) * 17 + 4;
QStyleOptionFrameV2 opt;
opt.initFrom(this);
return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h).
expandedTo(QApplication::globalStrut()), this));
}
};
Now you can create an instance of TextEdit and set the cursor width to zero :
textEdit->setCursorWidth(0);
Most straight forward thing I found was stolen from this github repo:
https://github.com/igogo/qt5noblink/blob/master/qt5noblink.cpp
Basically you just want to disable the internal "blink timer" Qt thinks is somehow good UX (hint blinking cursors never were good UX and never will be - maybe try color or highlighting there eh design peeps).
So the code is pretty simple:
from PyQt5 import QtGui
app = QtGui.QApplication.instance()
app.setCursorFlashTime(0)
voilĂ .
Solution in python:
# somelibraries
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.setFocus() # this is what you need!!!
container = QWidget()
container.setLayout(self.layout)
# Set the central widget of the Window.
self.setCentralWidget(container)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
I ran into the same problem but setReadOnly is not a viable option because it alters the UI behavior in other places too.
Somewhere in a Qt-forum I found the following solution that actually solves the problem exactly where it occurs without having impact on other parts.
In the first step you need to derive from QProxyStyle and overwrite the pixelMetric member function:
class CustomLineEditProxyStyle : public QProxyStyle
{
public:
virtual int pixelMetric(PixelMetric metric, const QStyleOption* option = 0, const QWidget* widget = 0) const
{
if (metric == QStyle::PM_TextCursorWidth)
return 0;
return QProxyStyle::pixelMetric(metric, option, widget);
}
};
The custom function simply handles QStyle::PM_TextCursorWidth and forwards otherwise.
In your custom LineEdit class constructor you can then use the new Style like this:
m_pCustomLineEditStyle = new CustomLineEditProxyStyle();
setStyle(m_pCustomLineEditStyle);
And don't forget to delete it in the destructor since the ownership of the style is not transferred (see documentation). You can, of course, hand the style form outside to your LineEdit instance if you wish.
Don't get complicated:
In QtDesigner ,
1.Go the the lineEdit 's property tab
2.Change focusPolicy to ClickFocus
That's it...

Custom Qt designer widget : a scroll Area containing a custom vertical layout

I want to do something fairly simple : add a custom widget to Qt designer that would basically be a scrollArea containing a custom vertical layout(I added some code to the vertical layout in order to handle its objects for my projects).
The idea would be to represent a vertical menu that would be on the side of my screen
What I have done so far
I created the custom widget plugin and my custom layout.
My custom widget codes looks like this:
#include "menuwidget.h"
MenuWidget::MenuWidget(QWidget *parent) :
QScrollArea(parent)
{
this->setWidgetResizable(true);
QWidget* layoutHoldingWidget= new QWidget(this);
layout= new MenuLayout();
layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
layout->addStretch(1);
layoutHoldingWidget->setLayout(layout);
this->setWidget(layoutHoldingWidget);
}
If I add manually to the layout (in the constructor code) some buttons
for(int i =0;i<20;i++)
layout->addWidget(new QPushButton(this));
It does work and I can see my scrollArea containing some buttons, which is almost what I want.
What I want
I would like to be able to add these buttons directly via Qt designer: the user would first drag the empty MenuWidget on the main window, then would drag QPushButtons on my custom widget exactly like he would do on a regular vertical layout.
Is that possible?How could I do such a thing?
Thank you ! :)
Edit 1
What I was missing was the "scrollAreaWidgetContents" widget that is always created when you drag and drop a QScrollArea. I did a similar thing by adding a widget (let's call it containerWidget) to my custom scrollArea in its domXml function, which enables me to drag and drop widgets on my scroll Area like I wanted to do.
BUT there's still something I can't figure out : I want the containerWidget to have a customLayout (myCustomLayout) . If I add it in the domXml function, I get the following line in the terminal :
Designer:The layout type 'MyCustomLayout' is not supported,
defaulting to grid.
So it means that I can't tell designer to use my custom layout to place my widgets, which is kind of sad :D
Is there any way to "cheat" here?
There are two things to consider:
1) Overwrite in the class you derive from QDesignerCustomWidgetInterface the function to return true
bool isContainer() const { return true; }
This tells QtDesigner that the widget can contain children. (In Qt nearly any Widget can contain any widget as child, but QtDesigner tries to restrict it in a sensible way - e.g. you cant add children to a QLabel in QtDesigner)
2) Implement childEvent of your Widget. Probably in your case it would add Widgets added in QtDesigner to a layout.
Here is a core I've implemented to try this out. I've created a skeleton using "Qt Widget Plugin" Wizard in QtCreator and modified a little bit.
Don't forget to build as release, for the compiler/Qt-version of your QtDesigner , to copy the .dll and .lib files in \plugins\designer directory and to restart QtDesigner!
verticalplugin.cpp
//all other functions remained as created by QtCreator wizard
bool VerticalMenuPlugin::isContainer() const
{
return true;
}
VerticalMenu.h
#ifndef VERTICALMENU_H
#define VERTICALMENU_H
#include <QtGui/QWidget>
#include <QtGui/QVBoxLayout>
class VerticalMenu : public QWidget
{
Q_OBJECT
protected:
virtual void childEvent ( QChildEvent * event );
public:
VerticalMenu(QWidget *parent = 0);
};
#endif
VerticalMenu.cpp
#include "verticalmenu.h"
#include <QChildEvent>
VerticalMenu::VerticalMenu(QWidget *parent) :
QWidget(parent)
{
setLayout (new QVBoxLayout);
}
void VerticalMenu::childEvent ( QChildEvent * event )
{
if ( event->added() )
{
QWidget * newChild = qobject_cast<QWidget *>(event->child());
if ( newChild )
{
layout()->addWidget( newChild );
}
}
}
I hope' it would help as a starting point.
Qt 4 does not support custom layout plugins for designer, so I couldn't achieve what I wanted to do. I will instead use a Vertical Layout and try to implement the additional features that were supposed to be in the custom layout code in the widget code.

Qt/win: showMaximized() overlapping taskbar on a frameless window

I'm building an Qt-Application without the default window border as a frameless window.
The window functions are included by setting window flags in the QMainWindow like:
MainDialog::MainDialog(QWidget *parent):
QMainWindow(parent), currentProject(NULL), currentUser(NULL),
aViews(new QList<AViewForm*>()),
bViews(new QList<BViewForm*>()),
cViews(new QList<CViewForm*>())
{
ui.setupUi(this);
this->statusBar()->showMessage(tr(""));
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowSystemMenuHint);
...
}
The MainWindow has an .ui file within, thats why I cannot inherit from QDesktopWidget.
The Problem I have now is that the Appication overlays the windows taskbar when maximizing.
My Question now: is there any posibility to find out the available height of the OS desktop without the
availableGeometry().height()
-Method of QDesktopWidget? I cannot find anything in the documentation :(
Somebody else here asked a similar Question but used a QWidget instead of a QMainWindow.
I would be glad about any hints to my Problem
As you say you can use QDesktopWidget. If you don't have your class inherit from it you can create one in your constructor just for retrieving the height :
QDesktopWidget w;
int availableHeight = w.availableGeometry().height();
Guess thats not good practise, but i solved it as followed:
I built a new class which needs a MainWindow as param and with slots for the scaling actions:
FullScreen::FullScreen(QMainWindow &mainWindow, QObject *parent) : QObject(parent), mainWindow(mainWindow)
{
this->saveCurrentPosition();
}
void FullScreen::maximize()
{
this->saveCurrentPosition();
mainWindow.move(QApplication::desktop()->mapToGlobal(QApplication::desktop()->availableGeometry().topLeft()));
mainWindow.resize(QApplication::desktop()->availableGeometry().size());
}
void FullScreen::normalize()
{
mainWindow.move(lastGlobalPosition);
mainWindow.resize(lastSize);
}
void FullScreen::saveCurrentPosition()
{
lastGlobalPosition = mainWindow.mapToGlobal(mainWindow.rect().topLeft());
lastSize = mainWindow.size();
}
The only Problem which now occures is when the application is fullscreen and you move the taskbar. I have not set any resizeEvent though

Resources