没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
CSci493.73 Graphical User Interface Programming
Drawing in GTK+
Prof. Stewart Weiss
Drawing in GTK+
Background
In order to understand how to draw in GTK, you rst have to understand something about how GTK draws
widgets, because how GTK draws widgets has an important role in how you design your drawing application.
An understanding of how GTK draws widgets is also required if you ever plan to build your own custom
widgets.
Windows and Clipping
Most windowing systems are designed around the idea that an application's visual display lies within a
rectangular region on the screen called its
window
. The windowing system, e.g. Gnome or KDE or Explorer,
does not automatically save the graphical content of an application's windows; instead it asks the application
itself to
repaint
1
its windows whenever it is needed. For example, if a window that is stacked below other
windows gets raised to the top, then a client program has to repaint the area that was previously obscured.
When the windowing system asks a client program to redraw part of a window, it sends an
exposure event
to the program that contains that window. An exposure event is simply an event sent from the underlying
windowing system to a widget to notify it that it must redraw itself.
In this context, a "window" means "a rectangular region with automatic clipping", not a top-level application
window. Clipping is the act of removing portions of a window that do not need to be redrawn, or looked at
the other way, it is determining which are the only regions of a window that must be redrawn. In Figure
1, window A is below window B, which is below window C. If the user does something that brings A
forward on the screen, then the only region of window A that must be redrawn is the portion of A in the
set
A ∩ (B ∪ C)
, which is shaded in the gure.
Figure 1: Window clipping.
Most windowing systems support nested windows, which are called
child windows
. A top-level window may
contain many child windows, which may contain child windows in turn, and so on. For example, a top-level
window will typically contain a window for the menu bar, one for the document area, one for each scrollbar,
1
We will use the terms draw and paint interchangeably. Technically, the operation we are describing is painting, because
it is the application of colors and brush strokes to a canvas. Drawing is just a special kind of painting. All GUI APIs refer
to the process of visually rendering their windows as painting, or repainting, even if they describe the process occasionally as
drawing. We will keep with this tradition of equating the two terms.
1
CSci493.73 Graphical User Interface Programming
Drawing in GTK+
Prof. Stewart Weiss
and one for the status bar. In addition, controls that receive user input, such as clickable buttons, typically
have their own subwindows. It is possible for GTK+ to run on a windowing system with no notion of nested
windows. This is not a problem, because GDK presents the illusion of being under such a system, and
therefore, in GTK+, nested windows are always available.
The Drawing Cycle
Generally, the drawing cycle begins when GTK+ receives an exposure event from the underlying windowing
system, such as when the user drags a window over another one. In this case the windowing system will
tell the underlying window that it needs to repaint itself. The drawing cycle can also be initiated when a
widget itself decides that it needs to update its display. For example, when the user types a character in
a
GtkEntry
widget, the entry asks GTK+ to queue a redraw operation for itself. In other words, a widget
can call a function that requests that the windowing system itself send it an event to redraw itself! This
simplies the program, as you will soon see.
A
GdkWindow
represents a window from the underlying windowing system on which GTK+ is running. On
X11 it corresponds to a (X) Window; on Win32, it corresponds to a
HANDLE
. It is the windowing system that
generates events for these windows. These events are passed to the GDK interface, which translates these
native events into
GdkEvent
structures and sends them on to the GTK layer. In turn, the GTK widget layer
nds the widget that corresponds to a particular
GdkWindow
and emits the corresponding event signals on
that widget.
Widgets With and Without GdkWindows
If every single widget had its own
GdkWindow
, the drawing cycle would be trivial: the underlying windowing
system would notify GDK about a window that needed redrawing, GDK would pass that exposure event for
the specic
GdkWindow
to the GTK layer, and the GTK layer would emit the expose-event signal for that
widget. The widget's expose event handler would then repaint the widget. Nothing else would have to be
done; the windowing system would generate exposure events for each window that needed it, and then each
corresponding widget would draw itself in turn.
However, in practice, there are reasons for which it is more convenient and ecient to allow widgets not to
have a
GdkWindow
of their own, but to instead share the one from their parent widget. Every widget has
a
GTK_NO_WINDOW
ag that indicates whether or not the widget has its own window. If a widget does not
have its own window, it must inform GTK that it does not, when it is created, by setting this ag to
true
.
(In GTK+-2.18 and later, the widget should call
gtk_widget_set_has_window()
, passing a
false
value in
its second argument.) This is not something you would have to do as a programmer. It is the job of the
widget's implementer to do this in the widget's constructor (its
init
method.) You, as a programmer, can
test whether a particular widget has a
GdkWindow
of its own by inspecting the value of the
GTK_NO_WINDOW
ag or by calling the
gtk_widget_get_has_window()
method in GTK+-2.18 or later, which will return
true
if it has its own window
2
. Widgets that do not have their own GDK windows are called
no-window widgets
or
GTK_NO_WINDOW
widgets.
Why would you want a widget to be window-less? There are two primary reasons.
•
Some widgets may want the parent's background to show through, even when they draw on parts of
it. Such is the case, for instance, when a label is placed on a button with a themed texture. If each
widget had its own window, and therefore its own background, labels would look bad because there
would be a visible break with respect to their surroundings.
•
Reducing the total number of GDK windows reduces the volume of trac between GDK and GTK, so
if a widget can be implemented without a window, it improves performance.
2
You cannot just test whether the
window
member variable of the GdkWindow structure is NULL, because it may be sharing
its parent's GdkWindow.
2
CSci493.73 Graphical User Interface Programming
Drawing in GTK+
Prof. Stewart Weiss
Hierarchical Drawing
With this understanding, we can direct our attention to describing the sequence of steps that take place
when GTK receives the exposure event. The very rst step is that GTK nds the widget that corresponds
to the window that received the event. This widget cannot be a no-window widget, otherwise the widget
wouldn't own the window that received the event. This widget begins by painting its background. Then, if
it is a container widget, it tells each of its no-window children to paint themselves. This process is applied
recursively for all the no-window descendants of the original widget.
This process does not get propagated to widgets that have windows of their own (those for which
GTK_NO_WINDOW
is
true
). This is because if any of those widgets require redrawing, the windowing system will have already
sent exposure events to their corresponding GDK windows. Thus, there is no need to propagate the exposure
to them on the GTK+ side.
An Example (from the GTK+ API Documentation)
The following example is from the online Gnome/GTK+ documentation, The GTK+ Drawing Model,
at http://library.gnome.org/devel/gtk/unstable/chap-drawing-model.html. It demonstrates how a top-level
window would repaint itself when it has only no-window widget children.
Figure 2: Hierarchical redrawing of a window
1. The outermost, thick rectangle is a top-level
GtkWindow
, which
is not
a
GTK_NO_WINDOW
widget
therefore, it does receive its exposure event as it comes from GDK. First the
GtkWindow
would paint
its own background. Then, it would ask its only child to paint itself, which is the dotted-rectangle
numbered 2.
2. The dotted rectangle represents a
GtkVBox
, which has been made the sole child of the
GtkWindow
.
GtkBoxes, which are no-window widgets, are just layout containers that do not paint anything by
themselves, so this
GtkVBox
would draw nothing, but rather ask its children to draw themselves. The
children are numbered 3 and 6.
3. The thin rectangle is a
GtkFrame
, also a no-window widget, which has two children: a label for the
frame, numbered 4, and another label inside, numbered 5. First the frame would draw its own beveled
box, then ask the frame label and its internal child to draw themselves.
4. The frame label has no children, so it just draws its text: "Frame Label".
5. The internal label has no children, so it just draws its text: "This is some text inside the frame!".
6. The dotted rectangle represents a
GtkHBox
. Again, this does not draw anything by itself, but rather
asks its children to draw themselves. The children are numbered 7 and 9.
3
CSci493.73 Graphical User Interface Programming
Drawing in GTK+
Prof. Stewart Weiss
7. The thin rectangle is a
GtkButton
with a single child, numbered 8. First the button would draw its
beveled box, and then it would ask its child to draw itself.
8. This is a text label which has no children, so it just draws its own text: "Cancel".
9. Similar to number 7, this is a
GtkButton
with a single child, numbered 10. First the button would
draw its beveled box, and then it would ask its child to draw itself.
10. Similar to number 8, this is a
GtkLabel
which has no children, so it just draws its own text: "OK".
The above steps illustrate how a widget is redrawn. If all of this drawing were to take place directly on the
screen, there would be ickering as the various parts were redrawn. This is partly because many areas get
redrawn repeatedly, such as backgrounds and decorative elements. Therefore, GTK implements a
double
buering
scheme at the GDK level.
Double buering
In double buering, there are two canvases: an o-screen canvas and an on-screen canvas. Drawing takes
place on the o-screen canvas, which is then painted to the screen. The canvas that was on the screen then
becomes the o-screen canvas for the next drawing. In GTK, this double buering is opaque to the widgets;
they normally don't know that they are drawing on an o-screen buer; they just issue their normal drawing
commands, and the buer gets sent to the windowing system when all drawing operations are done.
Two basic functions in GDK form the core of the double-buering mechanism:
•
gdk_window_begin_paint_region()
•
gdk_window_end_paint()
.
The rst function tells a
GdkWindow
to create a temporary o-screen buer for drawing. All subsequent
drawing operations to this window get automatically redirected to that buer. The second function actually
paints the buer onto the on-screen window, and frees the buer.
Automatic double buering
It would be inconvenient for all widgets to have to call
gdk_window_begin_paint_region()
at the beginning
of their expose handlers and
gdk_window_end_paint()
at the end. To make this easier, most GTK+ widgets
have the
GTK_DOUBLE_BUFFERED
widget ag turned on by default. When GTK encounters such a widget,
it automatically calls
gdk_window_begin_paint_region()
before emitting the expose-event signal for the
widget, and then it calls
gdk_window_end_paint()
after the signal has been emitted. The terminology
needs explanation here. The signal emission process ends when all handlers for the signal have been invoked.
Remember that, in general there is a sequence of signal handler invocations when a signal is emitted. The
callback that you supply is just one of them. Thus, after the expose event signal has been handled, the buer
is painted to the screen, whether or not your application did anything to it.
This is convenient for most widgets, so that they do not need to worry about creating their own temporary
drawing buers or about calling those functions. However, some widgets may prefer to disable this kind of
automatic double buering and do things on their own. To do this, the
GTK_DOUBLE_BUFFERED
ag must
be turned o in the widget's constructor. Certain widgets, such as image viewers, would benet from this,
whereas widgets such as text viewers, which have their backgrounds drawn and redrawn repeatedly, would
not.
4
剩余16页未读,继续阅读
资源评论
tsjesyan
- 粉丝: 0
- 资源: 1
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功