1. Trang chủ
  2. » Công Nghệ Thông Tin

The book of qt 4 the art of building qt applications - phần 4 pot

45 277 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 45
Dung lượng 605,67 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The textCursor method of the text window represents the current position of thewriting cursor.17 We pass the text, which it should insert at the cursor position, tothe instance.. 5 Ch ap

Trang 1

new TemplateHandler(view, textEdit, this);

The main window requires the object name to save the window properties Wewill discuss this topic at the end of Section 4.8 If the name is missing, Qt com-plains at runtime on the standard output Unfortunately, it is not possible to setthe windowTitle attribute of QDockWidget in the Designer, which is why it is im-portant that this must be done separately in the constructor windowTitle labelsthe window and also gives a name to the toggle action that is generated by tog-gleViewAction()

In the final step we breathe life into the widget by filling it with a list view We willlater find the templates in this view The TemplateHandler class now instantiated

is responsible for filling the list and for inserting templates at the current cursorposition in the editor window:

We pass the list created in this way to the model via setStringList() and turn thisinto the reference model for our view, the list view The list view is now filled, and

16 In a proper application the templates are not compiled statically, of course, but are loaded from

a file.

Trang 2

we just need to include the selected template in the editor window To do this,

we connect the clicked() signal of the view and implement the insertText() method

(which we must first declare in the class definition as a slot, of course):

// cuteedit2/templatehandler.cpp (continued)

void TemplateHandler::insertText( const QModelIndex& index )

{

QString text = mModel->data(index, Qt::DisplayRole).toString();

QTextCursor cursor = mTextEdit->textCursor();

cursor.insertText(text);

mTextEdit->setTextCursor(cursor);

}

The model index passed represents the selected line in our model Using the data()

method, we can obtain the data as QVariant from this, which we must still convert

into a QString QVariant works in a similar way to a union in C++ The class can

also convert various types—both Qt-specific data types such as QString and QSize,

as well as C++ types such as int or double—from one to another

Figure 4.13: The template dock window in docked condition: A click inserts the text into the corresponding line in the editor window.

The model/view concept of Qt has many different roles for a model index (see table

8.1 on page 209; for instance, many views can display an icon (Qt::DecorationRole),

in addition to normal text (Qt::DisplayRole) At the moment, however, only Qt::

DisplayRole is relevant to us

Trang 3

The textCursor() method of the text window represents the current position of thewriting cursor.17 We pass the text, which it should insert at the cursor position, tothe instance Now we must insert the text cursor to the current cursor positionagain, using setTextCursor, to update the cursor.18

Our dock window is now completely implemented Thanks to the QObject baseclass and the fact that we pass the main window as the parent object, we do notneed to delete the instance of TemplateHandler manually The result is shown inFigure 4.13

4.8 Saving Preferences

Last but not least, our program should be able to keep the settings made by theuser, even after the program is restarted To this end, different conventions havebecome established on different operating systems

Depending on the platform, application data may be stored in the Windows istry (in the user scope HKEY_LOCAL_MACHINE\Software) or in the system scopeHKEY_CURRENT_USER\Software), in an XML-based plist file under Mac OS X, or in/etc/xdg,19(system-wide settings) or ~/.config (user-defined settings) under Unix

Reg-Qt encapsulates access to these configuration storage systems with the help of

theQSettings class Every filing system in this is a backend QSettings objects

can be created either on the heap or on the stack Since little work is needed

to instantiate them, we recommend that you create them on the stack, if this isnecessary

In case two or more QSettings instances are working with the same data, the classensures that data between different instances is always correctly synchronized, incase two or more QSettings objects are working with the same file The sameapplies for two threads, both of which contain a QSettings object with the link tothe same file, and even for two different processes, in case both are using QSettingslinked to a common file Qt uses internal locking mechanisms for this purpose.The QSettings constructor normally requires two parameters for the instantiation

in order to generate the appropriate entry in the configuration storage system: thename of the organization for which the programmer works, and the name of theprogram In Windows,

17 The QTextCursor class in general does not have to describe the currently visible cursor, but it can manipulate text at any position at all.

value-based one, and we therefore work with a copy created with the allocation to the cursor variable.

