gtkmm: add widgets that are not fields of class - c++

I would like to add to gtkmm widgets that are not fields of class (I declare them in method).
I tried this:
class MyWidget : public Gtk::Window {
public:
MyWidget();
virtual MyWidget();
}
MyWidget::MyWidget() {
Gtk::Label label("my label");
add(label);
label.show();
}
But it does not shwo anthing.
But when i declare label in class and extend method by it, it works:
class MyWidget : public Gtk::Window {
public:
MyWidget();
virtual MyWidget();
protected:
Gtk::Label label;
}
MyWidget::MyWidget() : label("my label") {
add(label);
label.show();
}
What am I doing wrong?

A widget created in such way is destroyed with the closing bracket of the method. Please read this tutorial about memory management.
Personally I'd propose using dynamic allocation with make_managed() and add(). It's very easy to use and I have never had any problems when using that.
MyWidget::MyWidget() {
auto* label = Gtk::make_managed<Gtk::Label>("my label");
add(*label);
label->show();
}

Related

Linking a QWidget to another QWidget new created

I am try to, by pressing a button in the main QWidget, to create a new QWidget. In that new created QWidget I want to have a button connected to a slot of the main QWidget.
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI* s=new SmallGUI{};
s->show();
});
}
void something(){
//something
}
and the second class
class SmallGUI1 :
public QWidget
{
public:
SmallGUI(){connection();};
private:
QPushButton* N =new QPushButton;
void connection(){
//to connect N to something()
}
I want to connect N to something() .
Before we start, there are some other problems with you code.
Note that in your second class, the constructor is not named the same as the class, which will cause some... Problems.
You also forgot to put a parent for your buttons (which may thus cause some unexpected results) AND for your Widgets (which is again not a good idea).
So, that being said, let us get to the main topic.
I tend to only put prototypes and declare the attributes in the .h file to make the code clearer, but you may of course adapt it to your needs or to your own programming convention.
There are several ways to do something like this, but the simplest one should look like this :
SmallGUI1.h :
#include "UI.h" //The file in which the class UI is declared
//OR :
//class UI; //If you need to include this file in UI.h
class SmallGUI1 : public QWidget{
Q_OBJECT //Q_OBJECT macro, check the doc for more explainations about it
public:
explicit SmallGUI1(UI *parent = nullptr); //Explicit means that this constructor cannot be used for implicit casts
~SmallGUI1();//Destructor needed because I only put the constructor above
private:
QPushButton* N; //Not very good looking to initialize attributes in the .h in my opinion, but works fine.
}
SmallGUI1.cpp :
SmallGUI1::SmallGUI1(UI *parent) : QWidget(parent){
N = new QPushButton(tr("Some text on the button") , this); //tr to enable translation on this string
//************* If I understood your question correctly, this is what you are looking for *************
connect(N , &QPushButton::clicked , parent , &UI::doSomething); //Select the signal you want
/*
Some code here
*/
show();
}
SmallGUI1::~SmallGUI1(){qDeleteAll(children());}
UI.h :
class UI : public QWidget{
Q_OBJECT
public:
explicit UI(QWidget *parent = nullptr);
~UI();
private:
QPushButton* all;
private slots :
void createSmallGUI1();
void doSomething();
}
UI.cpp :
#include "SmallGUI1.h"
UI::UI(QWidget *parent) : QWidget(parent){
all = new QPushButton(tr("ALL") , this);
connect(all , &QPushButton::clicked , this , &UI::createSmallGUI1);
/*
Some code here
*/
}
UI::~UI(){qDeleteAll(children());}
void UI::createSmallGUI1(){SmallGUI1 *gui = new SmallGUI1(this);}
void UI::doSomething(){
/*
Clever code here
*/
}
You can define the second widget as a child of the main widget to make things easier:
class UI : public QWidget {
...
private:
SmallGUI* s;
...
and then initialize it in the UI constructor, along with your all button. You can initially hide the child widget or disable it:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
and 'show' it with button clicked signal
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
Doing so gives you the option to connect the clicked signal of your N button to the something function in the parent class
connect(s->N, &QPushButton::clicked, this, &UI::something);
The complete program would be as follows,
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
#include <QWidget>
class SmallGUI : public QWidget {
public:
SmallGUI(QWidget* parent) : QWidget(parent) {
N = new QPushButton{"btn2", this};
connection();
};
QPushButton* N;
private:
void connection(){};
};
class UI : public QWidget {
public:
UI() {
all = new QPushButton{"ALL", this};
setWindowTitle("UI"); // just for clarification
s = new SmallGUI(this);
s->hide();
connection();
};
private:
SmallGUI* s;
QPushButton* all;
void connection() {
connect(all, &QPushButton::clicked, s, &SmallGUI::show);
connect(s->N, &QPushButton::clicked, this, &UI::something);
}
void something() { QMessageBox::information(this, "Hello", "Hello"); }
};
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
UI w;
w.show();
return a.exec();
}
It is not good idea to connect to parent's slots from "nested" class, since SmallGUI1 will be tied to class UI.
Here is better solution, I think:
class UI : public QWidget
{
public:
UI(){connection();};
private:
QPushButton* all = new QPushButton{ "ALL" };
void connection(){
QObject::connect(all,QPushButton::clicked,[](){
SmallGUI1* s=new SmallGUI1;
connect(s,&USmallGUI1::button_clicked,this,&UI::something);
s->show();
});
}
void something(){
//something
}
And SmallGUI1 class:
class SmallGUI1 :
public QWidget
{
public:
SmallGUI1(){connection();};
signals:
void button_clicked();
private:
QPushButton* N;
void connection(){
//to connect N to something()
N = new QPushButton;
connect(N,&QPushButton::clicked,this,&SmallGUI1::button_clicked)
}
This way, you are connecting QPusButton::clicked signal from SmallGUI1 to the signal SmallGUI1::button_clicked(). Dont need to implement additional slot, just connect signal to signal.
And in UI you are connecting button_clicked() signal to the slot dosomething()
DONT FORGET THE CONSTRUCTOR OF SmallGUI1! In your code, SmallGUI() will be just a method, which will not be called when SmallGUI1 is instantiated, and you have to call it by yourself.

How to set visibility on widgets within a subclass of QWidgetAction

Qt 5.6.3, eglfs Linux platform.
I have a selection of classes derived from QWidgetAction. The QWidgetActions are all parented from a menu, and the widgets they contain are parented from the same menu. The contained widgets are all set as the default widget for the QWidgetAction. Nothing has been reimplemented from QWidgetAction.
I thought that setting the visibility of the QWidgetAction would automatically set the visibility of the custom widget set contained within? Is this not true, as doing so is certainly not showing and hiding the widgets as required!? Must I do something else to pass the visibility change to the contained widgets? Must I directly request the widget from the QWidgetAction and then apply visibility to it directly (which seems like a hack)?
I'm interested in how the QWidgetActions are supposed to be implemented. The documentation is almost non-existent, so I'm after peoples experience with them as much as anything. I have intermittent issues with what looks like a double delete of a custom widget and visibility not behaving as it should.
class Base : public QWidgetAction
{
Q_OBJECT
public:
explicit Base(QWidget* parent, QString labelText = "", QString iconPath = "", Qt::AlignmentFlag alignment = Qt::AlignHCenter) :
QWidgetAction(parent),
mCustomWidget(nullptr),
mParentWidget(nullptr),
mTextLabel(nullptr),
mAlignment(alignment),
mLabelText(labelText),
mIconPath(iconPath) {}
virtual ~Base() {}
protected:
QWidget *mCustomWidget;
QWidget *createTheWidgetSet(QWidget *parent)
{
if (mParentWidget == nullptr) {
mParentWidget = new QWidget(parent);
mCustomWidget = createCustomWidget(mParentWidget);
if (mCustomWidget != nullptr) {
if (!mLabelText.isEmpty()) {
mCustomWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
}
}
int rightMargin = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, mParentWidget);
layout->setContentsMargins(1, 2, rightMargin, 2);
if (!mLabelText.isEmpty()) {
QString some_calced_text{};
mTextLabel = new QLabel(some_calced_text, mParentWidget);
layout->addWidget(mTextLabel);
} else {
if(mAlignment == Qt::AlignLeft){
int some_calced_val{20};
layout->addSpacing(some_calced_val);
}
}
if(mAlignment == Qt::AlignRight){
layout->addStretch();
}
layout->addWidget(mCustomWidget);
if(mAlignment == Qt::AlignLeft){
layout->addStretch();
}
}
setDefaultWidget(mParentWidget);
return mCustomWidget;
}
virtual QWidget *createCustomWidget(QWidget *parent) = 0;
private:
Q_DISABLE_COPY(Base)
QWidget *mParentWidget;
QLabel *mTextLabel;
Qt::AlignmentFlag mAlignment;
QString mLabelText;
QString mIconPath;
};
class SpinBoxActionWidget : public Base
{
Q_OBJECT
public:
explicit SpinBoxActionWidget(QWidget* parent, QString labelText = "", QString iconPath = "") :
Base(parent, labelText, iconPath),
mSpinBox(nullptr)
{
createTheWidgetSet(parent);
}
virtual ~SpinBoxActionWidget() {}
QSpinBox* getSpinBox() const
{
return mSpinBox;
}
protected:
QWidget *createCustomWidget(QWidget *parent) override
{
if (mSpinBox == nullptr) {
mSpinBox = new QSpinBox(parent);
mSpinBox->setFixedHeight(22);
}
return mSpinBox;
}
private:
Q_DISABLE_COPY(SpinBoxActionWidget)
QSpinBox *mSpinBox;
};
/* Elsewhere in code.... */
{
QMenu theMenu = new QMenu(parentWindow);
SpinBoxActionWidget theAct = new SpinBoxActionWidget(theMenu);
SpinBoxActionWidget theSecondAct = new SpinBoxActionWidget(theMenu);
theMenu->addAction(theAct);
theMenu->addAction(theSecondAct);
/* I now assume that I can do this, and the entire entry in the menu
* represented by "theAct" can be made visible and invisible.
* This doesn't work however, either the widget remains visible,
* or is partially hidden.
theAct->setVisible(true);
theAct->setVisible(false);
*/
}
You are not reimplementing the interface, that's why it doesn't work.
First, note that QWidgetAction derives from QAction which is not a QWidget; however, it does have a setVisible() function, which will actually just forward the call to all widgets created by the action.
You have to reimplement QWidgetAction::createWidget(parent) to add a new widget; your createCustomWidget was doing nothing useful. Here is a very simple example:
class SpinAction : public QWidgetAction
{
Q_OBJECT
public:
SpinAction(QObject* parent) : QWidgetAction(parent) {}
virtual ~SpinAction() {}
QWidget* createWidget(QWidget* parent) { return new QSpinBox(parent); }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// reimplement this function
};
You can add your action to whatever container you want, menus, toolbars, etc... This example will create a new widget for each container, and these created widgets won't be synchronized (for example on the spinbox value).
I just tested it in a main window, with a widget action added to a menu and a toolbar, and calling setVisible() works flawlessly.

How to call up a window only once with Qt

I want to know if there is any other way to call up a window once and not to call the desired window over again. This is the code I used :
void secondWindow::on_pushButton_clicked()
{
MainWindow *mainwindow;
mainwindow = new MainWindow(this);
mainwindow->show();
}
When you click the pushButton, it keeps on reproducing a new window. So is there a way it can show the same MainWindow whenever the pushButton is clicked?
I would solve this problem in one of the following ways:
1) Disable the pushButton button once it is clicked, and the MainWindow is created,
2) Declare the pointer to MainWindow as a secondWindow class' member variable:
class secondWindow : public ...
{
public:
secondWindow()
{
m_mainWindow = new MainWindow(this);
}
private slots:
void secondWindow::on_pushButton_clicked()
{
m_mainwindow->show();
}
private:
MainWindow *m_mainWindow;
};
Following will do what you want
void secondWindow::on_pushButton_clicked()
{
static MainWindow* mainwindow = new MainWindow(this);
mainwindow->show();
mainwindow->raise();
}
You should only initialize a new object once. Otherwise you only have to display the Window object.
class MainWindowFactory {
private:
MainWindow* mainWindow;
public:
MainWindowFactory();
MainWindow* getMainWindow();
};
MainWindowFactory::MainWindowFactory() {
mainWindow = NULL;
}
MainWindow* MainWindowFactory::getMainWindow() {
if (!mainWindow) {
mainWindow = new MainWindow(this);
}
mainWindow->show();
}

