Название: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: FluffyMan2000 от Декабрь 10, 2012, 15:40
доброго времени суток. Есть QMdiArea в QMainWindow. Добавляю в нее дочерние окна DocWindow (объекты-наследники QTextEdit, как у М.Шлее в примерах). У QMainWindow есть слоты, в которых текущее дочернее окно приводится к DocWindow и вызывается нужный слот DocWindow. Но дело в том, что слоты эти вызываются столько раз, сколько дочерних окон в QMdiArea. Это при том, что я привожу ТЕКУЩЕЕ дочернее окно к DocWindow и вызываю слот КОНКРЕТНОГО экзэмпляра. Понять не могу в чем ошибка. пол дня над ней сижу. Проект во вложении. DocWindow.h #ifndef _DocWindow_h_ #define _DocWindow_h_
#include <QTextEdit> #include <QMessageBox>
class DocWindow: public QTextEdit { Q_OBJECT private: QString docName;
public: DocWindow(QWidget* pwgt = 0);
protected: void closeEvent(QCloseEvent *ev);
signals: void changeWindowTitle(const QString&);
public slots: void setDocumentName(const QString &name); QString getDocumentName();
void setStartInTitle();
void slotSave(); void slotSaveAs();
QMessageBox::StandardButton maybeSave(); }; #endif //_DocWindow_h_
DocWindow.cpp #include <QtGui> #include "DocWindow.h"
DocWindow::DocWindow(QWidget* pwgt/*=0*/) : QTextEdit(pwgt) { }
void DocWindow::slotSave() { if (docName == "New document") { this->slotSaveAs(); } else { QTextDocumentWriter writer(docName); bool success = writer.write(this->document());
if (success) { this->document()->setModified(false); emit changeWindowTitle(docName); } } }
void DocWindow::slotSaveAs() { QString str = QFileDialog::getSaveFileName(0, docName, QApplication::applicationDirPath(), "Txt (*.txt);;Reach Text (*.html)"); if (!str.isEmpty()) { docName = str; slotSave(); } }
void DocWindow::setDocumentName(const QString &name) { docName = name; emit changeWindowTitle(docName); }
QMessageBox::StandardButton DocWindow::maybeSave() { if (this->document()->isModified()) { QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, "Application", "The document \"" + docName + "\" has been modified.\n" "Do you want to save your changes?", QMessageBox::Save | QMessageBox::Discard);
if (ret == QMessageBox::Save) { this->slotSave(); }
return ret; } }
void DocWindow::closeEvent(QCloseEvent *ev) { if (this->maybeSave() == QMessageBox::Cancel) { ev->ignore(); } else { ev->accept(); } }
QString DocWindow::getDocumentName() { return docName; }
void DocWindow::setStartInTitle() { this->setWindowTitle(docName + "*"); }
MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow> #include <QCloseEvent> #include <QMdiSubWindow> #include "DocWindow.h"
namespace Ui { class MainWindow; }
class MainWindow : public QMainWindow { Q_OBJECT
public: explicit MainWindow(QWidget *parent = 0); ~MainWindow();
private: Ui::MainWindow *ui;
protected: void closeEvent(QCloseEvent *ev);
public slots: void slotChangeWindowTitle(const QString&);
DocWindow* newDocument(); void loadDocument();
void delegateForSave(); void delegateForSaveAs(); void delegateForUndo(); void delegateForRedo(); void delegateForCopy(); void delegateForCut(); void delegateForPaste();
void checkForDocChanges(); };
#endif // MAINWINDOW_H
MainWindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h"
#include "DocWindow.h" #include <QFileDialog> #include <QTextCodec> #include <QMdiSubWindow> #include <QDebug>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this);
ui->mainToolBar->addAction(ui->actionNew); ui->mainToolBar->addAction(ui->actionOpen); ui->mainToolBar->addSeparator(); ui->mainToolBar->addAction(ui->actionSave); ui->mainToolBar->addAction(ui->actionSave_as); ui->mainToolBar->addSeparator(); ui->mainToolBar->addAction(ui->actionTile); ui->mainToolBar->addAction(ui->actionCascade);
ui->toolBarTextProperties->addAction(ui->actionBold); ui->toolBarTextProperties->addAction(ui->actionItalic); ui->toolBarTextProperties->addAction(ui->actionUnderlined); ui->toolBarTextProperties->addAction(ui->actionFont); ui->toolBarTextProperties->addAction(ui->actionColor);
ui->toolBarTextEdit->addAction(ui->actionCopy); ui->toolBarTextEdit->addAction(ui->actionCut); ui->toolBarTextEdit->addAction(ui->actionPaste); ui->toolBarTextEdit->addAction(ui->actionUndo); ui->toolBarTextEdit->addAction(ui->actionRedo);
connect(ui->actionTile, SIGNAL(triggered()), ui->mdiArea, SLOT(tileSubWindows())); connect(ui->actionCascade, SIGNAL(triggered()), ui->mdiArea, SLOT(cascadeSubWindows()));
connect(ui->actionNew, SIGNAL(triggered()), this, SLOT(newDocument())); connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(loadDocument())); connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(close()));
connect(ui->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(checkForDocChanges())); }
MainWindow::~MainWindow() { delete ui; }
DocWindow* MainWindow::newDocument() { DocWindow *pDoc = new DocWindow; ui->mdiArea->addSubWindow(pDoc); pDoc->setAttribute(Qt::WA_DeleteOnClose); pDoc->setDocumentName("New document"); pDoc->setWindowTitle(pDoc->getDocumentName() + "*"); pDoc->document()->setModified(true); pDoc->show();
// активируем/деактивируем пункты меню ui->actionUndo->setEnabled(pDoc->document()->isUndoAvailable()); ui->actionRedo->setEnabled(pDoc->document()->isRedoAvailable()); ui->actionPaste->setEnabled(pDoc->canPaste());
connect(pDoc, SIGNAL(changeWindowTitle(const QString&)), this, SLOT(slotChangeWindowTitle(const QString&))); connect(pDoc, SIGNAL(textChanged()), this, SLOT(checkForDocChanges()));
connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(delegateForSave())); connect(ui->actionSave_as, SIGNAL(triggered()), this, SLOT(delegateForSaveAs())); connect(ui->actionUndo, SIGNAL(triggered()), this, SLOT(delegateForUndo())); connect(ui->actionRedo, SIGNAL(triggered()), this, SLOT(delegateForRedo())); connect(ui->actionCopy, SIGNAL(triggered()), this, SLOT(delegateForCopy())); connect(ui->actionCut, SIGNAL(triggered()), this, SLOT(delegateForCut())); connect(ui->actionPaste, SIGNAL(triggered()), this, SLOT(delegateForPaste()));
return pDoc; }
void MainWindow::slotChangeWindowTitle(const QString& str) { qobject_cast<DocWindow*>(sender())->setWindowTitle(str); }
void MainWindow::delegateForSave() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->slotSave(); qDebug() << "save"; }
void MainWindow::delegateForSaveAs() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->slotSaveAs(); qDebug() << "save as"; }
void MainWindow::loadDocument() { QString name = QFileDialog::getOpenFileName(this, "Open File...", QString(), "Txt (*.txt);;Reach Text (*.html *.htm)");
if (!name.isEmpty()) { DocWindow *pDoc = this->newDocument(); pDoc->setDocumentName(name);
if (!QFile::exists(name)) { return; }
QFile file(name); if (!file.open(QFile::ReadOnly)) { return; }
QByteArray data = file.readAll(); QTextCodec *codec = Qt::codecForHtml(data); QString str = codec->toUnicode(data);
if (Qt::mightBeRichText(str)) { pDoc->setHtml(str); } else { str = QString::fromUtf8(data); pDoc->setPlainText(str); } } }
void MainWindow::closeEvent(QCloseEvent *ev) { QList<QMdiSubWindow*> list = ui->mdiArea->subWindowList(); bool saved = true;
for (int i = 0; i < list.count(); i++) { if (qobject_cast<DocWindow*>(list.at(i)->widget())->document()->isModified()) { saved = false; break; } }
if (!saved) { QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, "Application", "You have unsaved documents.\n" "Do you realy want to quit?", QMessageBox::Yes | QMessageBox::Cancel);
if (ret == QMessageBox::Cancel) { ev->ignore(); } else { ui->mdiArea->closeAllSubWindows(); ev->accept(); } } }
void MainWindow::delegateForUndo() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->document()->undo(); qDebug() << "undo"; }
void MainWindow::delegateForRedo() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->document()->redo(); qDebug() << "redo"; }
void MainWindow::delegateForCopy() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->copy(); qDebug() << "copy"; }
void MainWindow::delegateForCut() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->cut(); qDebug() << "cut"; }
void MainWindow::delegateForPaste() { qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget())->paste(); qDebug() << "paste"; }
void MainWindow::checkForDocChanges() { if (ui->mdiArea->subWindowList().count() != 0) { DocWindow *pDoc = qobject_cast<DocWindow*>(ui->mdiArea->currentSubWindow()->widget());
if (pDoc != 0) { ui->actionUndo->setEnabled(pDoc->document()->isUndoAvailable()); ui->actionRedo->setEnabled(pDoc->document()->isRedoAvailable()); ui->actionPaste->setEnabled(pDoc->canPaste());
if (pDoc->document()->isModified()) { pDoc->setWindowTitle(pDoc->getDocumentName() + "*"); } } } }
Название: Re: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: mutineer от Декабрь 10, 2012, 16:49
Возьмем, например, слот delegateForSave(). При каждом создании дочернего окна ты привязываешь к этому слоту один и тот же сигнал. А согласно документации сколько раз ты вызвал connect для одной и той же пары сигнал-слот, столько раз слот и вызовется при испускании сигнала
Название: Re: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: FluffyMan2000 от Декабрь 10, 2012, 16:52
Возьмем, например, слот delegateForSave(). При каждом создании дочернего окна ты привязываешь к этому слоту один и тот же сигнал. А согласно документации сколько раз ты вызвал connect для одной и той же пары сигнал-слот, столько раз слот и вызовется при испускании сигнала
да. но я ведь в этом слоте привожу вызываю слот конкретного экземпляра. а как тогда в мое случае этого избежать?
Название: Re: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: mutineer от Декабрь 10, 2012, 16:54
Возьмем, например, слот delegateForSave(). При каждом создании дочернего окна ты привязываешь к этому слоту один и тот же сигнал. А согласно документации сколько раз ты вызвал connect для одной и той же пары сигнал-слот, столько раз слот и вызовется при испускании сигнала
да. но я ведь в этом слоте привожу вызываю слот конкретного экземпляра. а как тогда в мое случае этого избежать? При вызове да, ты вызываешь метод конкретного экземпляра, но вот при connect ты привязываешься к слоту MainWindow::delegateForSave(). Избежать этого элементарно - выполнять привязку один раз, а не при каждом создании нового подокна
Название: Re: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: FluffyMan2000 от Декабрь 10, 2012, 16:57
Возьмем, например, слот delegateForSave(). При каждом создании дочернего окна ты привязываешь к этому слоту один и тот же сигнал. А согласно документации сколько раз ты вызвал connect для одной и той же пары сигнал-слот, столько раз слот и вызовется при испускании сигнала
да. но я ведь в этом слоте привожу вызываю слот конкретного экземпляра. а как тогда в мое случае этого избежать? При вызове да, ты вызываешь метод конкретного экземпляра, но вот при connect ты привязываешься к слоту MainWindow::delegateForSave(). Избежать этого элементарно - выполнять привязку один раз, а не при каждом создании нового подокна хм. а в таком случае привязку ЧЕГО осуществлять один раз? если объект еще не создан.
Название: Re: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: mutineer от Декабрь 10, 2012, 17:01
connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(delegateForSave())); connect(ui->actionSave_as, SIGNAL(triggered()), this, SLOT(delegateForSaveAs())); connect(ui->actionUndo, SIGNAL(triggered()), this, SLOT(delegateForUndo())); connect(ui->actionRedo, SIGNAL(triggered()), this, SLOT(delegateForRedo())); connect(ui->actionCopy, SIGNAL(triggered()), this, SLOT(delegateForCopy())); connect(ui->actionCut, SIGNAL(triggered()), this, SLOT(delegateForCut())); connect(ui->actionPaste, SIGNAL(triggered()), this, SLOT(delegateForPaste()));
Вот набор привязок, которые ты выполняешь при каждом создании нового подокна. Какой объект тут еще не создан?
Название: Re: QMdiArea: слоты вызываются столько раз, сколько дочерних окон в QMdiArea
Отправлено: FluffyMan2000 от Декабрь 10, 2012, 17:07
connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(delegateForSave())); connect(ui->actionSave_as, SIGNAL(triggered()), this, SLOT(delegateForSaveAs())); connect(ui->actionUndo, SIGNAL(triggered()), this, SLOT(delegateForUndo())); connect(ui->actionRedo, SIGNAL(triggered()), this, SLOT(delegateForRedo())); connect(ui->actionCopy, SIGNAL(triggered()), this, SLOT(delegateForCopy())); connect(ui->actionCut, SIGNAL(triggered()), this, SLOT(delegateForCut())); connect(ui->actionPaste, SIGNAL(triggered()), this, SLOT(delegateForPaste()));
Вот набор привязок, которые ты выполняешь при каждом создании нового подокна. Какой объект тут еще не создан? Баааалин! как я мог так затупить. спасибо. Вынес эти соединения в конструктор MainWindow
|