generic term for the Freedesktop.org developers See also list/2003-March/msg00041.html.

Trang 4

http://www.redhat.com/archives/xdg-QSettings settings("OpenSourcePress", "CuteEdit");

would reference the registry path

HKEY CURRENT USER\Software\OpenSourcePress\CuteEdit

If a programmer generates such QSettings instances at many locations in the code,

it would be a good idea not to have to constantly pass the parameters This is

possible if we feed the application itself with program and organization details,

preferably straight in the main() function:

QCoreApplication::setOrganizationName("OpenSourcePress");

QCoreApplication::setOrganizationDomain("OpenSourcePress.de");

QCoreApplication::setApplicationName("CuteEdit");

From now on, QSettings makes use of these details, so that an instance without

parameters is all that is needed:

QSettings settings;

It is surprising that setOrganizationDomain() method exists, since we have just

managed without it But it is justified through the way that Mac OS X stores its

settings: it tries to sort the organizations according to an inverted domain name

pattern If the domain details are missing, QSettings creates artificial details from

the organization name If setOrganizationDomain() is specified correctly, the

file-names in OS X are as follows:

$HOME/Library/Preferences/de.OpenSourcePress.CuteEdit.plist

$HOME/Library/Preferences/de.OpenSourcePress.plist

/Library/Preferences/de.OpenSourcePress.CuteEdit.plist

/Library/Preferences/de.OpenSourcePress.plist

It is not absolutely essential to specify the domain, but it should not be left out

in case the organization has a real domain name The first two parts specify the

user scope, and the last two specify the system scope, a distinction that—as hinted

above—concerns all three platforms

In the user scope (QSettings::UserScope) an application saves all the applications

involving just that user, while in the system scope (QSettings::SystemScope) it saves

data that are important for all users Because writing in the system scope generally

requires root or administrator rights, the following constructor is normally relevant

only for installation programs:20

20 Never assume that the user has administrator rights, even if this is standard practice in many

Windows home installations.

Trang 5

QSettings settings(QSettings::SystemScope);

QSettings now ignores the user scope and reads and writes exclusively in the tem scope If you specify QSettings::UserScope instead, the class behaves as if itwas called via the standard constructor QSettings looks in this for a setting, first

sys-in the user scope If the object is not found there, it then looks for it sys-in the systemscope

To write the actual data, QSettings provides the setValue() call, which expects akey and the actual value The value itself is of the QVariant type, with which weare already familiar The following code first stores a value in the system-specificconfiguration backend and then reads it out:

// configtest/main.cpp

// manufacturer, product QSettings settings("OpenSourcePress", "ConfigTest");

QString hello = "Hello, world!";

// store a value settings.setValue("Greeting", hello);

// reset variable hello = "";

// read value and assign to variable hello = settings.value("Greeting").toString();

qDebug() << hello; // prints "Hello, world!"

The explicit conversion to a QString using toString() is necessary because C++ isnot in a position to correctly convert the QVariant value returned by Qt becauseQString has no knowledge of QVariant, and thus it does not provide an assignmentoperator

After it is run, the program generates a file in Unix called ~/.config/OpenSourcePress/ConfigTest.conf with the contents

[General]

Greeting=Hello, world!

Since we have not specified any group, QSettings stores the key in the [General]standard group There are generally two methods of naming a specific group Onone hand, we can specify the desired group before one or more setValue() calls, but

we must remove this setting afterward if we want to continue using the object forother purposes:

settings.beginGroup("My Group");

settings.setValue("Greeting", hello);

settings.endGroup();

Trang 6

On the other hand, we can simply place the name of the group in front of the key,

separated by a slash:

settings.setValue("My Group/Greeting", hello);

In both cases the result looks like this:

[My Group]

Greeting=Hello, world!

Under Windows, groups are subpaths of the current application path in the

Reg-istry, whereas Mac OS X structures them through XML tags

4.8.1 Extending CuteEdit

To use QSettings in CuteEdit, we first set up two methods for reading and writing

in MainWindow: readSettings() and writeSettings()

We call writeSettings() in the destructor This generates a new QSettings object and

saves the size of the current window in the Size key of the MainWindow group In

the next step we save all internal settings for the MainWindow; for instance, the