gtkmm - multiple windows/popup window

I've been searching a lot and I still can't find a good example of how to have multiple windows inside the same application with GTK. My program is in C++ but I don't mind an example in C which would help me understand the principle anyway.
So, the basic idea is to create my own derived object from Gtk::Window as opposed to Gtk::Dialog. Dialog has a run method which works flawlessly to open a modal popup window, but it's not flexible enough for what I'm trying to do. Does anyone know how I'd go about spawning a new window when I click a button in my program?
For example:
void MainWindow::on_button_clicked()
{
NewWindow window;
//Some code to display that window and stay in a loop until told to return
}
Where NewWindow is derived from Gtk::Window as such:
class NewWindow : public Gtk::Window
{
//Normal stuff goes here
}
Anything will help...I'm really confused here!
Another way to have a new window is to create a pointer to a Gtk window variable(Gtk::Window* about_window_;) then set the Gtk window variable to a new instance of the other window (about_window_ = new Window;), after that show the new window (about_window_->show();). Below is a full example of this:
class AboutWindow : public Gtk::Window
{
public:
AboutWindow();
~AboutWindow();
protected:
Gtk::Label lbl_;
};
AboutWindow::AboutWindow()
{
this->set_default_size(100, 100);
this->set_title("About");
lbl_.set_label("About label");
this->add(lbl_);
this->show_all_children();
}
AboutWindow::~AboutWindow()
{
}
class MainWindow : public Gtk::Window
{
public:
MainWindow();
virtual ~MainWindow();
protected:
void onButtonClicked();
void aboutWinClose();
Gtk::Button button_;
Gtk::Label lbl_;
Gtk::Box box_;
Gtk::AboutWindow* aboutw_;
};
MainWindow::MainWindow()
{
this->set_default_size(100, 100);
box_.set_orientation(Gtk::ORIENTATION_VERTICAL);
this->add(box_);
box_.pack_start(lbl_);
lbl_.set_label("a test");
button_.set_label("Open About Window");
box_.pack_end(button_);
button_.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onButtonClicked));
aboutw_ = 0;
this->show_all_children();
}
MainWindow::~MainWindow()
{
}
void MainWindow::onButtonClicked()
{
if(aboutw_ != 0)
return;
aboutw_ = new AboutWindow;
aboutw_->signal_hide().connect(sigc::mem_fun(*this, &MainWindow::aboutWinClose));
aboutw_->show();
}
void MainWindow::aboutWinClose()
{
aboutw_ = 0;
}
Added for reference.
If you don't want the new window to be modal, then simply create it, show() it, and return from your main window's method without entering a loop.

