I'm using the QMdiArea in Qt 4.4.
If a new project is created, I add a number of sub windows to a QMdiArea. I'd like to disallow the user to close a sub window during runtime. The sub windows should only be closed if the whole application is closed or if a new project is created.
How can I do this?
You need to define your own subWindow. create a subclass of QMdiSubWindow and override the closeEvent(QCloseEvent *closeEvent). you can control it by argument. for example:
void ChildWindow::closeEvent(QCloseEvent *closeEvent)
{
if(/*condition C*/)
closeEvent->accept();
else
closeEvent->ignore(); // you can do something else, like
// writing a string in status bar ...
}
then subclass the QMdiArea and override QMdiArea::closeAllSubWindows () like this:
class MainWindowArea : public QMdiArea
{
Q_OBJECT
public:
explicit MainWindowArea(QWidget *parent = 0);
signals:
void closeAllSubWindows();
public slots:
};
// Implementation:
MainWindowArea::closeAllSubWindows()
{
// set close condition (new project is creating, C = true)
foreach(QMdiSubWindow* sub,this->subWindowList())
{
(qobject_cast<ChildWindow*>(sub))->close();
}
}
you may also need to override close slot of your mdi area.
You'd do this the same as for a top-level window: process and ignore the QCloseEvent it sent. QMdiArea::closeActiveSubWindow/QMdiArea::closeAllSubWindows just call QWidget::close, which sends a closeEvent and confirms that it was accepted before proceeding.
You can process this event by subclassing QMdiSubWindow and reimplementing QWidget::closeEvent, or by using an event filter to intercept it..
Related
I have written a basic image viewing application using Qt and C++, i.e. I have a class
ImageViewApp : public QMainWindow, private Ui::ImageViewer {
Q_OBJECT
public:
ImageViewApp ( const char * InputFilename = NULL ) {
setupUi ( this );
}
};
with a .ui file created with Qt Designer, that generates a header file defining
class Ui_ImageViewer {
public:
void setupUi(QMainWindow *ImageViewer) { … }
};
namespace Ui {
class ImageViewer: public Ui_ImageViewer {};
} // namespace Ui
Now I would like to write another application ImageRegistrationApp, where I extend the QMenuBar of ImageViewApp by additional QMenus containing QActions for a certain purpose, say image registration. Additionally, I would like to change some other things, such as the windowTitle and add QActions to existing QMenus of ImageViewApp.
I am looking for a way where I don't need to touch or copy the the .ui file of ImageViewApp. I would like to do something like inheritance, where changes to the UI of ImageViewApp affect the UI of ImageRegistrationApp. Also I would like to be able to create and edit the additional QMenus and QActions for ImageRegistrationApp via Qt Designer.
Is this possible?
UPDATE:
I tried changing the title of ImageRegistrationApp and adding a QAction to an existing QMenu in ImageViewApp through inheritance within C++ as follows:
ImageRegistrationApp : public ImageViewApp {
Q_OBJECT
public:
QAction *actionTest;
ImageRegistrationApp ( const char * InputFilename = NULL )
: ImageViewApp ( InputFilename ) {
this->setWindowTitle(QApplication::translate("ImageViewer", "ImReg", 0, QApplication::UnicodeUTF8));
actionTest = new QAction(this);
actionTest->setObjectName(QString::fromUtf8("actionTest"));
actionTest->setText(QApplication::translate("ImageViewer", "Test", 0, QApplication::UnicodeUTF8));
this->menuTools->addAction(actionTest);
}
protected slots:
void on_actionTest_triggered() {
QMessageBox::information(NULL, "Test", "Hello world!" );
}
};
I also changed the inheritance in ImageViewApp to protected Ui::ImageViewer in order to be able to access the Ui elements.
The title of my ImageRegistrationApp changes as intended and also the QAction Test shows up in the menu Tools, but when I click it, nothing happens though I expect it to display the QMessageBox as defined in the slot on_actionTest_triggered.
Is there anything else I have to do, to connect the QAction with the slot?
I tried QObject::connect(actionTest, SIGNAL(triggered()), this, SLOT(actionTest)); but this did not change anything.
The problem with the slot not being executed when the QAction actionTest was triggered, was a misspelling. I had also tried QObject::connect(actionTest, SIGNAL(on_triggered()), this, SLOT(on_actionTest_triggered)); before, but there were the () missing at the end.
Adding the following line at the end of the constructor of ImageRegistrationApp solved the problem:
QObject::connect(actionTest, SIGNAL(on_triggered()), this, SLOT(on_actionTest_triggered()));
Instantiating ImageRegistrationApp now instantiates the entire GUI of ImageViewApp and adds the additionally defined QActions to the respective QMenus. This method should be capable of everything I asked for in the question, except for designing additional QMenus with Qt Designer. Instead, everything has to be coded in C++ source.
If i click on one dialog button on the other dialog will show some text, it doesnt have to be text basicly connection between two dialogs. Something that will help me about my problem.
If you have two QWidget (or inherited) objects represented as dialogs, then you should use an QObject::connect method. Learn about signals and slots in Qt. It looks like:
class DialogA : public QWidget {
Q_OBJECT
...
public slots:
void ShowSomeText(); // called when receive a signal
...
};
class DialogB : public QWidget {
Q_OBJECT
...
void SendTextSignal(); // sends a signal
...
};
// somwhere in code
DialogA da;
DialogB db;
connect(db, SIGNAL(SendTextSignal()), da, SLOT(ShowSomeText()));
I'm trying to implement a custom widget hierarchy:
QMainWindow -> QFrame -> MyWidget -> QFrame -> MySubWidget
Here is how MyWidget class looks like:
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget(QWidget *parent = 0, ...);
...
public slots:
void SlotFunction(int i);
...
private:
MySubWidget *sub_w;
QFrame *sub_frame;
...
}
If I try to create an MySubWidget during MyWidget constructor, then all MySubWidget elements are shown as intended:
MyWidget::MyWidget (...) : QWidget(parent) {
...
sub_frame = new QFrame(this);
...
sub_w = new MySubWidget(sub_frame); // commented out on a runtime test
}
But if I try to add subwidget during runtime, sub_frame remains blank. I.e. signal reaction:
void MyWidget::SlotFunction(int i) {
sub_w = new MySubWidget(sub_frame); // update, repaint, show and hide methods aren't helphul
}
I know this is an old question, but I was having a very similar issue and it turned out to be a lack of call to the QWidget::show(). Perhaps that was your problem as well?
My question here: Dynamically add instance inherited from QWidget
Cheers.
Are you reaching your function?
At the top of your function before making a new instance of MySubWidget put:
qDebug() << Q_FUNC_INFO;
Is the slot connected properly?
Qt will let you know if it is unable to connect a slot using a runtime warning. Look at the debug output that shows up in Qt Creator and it may mention a reason why the slot was never reached.
Is subframe visible?
If the parent of your object isn't visible, then showing or hiding the child object will only affect it when the parent is shown.
Hope that helps. Good luck.
Here's a simple code that creates a button and assigns a onclick handler:
auto btn = new QPushButton("CLICK ME");
connect(btn, SIGNAL(clicked()), this, SLOT(btn_Click()));
private slots:
void btn_Click() {
alert("clicked!");
}
It works as it should if called in the main window class. However when I try to do this in a child window, clicking the button does nothing. The child window is shown like this:
auto settingsWindow = new SettingsWindow();
settingsWindow->show();
I guess it's somehow connected with the receiver object which is now a different window. But how can I make it work?
In order to be able to declare signals/slots in your own class you should include Q_OBJECT directive in your class:
class SettingsWindow {
Q_OBJECT
...
};
You should add a MACRO in class SettingsWindow to enable singal receiving.
Add "Q_OBJECT" like the following.
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget();
....
According to the help of Qt for QWebPage [Slot ShoudInteruptJavaScript], located here:
This function is called when a JavaScript program is running for a long period of time.
If the user wanted to stop the JavaScript the implementation should return true; otherwise false.
The default implementation executes the query using QMessageBox::information with QMessageBox::Yes and QMessageBox::No buttons.
Warning: Because of binary compatibility constraints, this function is not virtual. If you want to provide your own implementation in a QWebPage subclass, reimplement the shouldInterruptJavaScript() slot in your subclass instead. QtWebKit will dynamically detect the slot and call it.
I don't want qt show a message when javascript runnig for long period of time.
So, how can i reimplement ShoudInteruptJavaScript? and where should i create it?
Please show me a sample
Thanks
All the info you need is in the documentation.
Create a new custom class that inherits from QWebPage, make sure it's a Q_OBJECT to receive signals.
class MyFunkyPage : public QWebPage {
Q_OBJECT
public slots:
bool shouldInterruptJavaScript() {
QApplication::processEvents(QEventLoop::AllEvents, 42);
// Ignore the error
return false;
}
};
Set the page of your QWebView to a custom subclass of QWebPage.
setPage(new MyFunkyPage());
Then when your page gets this signal it won't stop the script from executing, and it won't show a dialog.
The MyFunkyPage solution potentially leaks memory and causes crashes because the object passed to setPage has no parent and setPage does not take ownership. Instead,
class QWebPageWithoutJsWarning : public QWebPage {
Q_OBJECT
public:
QWebPageWithoutJsWarning(QObject* parent = 0) : QWebPage(parent) {}
public slots:
bool shouldInterruptJavaScript() {
return false;
}
};
Set the page of your QWebView to the custom subclass of QWebPage, parented on the WebView,
void suppressJSWarning(QWebView& webView) {
webView.setPage(new QWebPageWithoutJsWarning(&webView));
}
#anson-mackeracher almost had it right.
Qt needs it to be a private slot, not a public one. Here's what works for my class:
class MyFunkyPage : public QWebPage {
Q_OBJECT
private slots:
bool shouldInterruptJavaScript() {
// Ignore the error (return true to kill the runaway JavaScript)
return false;
}
};
Set the page of your QWebView to a custom subclass of QWebPage.
setPage(new MyFunkyPage());
I just tested this with Qt 4.8.4 and it works like a charm. I didn't need the processEvents call.