positions of the toolbars and dock windows To do this, QMainWindow provides

the saveState() method, which converts these properties into a QByteArray:

We call its counterpart, readSettings(), as the final step in the constructor of the

class It reads the settings and applies them to the finished main window, using

restoreState() restoreState() restores the internal status of the main window,

us-ing the read-out QByteArray But first we must convert the QVariant returned by

value() into a QSize or QByteArray:

Trang 7

}

The second parameter that we pass to value()—sizeHint()—is also unusual It is thedefault value if the backend cannot find the key In specific cases it ensures thatthe editor window has an appropriate initial size

Trang 8

5 Ch ap

Laying Out Widgets

Even if you leave it up to Qt to arrange the widgets in a dialog or main window

(as we have done so far), there is nothing preventing you from doing the layout

manually using the class library in special cases In practice this is seldom done,

but a closer look at manual layout provides an understanding of the Qt layout

mechanism, which we will examine below

5.1 Manual Layout

In the traditional version of GUI design, each widget is “attached by hand” to a

point in the overlying window or widget (that is, the widget that has been specified

as a parent object for the given GUI element) and fixed values for its height and

width are defined The QWidget class provides the setGeometry() method as a basis

class for nearly all graphical elements This expects four integer parameters: first

Trang 9

the values for the x and y positions relative to the parent widget, followed by theheight and width At this point in time the parent widget does not have to displayits own final size.

As an example, we can look at a window derived from QWidget (Figure 5.1):// manually/window.cpp

laid out widget

The setFixedSize() method instructs the window to accept a fixed, unchanged size.Then we position an editor window (a QTextEdit widget1) and a button

From these setGeometry() calls it is already evident that it is quite difficult to guessthe correct values Getting a layout constructed in this manner to work is a contin-uous cycle of choosing candidate values, compiling, and then adjusting the values

to improve the appearance It can also be quite awkward if the widget or dialog

1 For all those who have not (yet) read, or so far merely browsed through Chapter 4: The QTextEdit class provides a multiple-line input field for text, which can be formatted via the API In addition

to pure text, it can also load structured HTML.

Trang 10

changes: If you want to add a new button in the middle of an arrangement, for

example, the position of all elements placed beneath the new element must be

modified

Now, it can be argued that none of this is a problem in practice, since the Qt

Designer considerably simplifies the positioning work involved But even a GUI

designer cannot solve all problems without using automatic layouts

One of these problems concerns widgets that would look better if they could shrink

or grow: In an inflexible layout and without additional aids, such elements—like the

editor window in the example—always retain the same size, although it would be

nice if they would adjust to the available screen size or at least give the user the

option of changing their dimensions

To keep the size of the dialog flexible, we could replace the setFixedSize() call with

the resize() method, which also expects two integer parameters or a QSize

param-eter This only adjusts the size, and does not fix it The user can now change the

dimensions of the dialog with the mouse, although the widgets that it contains

retain their dimensions

Alternatively, you could reimplement the QWidget method resizeEvent(): Qt always

invokes this method when the widget size changes You could write code to

com-pute the new sizes and positions of the window elements on each resize event

But this procedure is much too complex in most cases, and also requires manual

calculation of the widget proportions.2

In addition, reimplementing resizeEvent() poses a particular problem in

combina-tion with internacombina-tionalizacombina-tion: With localized software, the dimensions of a labeled

widget may depend on the language in which it is displayed A button called Close

in English has a much longer label in the German translation (Schließen), and the

text will be cut off unless special precautionary measures are taken

Ultimately, we can only patch up the symptoms in this way To actually solve the

underlying problem, we cannot avoid using automatic layout

5.2 Automatic Layout

The QLayout class and specialized layouts derived from it help the developer to

position widgets dynamically For this to succeed, each graphic element derived

from QWidget has a sizeHint() method, which returns how much space the widget

would like to occupy under normal circumstances In the same way, there is a

minimumSizeHint() method—a widget may under no circumstances be smaller than

the value returned by minimumSizeHint() Both sizeHint and minimumSizeHint are

properties, which can be changed with the corresponding set method

re-sizeEvent() to display status windows on the current layout at the lower-right edge of the

window.

Trang 11

Each widget also has a size policy, which the developer can set for the horizontal

