I'm working on an application that uses a MDI. I have a bunch of toolbox and menu bar defined in a main window containing the QMDIArea.
All subwindows are of the same class. In order to connect the buttons to the active sub window, I did the following think:
void MainWindow::zoomOut() {
QMdiSubWindow* sub_window = central_document_interface->currentSubWindow();
if (sub_window) {
PlanWindow* plan_window = (PlanWindow*)(sub_window->widget());
plan_window->zoomOut();
}
}
I think it would be smarter to reconnect the signals using the subWindowActivated signal. But the problem I have is that I intend to have several types of sub window (different classes). All signal aren't used by all these classes.
I can't manage to find a clean way to differentiate them and connect or not the signals according to their class. How would you do this ?
I created this code for my project:
void MainWindow::slot_menuEditZoomOut() {
WindowAreaManagerInterface::instance()->
LambaOnCurrentCustomWindow<CAbstractZoomAction>([](CAbstractZoomAction *zoom){ zoom->zoomOut(); });
}
Where the WindowAreaManagerInterface is
class WindowAreaManagerInterface : public QMdiArea {
Q_OBJECT
public:
static WindowAreaManagerInterface *instance();
template<class T>
T *currentCustomWindow() {
QWidget *widget = 0;
QMdiSubWindow *subWindow = currentSubWindow();
if (subWindow) widget = subWindow->widget();
return dynamic_cast<T *> (widget);
}
template<class T, typename Func>
void LambaOnCurrentCustomWindow(Func F) {
T *window = currentCustomWindow<T>();
if (window)
F(window);
}
//other methods follow..
}
Hope this help.
Related
Goal
I want to benchmark some drawing libraries and gather diagnostics. I thought of implementing in each benchmark the same interface and then instantiating different classes in the main window.
The Challenge
The problem is that some benchmarks use QWidget and other QOpenGLWidget. So even if I implement the same functionality I can not use it, without dynamic casting to each possible instance.
What I tried so far
My first thought was to create an interface and use virtual multiple inheritance. But that doesn't seem to work and I am not sure if that's even the right solution.
I also thought of the Composite Pattern or Adapter Pattern, but seem some problems, as I want to override some functions of QWidget like resizeEvent in each benchmark. Of course I could duplicate that code or put it into some non-member function. But maybe there is something more elegant?
QWidgets Addin
class BenchmarkAddin : virtual public QWidget {
public:
BenchmarkAddin() {
connect(&timer_, SIGNAL(timeout()), this, SLOT(update()));
}
double get_fps() {
// use frames_ and time to estimate fps
}
void count_frame() {
++frames_;
}
void set_parameters(int param1_) {
param1_;
}
protected:
void resizeEvent(QResizeEvent* event) override {
init();
}
virtual void init() = 0;
int param1_;
private:
int frames_;
QTimer timer_;
}
Raster Benchmark
class RasterBenchmark : public BenchmarkAddin {
protected:
void init() override {
// create buffers
}
void paintEvent(QPaintEvent* event) override {
// do drawing using param1_
count_frame();
}
}
OpenGL benchmark
class OpenGLBenchmark : virtual public QOpenGLWidget, public BenchmarkAddin {
protected:
void paintGL() override {
// do GL drawing using param1_
count_frame();
}
}
Main Window Example Usage
BenchmarkAddin *widget;
if (benchmark == "raster") {
widget = new RasterBenchmark(this);
else
widget = new OpenGLBenchmark(this);
widget.set_parameters(100);
...
std::cout << widget.get_fps() << std::endl;
Obviously this doesn't work, as QOpenGLWidget doesn't use virtual inheritance for QWidget. Also there is a problem with Qt's object meta system.
Question:
Any idea how I could implement an interface that is both accessible within a subclass of QWidget and QOpenGLWidget?
In my application I have a QDialog which itself contains a complex, QWidget-derived GUI element. The QDialog is modal and opened with exec() and the embedded GUI element handles all user interactions.
So only this child QWidget knows when the QDialog can be closed, which is done this way:
QDialog* parent=qobject_cast<QDialog*>(parentWidget());
if (parent) parent->close();
This is necessary because the QDialog has to be closed and not only the QWidget.
Now a user reported a situation where QDialog::exec() has returned but where the dialog (or only the GUI element?) was still visible. From the log files I can see QDialog::exec() really has returned and the code right after this call was executed.
So my current assumption: the GUI element has lost its parent so that the close() call shown above was not called because "parent" was null.
Any idea how this can happen? Is there a regular way where the parent of a QWidget can disappear?
Generally speaking, using QDialog::exec to reenter the event loop will cause trouble, because suddenly all the code that runs in the main thread must be reentrant. Most likely you're facing fallout from that. Don't reenter the event loop, and you'll be fine or the problem will become reproducible.
If you need to react to the dialog being accepted or rejected, connect code to the relevant slots. I.e. change this:
void do() {
MyDialog dialog{this};
auto rc = dialog.exec();
qDebug() << "dialog returned" << rc;
}
to that:
class Foo : public QWidget {
MyDialog dialog{this};
...
Foo() {
connect(&dialog, &QDialog::done, this, &Foo::dialogDone);
}
void do() {
dialog.show();
}
void dialogDone(int rc) {
qDebug() << "dialog returned" << rc;
}
};
or, if you want to lazily initialize the dialog:
class Foo : public QWidget {
MyDialog * m_dialog = nullptr;
MyDialog * dialog() {
if (! m_dialog) {
m_dialog = new MyDialog{this};
connect(m_dialog, &QDialog::done, this, &Foo::dialogDone);
}
return m_dialog;
}
...
void do() {
dialog()->show();
}
void dialogDone(int rc) {
qDebug() << "dialog returned" << rc;
}
};
It is a horrible antipattern for the child widget to attempt to meddle with the parent. The knowledge that the widget has a parent should not leak into the widget, it should be localized to the parent. Thus, the child widget should emit a signal that indicates that e.g. the data was accepted. When you create the dialog, connect this signal to the dialog's accept() or close() slots:
class MyWidget : public QWidget {
Q_OBJECT
public:
Q_SIGNAL void isDone();
...
};
class MyDialog : public QDialog {
QGridLayout layout{this};
MyWidget widget;
public:
MyDialog() {
layout.addWidget(&widget, 0, 0);
connect(&widget, &MyWidget::isDone, this, &QDialog::accepted);
}
};
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.
I am writing for the Qt framework and I want to create a QWidget. QWidget exposes some interfaces such as mousePressEvent and mouseReleaseEvent. I want to extend this class to allow mouseClickEvent and mouseDoubleClickEvent. Normally, you would think to extend QWidget and implement those functions. The problem is that there are other classes (QPushButton for example) provided as part of the library which extend QWidget. Therefore, how do I get that added functionality into those classes without having to extend each one and copy the code?
class ClickHandeler : public QWidget {
virtual void mouseClickEvent();
virtual void mouseDoubleClickEvent();
int clickCount; //initialized to 0;
void mouseReleaseEvent(QEvent *event){
clickCount++;
QTimer::singleShot(500, this, SLOT(checkClick()));
}
void checkClick(){
if (clickCount == 2){
this->mouseDoubleClickEvent();
clickCount = clickCount-2;
} else {
this->mouseDoubleEvent()
clickCount--;
}
}
}
// QPushButton inherits QWidget too! Yikes!
class MyPushButton : public QPushButton, public ClickHandeler {
void mouseClickEvent(){
alert("i have been clicked");
}
void mouseDoubleClickEvent(){
alert("i have been double clicked");
}
}
I want something like MyPushButton, but I am worried that the function overriding will not work as expected.
Im sorry if this question is obvious to people, but I do not know what the terminology is. I have googled interfaces for c++ and I get abstract interfaces (which doesnt properly solve this problem). If I'm just being stupid and need to know a better term for google, let me know in the comments and Ill remove the question.
I think you just need to extend the QWidget and call the QWidget same functions.
For example:
class QWidget {
public:
void mouseClickEvent();
};
class QPushButton {
public:
void mouseClickEvent();
};
class MyWidget : public QWidget, public QPushButton {
public:
void mouseClickEvent()
{
//base::mouseClickEvent();// ambiguous
QWidget::mouseClickEvent();
QPushButton::mouseClickEvent();
/* Do your own code here */
}
};
I have two widgets defined as follows
class mainWindow : public QWidget
{
Q_OBJECT
public:
mainWindow();
void readConfig();
private:
SWindow *config;
QVector <QString> filePath;
QVector <QLabel*> alias,procStatus;
QVector <int> delay;
QGridLayout *mainLayout;
QVector<QPushButton*> stopButton,restartButton;
QVector<QProcess*> proc;
QSignalMapper *stateSignalMapper, *stopSignalMapper, *restartSignalMapper;
public slots:
void openSettings();
void startRunning();
void statusChange(int);
void stopProc(int);
void restartProc(int);
void renew();
};
class SWindow : public QWidget
{
Q_OBJECT
public:
SWindow(QWidget *parent=0);
void readConfig();
void addLine(int);
private:
QVector<QPushButton*> selectButton;
QVector<QLabel*> filePath;
QVector<QLineEdit*> alias;
QSignalMapper *selectSignalMapper;
QVector<QSpinBox*> delay;
QGridLayout *mainLayout;
public slots:
void selectFile(int);
void saveFile();
void addLineSlot();
};
when i create and display SWindow object from mainWindow like this
void mainWindow::openSettings()
{
config = new SWindow();
config->show();
}
everything is ok, but now i need to access the mainWindow from SWindow, and
void mainWindow::openSettings()
{
config = new SWindow(this);
config->show();
}
doesn't display SWindow. How can i display SWindow?
How do i call a function on widget close?
By default a QWidget isn't a window. If it is not a window and you specify a parent, it will be displayed inside the parent (so in your case it is probably hidden by other widgets inside your mainWindow).
Look at windowFlags() too. Or you could make your SWindow inherit from QDialog, depending on what you use it for.
As for calling a function on widget close : you could reimplement closeEvent().
When you do config = new SWindow(this); you're setting the parent of config to be the instance of mainWindow.
This means config is no longer a top-level widget, therefore it won't display outside the mainWindow instance (specifically, it would need to be the central widget or inside the mainWindow instance's layout to be displayed).
EDIT: Sorry - I missed your last question; How do i call a function on widget close
You will want to override the QWidget::closeEvent(QCloseEvent *event) method. This gets called when you close a top-level widget. The most practical thing to do is emit() a signal so that another class can handle it having been closed.
As noted by Leiaz, you can use the windowsFlags flag when you create the widget. It would look like this:
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
config->show();
}
To reimplement the closeEvent:
header:
protected:
virtual void closeEvent ( QCloseEvent * event )
cpp:
void sWindow::closeEvent(QCloseEvent *event)
{
this->parentWidget()->SomeFunction();
qWidget::closeEvent(event);
}
However, its probably better to use signal/slots for your case here. Since you said you want to call the parent's renew method on some button click in sWindow, what you want is to EMIT a signal everytime the button is clicked, and connect this signal in the parent with the parent's refresh slot.
void sWindow::sWindow()
{
...
connect(ui.button, SIGNAL(clicked()), this, SLOT(btnClicked()));
}
void sWindow::btnClicked()
{
// whatever else the button is supposed to do
emit buttonClicked();
}
and in your parent class
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
connect(config, SIGNAL(buttonClicked()), this, SLOT(refresh()));
config->show();
}