Adding new functionality to a class

I'm studying the Qt4 library and I want to add some functionality to all the children of QWidget in my project. I need all widgets to have mousePressEvent overridden. Obviously I do not need to override it in every particular widget I use(I use many of them and I they to follow DRY rule) , instead I create a Foo class:
class Foo : public QWidget
{
Q_OBJECT
protected:
/* implementation is just a test */
virtual void mousePressEvent(QMouseEvent*) { this->move(0,0); }
};
And derive my button from it:
class FooButton : public QPushButton , public Foo
{
public:
FooButton (QWidget* parent) : QPushButton(parent) { };
};
But the event seems to be not overridden... What am I doing wrong?
Thanks in advance.
For the mousePressEvent access, try QObject::installEventFilter().
http://doc.qt.digia.com/stable/qobject.html
You are inheriting twice now from QWidget. This is problematic (see diamond problem).
Instead, drop your Foo class and move your custom mouse press event handler to your FooButton class:
class FooButton : public QPushButton
{
Q_OBJECT
protected:
/* implementation is just a test */
virtual void mousePressEvent(QMouseEvent*) { this->move(0,0); }
public:
FooButton (QWidget* parent) : QPushButton(parent) { };
};
If this doesn't suit the design of your application, then try using virtual inheritance instead. That is, change Foo to:
class Foo : public virtual QWidget
If that doesn't help, you should follow the advice of the other answers and install an event handler.
(You can read about virtual inheritance in its Wikipedia article.)
I suspect that the problem is that you're inheriting from QWidget twice, once through QPushButton and once through Foo.
From the way you phrased the question, I'm assuming that you want to do this for varying kinds of widgets, and thus don't want to have to subclass QPushButton, QLabel, QCheckBox, etc. If this is not the case then you should use Nikos's answer.
If not, your best bet is probably doing to be to use an event filter.
class MousePressFilter : public QObject {
Q_OBJECT
public:
MousePressFilter(QObject *parent) : QObject(parent) { }
protected:
bool eventFilter(QObject *watched, QEvent *event) {
QWidget *widget = dynamic_cast<QWidget*>(watched);
widget->move(0,0);
return false;
}
};
And then in your Foo class constructor:
class Foo {
Foo() {
installEventFilter( new MousePressFilter(this) );
}
};