and vertical values using setSizePolicy() The purpose of this can best be explained

by means of an example: The QTextEdit object from Figure 5.1 should, if possible,use all of the space in the window not required by other widgets—that is, the fullyavailable width and height Since this applies not only here, but in general foreditor windows, the standard setting for this widget type defines the size policyQSizePolicy::Expanding for both directions (that is, “windows for this widget typeshould expand as much as possible”)

A button, on the other hand, should only take up as much space vertically as isspecified in the sizeHint() This is ensured by QSizePolicy::Preferred (that is, widgets

of this type should occupy the ideal size, if possible) QPushButtons expand inwidth as far as possible, because for this direction Trolltech specifies QSizePol-icy::Expanding

Figure 5.2:

All layouts inherit

from the QLayout

base class.

QHBoxLayout QVBoxLayout QBoxLayout QGridLayout QStackedLayout

QLayout

5.2.1 Horizontal and Vertical LayoutQLayout as an abstract base class only covers the basic functions for layouts Spe-cific strategies, such as the already familiar horizontal or vertical layout, are lookedafter by the special Qt clusters shown in Figure 5.2 inheriting from QLayout.Thus the QVBoxLayout class, used in the example on page 29 and the followingpages, arranges widgets among themselves, vertically Here the order in which thewidgets are included in the layout using addWidget() is crucial

The example from page 142 now appears as follows:

Trang 12

QTextEdit *txt = new QTextEdit(this);

lay->addWidget(txt);

QPushButton *btn = new QPushButton(tr("&Close"), this);

lay->addWidget(btn);

}

The resize() instruction is not absolutely necessary Without it, Qt adds the

mini-mum sizes of the editor window and the button suggested by minimini-mumSizeHint()

to the spacing inserted by the layout, that is, the distance between two widgets in

a layout In addition it adds a margin for the layout and fixes the window size to

the total

Figure 5.3: The widget with a vertical layout

Figure 5.3 clearly shows the weaknesses of the vertical layout: The button takes

over the full width, which is not what we had in mind

There are two ways of overcoming this problem In the first case we make use of

something we are already familiar with, and take a look at the API

documenta-tion of QBoxLayout,3the class from which QVBoxLayout inherits: The addWidget()

method actually has two other parameters, stretch and alignment The latter looks

after the horizontal alignment of a widget It is now possible to arrange the button

correctly, thanks to Qt::AlignRight

To do this, we simply replace the last two lines of code above with the following:

QPushButton *btn = new QPushButton(tr("&Close"), this);

lay->addWidget(btn, 0, Qt::AlignRight);

You should try this method, particularly if you had trouble with the grid layout

described in Chapter 5.2.2 Grid layouts remain the better choice, particularly for

Trang 13

more complex layouts, in which you can easily lose track of what is lined up wherewhen using box layouts.

Box layouts have an additional property which we have so far ignored, the

so-called stretch factor If this does not equal 0, it determines the proportional space

occupied by the widget in the overall layout, in the direction of the box layout.This assumes, of course, that the widget is interested in spreading out in this par-ticular direction It does not make any sense for a button, for example, to stretchout vertically above the height or below the depth of the text or the icon that itdisplays

If this should still be necessary, however, the size policy can be adjusted using

set-SizePolicy() The method expects two parameters here from the QSizePolicy::Policyenumerator (see Table 5.1), which define the size guidelines for the horizontal andvertical stretches

Table 5.1:

than sizeHint()

QSizePolicy::Minimum sizeHint() is the smallest acceptable size

for the widget, but the widget may be larged as much as you want

en-QSizePolicy::Maximum sizeHint() is the largest acceptable size for

the widget, but the widget may be duced in size as much as you want.QSizePolicy::Preferred sizeHint() is the optimal size, but the

re-widget may be either larger or smallerthan this value (default for QWidget).QSizePolicy::Expanding As Preferred, but the widget demands any

available space in the layout

QSizePolicy::MinimumExpanding As Minimum, but the widget absolutely

demands any available space in the out

lay-QSizePolicy::Ignored Ignore sizeHint()—the widget is given as

much space as possible in the layout

But let’s return to the stretch factor: This is illustrated by the following code ample, which places five stretchable text edits next to each other, but assigns adifferent stretch to each of them:

Trang 14

for (int stretch = 1; stretch <= 5; stretch++) {

txtEdit = new QTextEdit(&w);

We choose a horizontal layout in this example and insert dynamically generated

text fields into it These contain a stretch factor of 1 to 5, according to the status

of the loop counter As can be seen in Figure 5.4, the widgets now take up more

space, increasing from left to right according to their factor Thus the second text

field has twice as much space as the first one, the third, three times as much, and

so on

The text edit behaves strangely as soon as the horizontal window size is reduced:

Although all widgets become proportionately smaller, they always try to attain

their smallest possible size (minimumSize()), which is always the same despite the

stretch factor Therefore, all the text fields in our example are the same size as soon

as the window has reached its minimum size

Figure 5.4: Stretch factors provide individual widgets with more space.

While horizontal stretches seldom cause problems, hardly any widget wants to be

stretched vertically However, if the user expands a dialog lengthways, the layouts

insert ugly spaces between all the GUI elements contained in the dialog window

This can also be avoided using manually defined spaces One approach is to define

a stretch factor in one of the addWiget() calls for a single position, to define a

Trang 15

pretetermined breaking point, so to speak An alternative is to use addStretch() toadd a stretch of any size to the end of the layout (that is, at the lower or right edge,depending on the layout type).

5.2.2 Grid LayoutThe best way to describe the grid layout in Qt is probably as a kind of table, such

as frequently encountered, for example, in HTML or spreadsheet calculations Incontrast to QBoxLayout derivatives, the grid layout class QGridLayout also requires

information on the column and row in which the layout should insert a widget.

As can be seen in Figure 5.2 on page 144, QGridLayout inherits directly from out, so it has properties differing from those of QBoxLayout-based layouts

QLay-In particular, these include another addWidget() method that requires, in addition

to the widget to be inserted, at least two more details, namely the row and thecolumn of the insertion point For widgets that should take up more space than onecell, an overloaded version of the method exists that expects four extra parameters:the coordinates of the first cell and the number of cells that the widget shouldcover in each direction

In addition the setColumnStretch() and setRowStretch() methods allow stretch tors to be set for individual columns or rows Here the first parameter specifies therow or column, and the second parameter specifies the relevant stretch factor.The following example implements our input dialog using a grid layout Through

fac-addWidget(), it positions the text field at the coordinates (0, 0) and specifies for it

a width of two columns and a height of one row

The button is placed on the second row and in the second column, with coordinates

(1, 1), because we start from zero when counting positions, as is usual in

informa-tion technology Stretching the first column with addColumnStretch() then ensuresthat the second column, in which the button is located, is squashed up Using thistrick, the layout is restricted to the optimal width:

QGridLayout *lay = new QGridLayout(this);

QTextEdit *txt = new QTextEdit(this);

lay->addWidget(txt, 0, 0, 1, 2);

Trang 16

QPushButton *btn = new QPushButton(tr("&Close"), this);

lay->addWidget(btn, 1, 1);

lay->setColumnStretch(0, 1);

}

5.2.3 Nested Layouts

Sometimes it is useful to nest layouts inside one another, for instance if you need

to include a new layout, with all its widgets, in an existing one For this reason,

QLayout classes provide a way of including other layouts, with addLayout() This

method expects the same parameters as the addWidget() method of the same

lay-out object

For more complex layouts in particular, the clear hierarchy created in this way turns

out to be very useful, especially if you want to arrange several buttons, as in the

QApplication app(argc, argv);

QWidget *w = new QWidget;

QHBoxLayout *mainLayout = new QHBoxLayout(w);

QTextEdit *txtEdit = new QTextEdit(w);

mainLayout->addWidget(txtEdit);

QVBoxLayout *buttonLayout = new QVBoxLayout;

QPushButton *cancelBtn = new QPushButton(QObject::tr("&Cancel"), w);

QPushButton *okBtn = new QPushButton(QObject::tr("&OK"), w);

QPushButton *defaultBtn = new QPushButton(QObject::tr("&Default"), w);

By placing the individual buttons in a separate layout (buttonLayout), they are

made to appear as one unit to the overlying layout mainLayout You can now use

