cosas de qt12

Upload: grissgs

Post on 02-Mar-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/26/2019 Cosas de Qt12

    1/6

    The "[*]" marker in the window title is a place marker for where we want the asterisk to appear when the file has unsaved changeson platforms other than Mac OS X. On Mac OS X, unsaved documents have a dot in their window's close button. We covered thisplace marker in Chapter 3(p. 61).

    In addition to creating new files, users often want to open existing files, picked from either a file dialog or a list such as a recentlyopened files list. Two static functions are provided to support these uses: open()for choosing a file name from the file system,and openFile()to create an Editorand to read into it the contents of a specified file.

    Editor *Editor::open(QWidget *parent){ QString fileName = QFileDialog::getOpenFileName(parent, tr("Open"), "."); if (fileName.isEmpty())

    return 0;

    return openFile(fileName, parent);}

    The static open()function pops up a file dialog through which the user can choose a file. If a file is chosen, openFile()iscalled to create an Editorand to read in the file's contents.

    Editor *Editor::openFile(const QString &fileName, QWidget *parent){ Editor *editor = new Editor(parent);

    if (editor->readFile(fileName)) { editor->setCurrentFile(fileName); return editor; } else {

    delete editor; return 0; }}

    This static function begins by creating a new Editorwidget, and then attempts to read in the specified file. If the read issuccessful, the Editoris returned; otherwise, the user is informed of the problem (in readFile()), the editor is deleted, and anull pointer is returned.

    bool Editor::save(){ if (isUntitled) { return saveAs(); } else { return saveFile(curFile); }

    }

    The save()function uses the isUntitledvariable to determine whether it should call saveFile()or saveAs().

    void Editor::closeEvent(QCloseEvent *event){ if (okToContinue()) { event->accept(); } else { event->ignore(); }}

    The closeEvent()function is reimplemented to allow the user to save unsaved changes. The logic is coded in the

    okToContinue()function, which pops up a message box that asks, "Do you want to save your changes?" IfokToContinue()returns true, we accept the close event; otherwise, we "ignore" it and leave the window unaffected by it.

    void Editor::setCurrentFile(const QString &fileName){ curFile = fileName; isUntitled = false; action->setText(strippedName(curFile)); document()->setModified(false); setWindowTitle(strippedName(curFile) + "[*]"); setWindowModified(false);}

    165

  • 7/26/2019 Cosas de Qt12

    2/6

    The setCurrentFile()function is called from openFile()and saveFile()to update the curFileandisUntitledvariables, to set the window title and action text, and to set the document's "modified" flag to false. Wheneverthe user modifies the text in the editor, the underlying QTextDocumentemits the contentsChanged()signal and sets itsinternal "modified" flag to true.

    QSize Editor::sizeHint() const{ return QSize(72 * fontMetrics().width('x'), 25 * fontMetrics().lineSpacing());}

    Finally, the sizeHint()function returns a size based on the width of the letter 'x' and the height of a text line. QMdiAreausesthe size hint to give an initial size to the window.

    MDI is one way of handling multiple documents simultaneously. On Mac OS X, the preferred approach is to use multipletop-level windows. We covered this approach in the "Multiple Documents" section of Chapter 3.

    166

  • 7/26/2019 Cosas de Qt12

    3/6

    7. Event Processing

    Reimplementing Event Handlers

    Installing Event FiltersStaying Responsive during Intensive Processing

    Events are generated by the window system or by Qt itself in response to various occurrences. When the user presses or releases akey or mouse button, a key or mouse event is generated; when a window is shown for the first time, a paint event is generated totell the newly visible window that it needs to draw itself. Most events are generated in response to user actions, but some, such astimer events, are generated independently by the system.

    When we program with Qt, we seldom need to think about events, because Qt widgets emit signals when something significantoccurs. Events become useful when we write our own custom widgets or when we want to modify the behavior of existing Qtwidgets.

    Events should not be confused with signals. As a rule, signals are useful when using a widget, whereas events are useful whenimplementing a widget. For example, when we are using QPushButton, we are more interested in its clicked()signal thanin the low-level mouse or key events that caused the signal to be emitted. But if we are implementing a class such asQPushButton, we need to write code to handle mouse and key events and emit the clicked()signal when necessary.

    Reimplementing Event Handlers

    In Qt, an event is an instance of a QEventsubclass. Qt handles more than a hundred types of events, each identified by an enumvalue. For example, QEvent::type()returns QEvent::MouseButtonPressfor mouse press events.

    Many event types require more information than can be stored in a plain QEventobject; for example, mouse press events need tostore which mouse button triggered the event as well as where the mouse pointer was positioned when the event occurred. Thisadditional information is stored in dedicated QEventsubclasses, such as QMouseEvent.

    Events are notified to objects through their event()function, inherited from QObject. The event()implementation inQWidgetforwards the most common types of events to specific event handlers, such as mousePressEvent(),keyPressEvent(), and paintEvent().

    We have already seen many event handlers when implementing MainWindow, IconEditor, and Plotterin earlier

    chapters. Many other types of events are listed in the QEventreference documentation, and it is also possible to create customevent types and to dispatch events ourselves. Here, we will review two common event types that deserve more explanation: keyevents and timer events.

    Key events are handled by reimplementing keyPressEvent()and keyReleaseEvent(). The Plotterwidgetreimplements keyPressEvent(). Normally, we only need to reimplement keyPressEvent()since the only keys forwhich release is important are the modifier keys Ctrl, Shift, and Alt, and these can be checked for in a keyPressEvent()using QKeyEvent::modifiers(). For example, if we were implementing a CodeEditorwidget, its stripped-downkeyPressEvent()that distinguishes between Home and Ctrl+Home would look like this:

    void CodeEditor::keyPressEvent(QKeyEvent *event){ switch (event->key()) { case Qt::Key_Home: if (event->modifiers() & Qt::ControlModifier) { goToBeginningOfDocument(); } else { goToBeginningOfLine(); } break; case Qt::Key_End: ... default: QWidget::keyPressEvent(event); }}

    The Tab and Backtab (Shift+Tab) keys are special cases. QWidget::event()handles them before it callskeyPressEvent(), with the semantic of passing the focus to the next or previous widget in the focus chain. This behavior isusually what we want, but in a CodeEditorwidget, we might prefer to make Tab indent a line. The event()reimplementation would then look like this:

    167

  • 7/26/2019 Cosas de Qt12

    4/6

    bool CodeEditor::event(QEvent *event){ if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Tab) { insertAtCurrentPosition('\t'); return true; } } return QWidget::event(event);}

    If the event is a key press, we cast the QEventobject to a QKeyEventand check which key was pressed. If the key is Tab, wedo some processing and return trueto tell Qt that we have handled the event. If we returned false, Qt would propagate theevent to the parent widget.

    A higher-level approach for implementing key bindings is to use a QAction. For example, if goToBeginningOfLine()andgoToBeginningOfDocument()are public slots in the CodeEditorwidget, and the CodeEditoris used as the centralwidget in a MainWindowclass, we could add the key bindings with the following code:

    MainWindow::MainWindow(){ editor = new CodeEditor; setCentralWidget(editor);

    goToBeginningOfLineAction = new QAction(tr("Go to Beginning of Line"), this); goToBeginningOfLineAction->setShortcut(tr("Home")); connect(goToBeginningOfLineAction, SIGNAL(activated()), editor, SLOT(goToBeginningOfLine()));

    goToBeginningOfDocumentAction = new QAction(tr("Go to Beginning of Document"), this); goToBeginningOfDocumentAction->setShortcut(tr("Ctrl+Home")); connect(goToBeginningOfDocumentAction, SIGNAL(activated()), editor, SLOT(goToBeginningOfDocument())); ...}

    This makes it easy to add the commands to a menu or a toolbar, as we saw in Chapter 3. If the commands don't appear in the userinterface, the QActionobjects could be replaced with a QShortcutobject, the class used internally by QActionto supportkey bindings.

    By default, key bindings set using QActionor QShortcuton a widget are enabled whenever the window that contains thewidget is active. This can be changed using QAction::setShortcutContext()or QShortcut::setContext().

    Another common type of event is the timer event. While most other event types occur as a result of a user action, timer eventsallow applications to perform processing at regular time intervals. Timer events can be used to implement blinking cursors andother animations, or simply to refresh the display.

    To demonstrate timer events, we will implement the Tickerwidget shown in Figure 7.1. This widget shows a text banner thatscrolls left by one pixel every 30 milliseconds. If the widget is wider than the text, the text is repeated as often as necessary to fillthe entire width of the widget.

    Figure 7.1. The Tickerwidget

    Here's the header file:

    Code View:#ifndef TICKER_H#define TICKER_H

    #include

    class Ticker : public QWidget{ Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText)

    public: Ticker(QWidget *parent = 0);

    168

  • 7/26/2019 Cosas de Qt12

    5/6

    void setText(const QString &newText); QString text() const { return myText; } QSize sizeHint() const;

    protected: void paintEvent(QPaintEvent *event); void timerEvent(QTimerEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event);

    private: QString myText; int offset; int myTimerId;

    };#endif

    We reimplement four event handlers in Ticker, three of which we have not seen before: timerEvent(), showEvent(),and hideEvent().

    Now let's review the implementation:

    #include

    #include "ticker.h"

    Ticker::Ticker(QWidget *parent)

    : QWidget(parent){ offset = 0; myTimerId = 0;}

    The constructor initializes the offsetvariable to 0. The x-coordinate at which the text is drawn is derived from the offsetvalue. Timer IDs are always non-zero, so we use 0 to indicate that no timer has been started.

    void Ticker::setText(const QString &newText){ myText = newText; update(); updateGeometry();}

    The setText()function sets the text to display. It calls update()to request a repaint and updateGeometry()to notifyany layout manager responsible for the Tickerwidget about a size hint change.

    QSize Ticker::sizeHint() const{ return fontMetrics().size(0, text());}

    The sizeHint()function returns the space needed by the text as the widget's ideal size. QWidget::fontMetrics()returns a QFontMetricsobject that can be queried to obtain information relating to the widget's font. In this case, we ask forthe size required by the given text. (The first argument to QFontMetrics::size()is a flag that isn't needed for simplestrings, so we just pass 0.)

    void Ticker::paintEvent(QPaintEvent * /* event */){ QPainter painter(this);

    int textWidth = fontMetrics().width(text()); if (textWidth < 1) return; int x = -offset; while (x < width()) { painter.drawText(x, 0, textWidth, height(), Qt::AlignLeft | Qt::AlignVCenter, text()); x += textWidth; }}

    169

  • 7/26/2019 Cosas de Qt12

    6/6

    The paintEvent()function draws the text using QPainter::drawText(). It uses fontMetrics()to ascertain howmuch horizontal space the text requires, and then draws the text as many times as necessary to fill the entire width of the widget,taking offsetinto account.

    void Ticker::showEvent(QShowEvent * /* event */){ myTimerId = startTimer(30);}

    The showEvent()function starts a timer. The call to QObject::startTimer()returns an ID number, which we can uselater to identify the timer. QObjectsupports multiple independent timers, each with its own time interval. After the call tostartTimer(), Qt will generate a timer event approximately every 30 milliseconds; the accuracy depends on the underlyingoperating system.

    We could have called startTimer()in the Tickerconstructor, but we save some resources by having Qt generate timerevents only when the widget is actually visible.

    void Ticker::timerEvent(QTimerEvent *event){ if (event->timerId() == myTimerId) { ++offset; if (offset >= fontMetrics().width(text())) offset = 0; scroll(-1, 0); } else { QWidget::timerEvent(event); }}

    The system calls the timerEvent()function at intervals. It increments offsetby 1 to simulate movement, wrapping at thewidth of the text. Then it scrolls the contents of the widget one pixel to the left using QWidget::scroll(). It would havebeen sufficient to call update()instead of scroll(), but scroll()is more efficient because it simply moves the existingpixels on-screen and generates a paint event only for the widget's newly revealed area (a 1-pixel-wide strip in this case).

    If the timer event isn't for the timer we are interested in, we pass it on to the base class.

    void Ticker::hideEvent(QHideEvent * /* event */){ killTimer(myTimerId); myTimerId = 0;}

    The hideEvent()function calls QObject::killTimer()to stop the timer.

    Timer events are low-level, and if we need multiple timers, it can become cumbersome to keep track of all the timer IDs. In suchsituations, it is usually easier to create a QTimerobject for each timer. QTimeremits the timeout()signal at each timeinterval. QTimeralso provides a convenient interface for single-shot timers (timers that time out just once), as we saw in Chapter6(p. 158).

    170