addLayout() to insert the buttonLayout into mainLayout

Trang 17

Within buttonLayout, use is again made of addStretch(): The variable empty spacecreated by this forces the buttons upwards and takes up the remaining space.

5.3 Splitter

Although horizontal and vertical layouts are dynamic, they cannot be changeddirectly by the user But sometimes he should be able to adjust the spacing betweentwo or more widgets interactively

This need is fulfilled by the QSplitter class that, just like the standard layouts, have

an addWidget() (but no addLayout()) method The partial widgets inserted using

this method are separated by a so-called handle, which can be picked up and

moved by using the mouse (Figure 5.5)

Figure 5.5:

Two text fields moved

with the splitter

In contrast to the QBoxLayout class, there are no specialized classes for QSplitterthat determine whether vertical or horizontal layout is used Instead the orien-tation property determines the alignment It can be set in the constructor, or setlater on If no orientation is specified, Qt creates horizontal splitters

5.3.1 Behavior During Size ChangesThe freedom of movement allowed the user by the splitter is restricted by thewidgets involved: The smallest size is specified by the minimumSizeHint or (if set)the minimumSize property If the user tries to shrink the widget more than this, the

splitter is completely hidden by the widget This is known as a collapsible widget.

If you want to prevent the user from so “getting rid of the widgets,” you can disablethis behavior with setCollapsible(0, false), where 0 stands for the first widget fromthe left for a horizontal splitter, or, with vertical splitters, for the top widget in thesplitter

Trang 18

The isCollapsible() method, which takes one integer argument, provides information

on whether the widget with the specified number is collapsible or not Another

property of the adjacent widget, maximumSize, ensures that the corresponding

area above the splitter cannot be made any smaller once the neighboring widget

has achieved its maximum size

Splitters can react in two ways if the user pulls the handle in one direction while

holding down the mouse button: They either draw a gray line at the point where

the handle would come if the mouse button is released, or else actually move

the handle to the corresponding location This latter method is known as opaque

resizing (that is, a size change that “lets no light in”)

Normally opaque resizing is a better choice, since the user can directly see the

re-sults of his actions Since this technique can often trigger a resizeEvent() under

certain circumstances, however, ugly artifacts can appear if one of the widgets

controlled by the splitter performs very complex drawing operations, or is not

op-timally programmed In this case it is often better to disable opaque resizing, with

setOpaqueResize(false)

5.3.2 Saving Splitter Positions and Determining the Widget

Size

To save the positions of individual splitters beyond program sessions, the QSplitter

API provides the methods saveState() and restoreState()

Since saveState() stores all values in a QByteArray, the method is ideally suited to

saving the sizes of a splitter between one program session and the next This is

done using the class presented on page 136, QSettings If we hadn’t implemented

the templates in CuteEdit as dock windows in Chapter 4, but separated them from

the text field with a splitter, we could save the values of a splitter as a key/value

pair called SplitterSizes in the configuration file, with the following code:

QSettings settings("OpenSourcePress", "CuteEdit");

For situations in which, depending on the alignment of the splitter, the widths or

heights of individual widgets are required as individual integer values, the QSplitter

API has the methods sizes() and setSizes(), which work with the list type QList<int>

This means that you can read out the sizes, for example by using the foreach macro

defined by Qt:

Trang 19

foreach(int size, splitter->sizes()) qDebug("Size: %i", size);

qDebug() is one of the debugging macros that works like the C function printf(),and returns the error message specified in the argument We use it here to quicklyproduce output Details on debugging with Qt are contained in Appendix A.Analogous to reading out the current splitter sizes, it is also possible to changethem by passing the new values with setSizes() in list form:

QList<int> sizes;

sizes << 20 << 60 << 20;

splitter->setSizes(sizes);

In this example, which assumes a splitter with three widgets, these are now 20,

60, and 20 pixels wide (for a horizontal splitter) or high (for a vertically arrangedsplitter)

5.3.3 Defining Relative SizesJust like a normal layout, QSplitter also provides a way of defining a stretch factorfor each widget inserted In contrast to layouts, these must be specified for splittersafterward using the setStretchFactor() method Since this function also requires theposition of the widget, apart from the stretch, you first have to define the position

of the widget using the indexOf() method This returns the correct position for agiven widget or a handle

The example below, documented in Figure 5.6, is derived from the stretch factorexample on page 147, but now uses a splitter instead of a layout Since splittersare aligned horizontally if no other details are given, the result more or less matchesthat of a QHBoxLayout—with the exception that the spaces between widgets nowcarry handles which can be used to define the size of the text edit

for (int stretch = 1; stretch <= 5; stretch++) {

txtEdit = new QTextEdit(&s);

s.addWidget(txtEdit);

s.setStretchFactor(s.indexOf(txtEdit), stretch);

Trang 20

Splitters are often used, for example, to separate a main widget from a page bar.

With different-sized monitors, the relative size created here by the use of stretch

factors can quickly become a nightmare: If the space on the the screen is too

small, the page bar will appear too small, while on widescreen displays, currently

very popular on laptops, they will take up too much space In such cases it is better

to specify a fixed initial size with setSizes() and to manage the sizes defined by the

user with saveState() and restoreState()

5.3.4 Customizing Handles

The splitter handle itself is implemented in the QSplitterHandle class But

some-times the standard implementation is not enough, for example, if you want to have

the splitter collapse when it is double-clicked, similar to the way the page bar of the

Mozilla browser reacts Then you have to use your own implementation, derived

from QSplitterHandle We will call this ClickSplitterHandle

Since we want to react to a double-click, we must also reimplement the

mouseDou-bleClickEvent() method, as well as the constructor To be able to use this

double-clickable handle in a splitter, the design of QSplitter also forces us to create a

subclass of the actual splitter A method that allows a QSplitterHandle instance to

be set is not enough, since a splitter—as already explained—can have any number

of handles

Because the copy operators of QWidget-based widgets are disabled, the QSplitter

API performs a trick: The class has a protected method called createHandle(), which

is allowed to overwrite subclasses The only purpose of this factory method

con-sists of creating a new instance of QSplitterHandle or a subclass QSplitter then

uses this method when creating new handles

In the following example we will therefore create, besides the subclass of

QSplitter-Handle called ClickSplitterQSplitter-Handle, a class with the name of ClickSplitter, which

Trang 21

in-herits directly from QSplitter and which overwrites only the createHandle() method:// clicksplitter/clicksplitter.h

ClickSplitterHandle(Qt::Orientation o, QSplitter *parent = 0); void mouseDoubleClickEvent(QMouseEvent *e);

private:

int lastUncollapsedSize;

}; class ClickSplitter : public QSplitter

{

Q_OBJECT friend class ClickSplitterHandle;

public:

ClickSplitter(Qt::Orientation o, QSplitter *parent = 0) : QSplitter(o, parent) {}

ClickSplitter(QSplitter *parent = 0) : QSplitter(parent) {}

protected:

QSplitterHandle * createHandle() {

return new ClickSplitterHandle(orientation(), this);

} };

The implementation is centered on the mouseDoubleClickEvent() method In theconstructor we initialize only the class variable lastUncollapsedSize, which we laterrequire in mouseDoubleClickEvent() so that we can remember how large the widgetwas before it was collapsed:

// clicksplitter/clicksplitter.cpp

#include "clicksplitter.h"

#include <QtGui>

ClickSplitterHandle::ClickSplitterHandle(Qt::Orientation o, QSplitter *parent) :QSplitterHandle(o, parent)

Trang 22

In ClickSplitterHandle::mouseDoubleClickEvent() we first determine the alignment

of the splitter We obtain the position of the splitter, using the QSplitter::indexOf()

method This is also the position of the widget lying to the right of (or directly

beneath) the splitter

For reasons of symmetry, a zeroth handle exists in every splitter, which QSplitter

never displays This guarantees that indexOf() always delivers a sensible position

The function makes a distinction between general widgets and splitters when doing

this, and is able to determine the number of a specific widget or splitter Thus the

splitter can be defined for a widget as follows,

The ClickSplitterHandle class variable lastUncollapsedSize remembers the last size

of the widget in an uncollapsed state If this is 0 for any reason, the implementation

uses the value of the respective sizeHint() The current position of our splitter

Ngày đăng: 13/08/2014, 08:21

TỪ KHÓA LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm