ZetCode-GUI-教程-三-
ZetCode GUI 教程(三)
原文:ZetCode
Tcl/Tk 中的菜单和工具栏
在 Tcl/Tk 教程的这一部分中,我们将使用菜单和工具栏。
菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,我们必须记住许多奥术命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。 菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。
简单菜单
第一个示例将显示一个简单的菜单。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this code example, we create
# a simple menu.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
menu .mbar
. configure -menu .mbar
menu .mbar.fl -tearoff 0
.mbar add cascade -menu .mbar.fl -label File \
-underline 0
.mbar.fl add command -label Exit -command { exit }
wm title . "Simple menu"
wm geometry . 250x150+300+300
我们的示例将显示一个文件菜单,其中包含一项。 通过选择退出菜单项,我们关闭应用。
menu .mbar
. configure -menu .mbar
我们创建一个菜单栏。 菜单栏是菜单的特例。
menu .mbar.fl -tearoff 0
创建文件菜单。 -tearoff选项指定无法从菜单栏中删除菜单。
.mbar add cascade -menu .mbar.fl -label File \
-underline 0
我们将文件菜单添加到菜单栏。 -underline选项在标签的第一个字符下划线。 现在可以使用 Alt + F 快捷方式下拉菜单。
.mbar.fl add command -label Exit -command { exit }
Exit命令被添加到文件菜单。 这将创建一个菜单项。 选择菜单项后,应用终止。

图:简单菜单
子菜单
子菜单是插入另一个菜单对象的菜单。 下一个示例对此进行了演示。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this code example, we create
# a submenu.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
menu .mbar
. configure -menu .mbar
menu .mbar.fm -tearoff 0
.mbar add cascade -menu .mbar.fm -label File \
-underline 0
menu .mbar.fm.sb
.mbar.fm.sb add command -label "News feed"
.mbar.fm.sb add command -label Bookmarks
.mbar.fm.sb add command -label Mail
.mbar.fm add cascade -label Import -menu \
.mbar.fm.sb -underline 0
.mbar.fm add separator
.mbar.fm add command -label Exit -underline 0 \
-command {exit}
wm title . submenu
wm geometry . 250x150+300+300
在示例中,文件菜单的子菜单中有三个选项。 我们创建一个分隔符和键盘快捷键。
menu .mbar.fm.sb
.mbar.fm.sb add command -label "News feed"
.mbar.fm.sb add command -label Bookmarks
.mbar.fm.sb add command -label Mail
我们有一个包含三个命令的子菜单。 子菜单是常规菜单。 注意小部件路径名的层次结构。
.mbar.fm add cascade -label Import -menu \
.mbar.fm.sb -underline 0
通过将菜单添加到“文件”菜单而不是菜单栏,我们创建了一个子菜单。 下划线参数创建键盘快捷键。 我们提供了角色位置,应在下面加下划线。 在我们的情况下,这是第一个。 位置从零开始。 当我们单击“文件”菜单时,将显示一个弹出窗口。 导入菜单下划线一个字符。 我们可以使用鼠标指针或 Alt + I 快捷方式选择它。
.mbar.fm add separator
分隔符是一条水平线,可以在视觉上分隔菜单命令。 这样,我们可以将项目分组到一些合理的位置。

图:子菜单
弹出菜单
在下一个示例中,我们创建一个弹出菜单。 弹出菜单是上下文窗口小部件,可以在窗口的客户区域中的任何位置显示。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this code example, we create
# a popup menu.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
menu .m -tearoff 0
.m add command -label Beep
.m add command -label Exit -command {exit}
bind . "<Button-3>" "showMenu %X %Y"
wm title . popupmenu
wm geometry . 250x150+300+300
proc showMenu {x y} {
tk_popup .m $x $y
}
在我们的示例中,我们使用两个命令创建一个弹出菜单。
menu .m -tearoff 0
.m add command -label Beep
.m add command -label Exit -command {exit}
上下文菜单是常规的menu小部件。 tearoff函数已关闭。
bind . "<Button-3>" "showMenu %X %Y"
我们将<Button-3>事件绑定到showMenu过程。 当我们右键单击窗口的客户区域时,将生成事件。 我们将两个参数传递给该过程。 这些是鼠标单击的 x 和 y 坐标。
proc showMenu {x y} {
tk_popup .m $x $y
}
showMenu过程显示上下文菜单。 弹出菜单显示在鼠标单击的 x 和 y 坐标处。 要显示弹出菜单,我们使用tk_popup命令。

图:弹出菜单
工具栏
菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 Tk 中没有工具栏小部件。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this code example, we create
# a toolbar.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
package require Img
menu .mbar
. configure -menu .mbar
menu .mbar.fl -tearoff 0
.mbar add cascade -menu .mbar.fl -label File \
-underline 0
frame .toolbar -bd 1 -relief raised
image create photo img -file "exit.png"
button .toolbar.exitButton -image img -relief flat -command {exit}
pack .toolbar.exitButton -side left -padx 2 -pady 2
pack .toolbar -fill x
wm title . toolbar
wm geometry . 250x150+300+300
我们的工具栏将是一个框架,在该框架上将放置一个按钮。
frame .toolbar -bd 1 -relief raised
工具栏已创建。 它是frame。 我们创建了一个凸起的边框,以便工具栏的边界可见。
image create photo img -file "exit.png"
button .toolbar.exitButton -image img -relief flat -command {exit}
创建带有图像的退出按钮。
pack .toolbar.exitButton -side left -padx 2 -pady 2
工具栏是框架,框架是容器小部件。 我们将按钮包装在左侧。 我们添加一些填充。
pack .toolbar -fill x
工具栏打包在根窗口中; 它是水平拉伸的。

图:工具栏
在 Tcl/Tk 教程的这一部分中,我们展示了如何创建带有菜单和菜单项以及工具栏的菜单栏。
Tcl/Tk 中的对话框
在 Tcl/Tk 教程的这一部分中,我们将使用对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
MessageDialog
消息框是方便的对话框,可向应用的用户提供消息。 该消息由文本和图像数据组成。 用tk_messageBox命令创建 Tk 中的消息框。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this program, we show various
# message boxes.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
frame .fr
pack .fr
ttk::button .fr.erButton -text Error -command onError
grid .fr.erButton
ttk::button .fr.wButton -text Warning -command onWarn
grid .fr.wButton -row 1 -column 0
ttk::button .fr.queButton -text Question -command onQuest
grid .fr.queButton -row 0 -column 1 -sticky we -columnspan 6
ttk::button .fr.infButton -text Information -command onInfo
grid .fr.infButton -row 1 -column 1
proc onError {} {
tk_messageBox -type ok -icon error -title Error \
-message "Could not open file"
}
proc onWarn {} {
tk_messageBox -type ok -icon warning -title Warning \
-message "Deprecated function call"
}
proc onQuest {} {
tk_messageBox -type ok -icon question -title Question \
-message "Are you sure to quit?"
}
proc onInfo {} {
tk_messageBox -type ok -icon info -title Information \
-message "Download completed"
}
wm title . "message boxes"
wm geometry . 300x150+300+300
我们使用网格管理器来设置四个按钮的网格。 每个按钮显示一个不同的消息框。
ttk::button .fr.erButton -text Error -command onError
grid .fr.erButton
我们创建一个错误按钮,它调用onError过程。 在方法内部,我们显示错误消息对话框。 该按钮将放置在网格的第一个单元格中。 ttk名称空间内的小部件为主题。 就功能而言,button和ttk::button是相同的按钮。 不同之处在于我们可以将主题应用于后者。
proc onError {} {
tk_messageBox -type ok -icon error -title Error \
-message "Could not open file"
}
如果按下错误按钮,则会显示错误对话框。 我们使用tk_messageBox命令创建消息框。 -type选项指定对话框中显示哪些按钮。 在我们的情况下,这是一个确定按钮。 -icon指定要显示的图标的类型。 -title提供对话框的标题,-message提供消息。

图:警告消息对话框
颜色选择器
颜色选择器是用于选择颜色的对话框。 我们使用tk_chooseColor命令显示对话框。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this script, we use tk_chooseColor
# dialog to change the colour of the text.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
label .l -text ZetCode
place .l -x 20 -y 90
button .b -text "Choose a color..." \
-command "onSelect .l"
place .b -x 20 -y 30
wm title . "color dialog"
wm geometry . 350x200+300+300
proc onSelect {widget} {
set col \
[tk_chooseColor -title "Choose a color" -parent .]
$widget configure -foreground $col
}
我们有一个按钮和一个标签。 单击按钮,我们显示一个颜色选择器对话框。 我们将通过从对话框中选择一种颜色来更改标签文本的颜色。
label .l -text ZetCode
place .l -x 20 -y 90
我们创建一个label小部件并将其放在窗口中。
button .b -text "Choose a color..." \
-command "onSelect .l"
place .b -x 20 -y 30
我们创建一个button小部件并将其放在窗口中。 我们将标签的窗口小部件路径传递给onSelect过程,该过程显示对话框并更改标签的颜色。
proc onSelect {widget} {
set col \
[tk_chooseColor -title "Choose a color" -parent .]
$widget configure -foreground $col
}
在onSelect程序内部,我们显示对话框并更改标签颜色。 首先,我们显示对话框并将所选的颜色值存储在col变量中。 稍后,我们使用configure命令更改标签的前景。 该命令在小部件的路径名上执行。 标签的路径名已传递给过程。

图:颜色选择器
文件对话框
tk_getOpenFile对话框允许用户从文件系统中选择文件。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this program, we use the
# tk_getOpenFile dialog to select a file from
# a filesystem.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
set types {
{"All Source Files" {.tcl .tk } }
{"Image Files" {.gif .png .jpg} }
{"All files" *}
}
proc onSelect { label } {
global types
set file [tk_getOpenFile -filetypes $types -parent .]
$label configure -text $file
}
label .l -text "..."
place .l -x 20 -y 90
button .b -text "Select a file" \
-command "onSelect .l"
place .b -x 20 -y 30
wm title . "openfile"
wm geometry . 350x200+300+300
在我们的代码示例中,我们使用tk_getOpenFile对话框选择一个文件,并在label小部件中显示其名称。
set types {
{"All Source Files" {.tcl .tk } }
{"Image Files" {.gif .png .jpg} }
{"All files" *}
}
这些是文件过滤器。 这些过滤器可用于仅显示对话框中的特定文件。
proc onSelect { label } {
global types
set file [tk_getOpenFile -filetypes $types -parent .]
$label configure -text $file
}
我们使用tk_getOpenFile命令显示对话框。 我们使用-filetypes选项应用文件过滤器。 所选文件名存储在file变量中。 configure命令用于更改标签的文本。

图:tk_getOpenFile
在 Tcl/Tk 教程的这一部分中,我们使用了对话框窗口。
Tcl/Tk 绘图
在 Tcl/Tk 教程的这一部分中,我们将做一些绘图。 在canvas小部件上完成 Tk 的绘制。 画布是 Tk 中图形的高级工具。
它可以用于创建图表,自定义窗口小部件或创建游戏。
色彩
颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。
#!/usr/bin/wish
# ZetCode Tcl Tk tutorial
#
# This program draws three
# rectangles filled with different
# colours.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
canvas .can
.can create rect 30 10 120 80 \
-outline #fb0 -fill #fb0
.can create rect 150 10 240 80 \
-outline #f50 -fill #f50
.can create rect 270 10 370 80 \
-outline #05f -fill #05f
pack .can
wm title . "Colours"
wm geometry . 400x100+300+300
在代码示例中,我们绘制了三个矩形,并用不同的颜色值填充了它们。
canvas .can
我们创建canvas小部件。
.can create rect 30 10 120 80 \
-outline #fb0 -fill #fb0
使用create命令,我们在画布上创建一个新的矩形项目。 前四个参数是两个边界点的 x,y 坐标:左上角和右下角。 使用-outline选项,我们可以控制矩形轮廓的颜色。 同样,-fill选项为矩形的内部提供颜色。

图:颜色
形状
我们可以在画布上绘制各种形状。 以下代码示例将显示其中的一些。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this script, we draw basic
# shapes on the canvas.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
canvas .can
.can create oval 10 10 80 80 -outline #777 \
-fill #777
.can create oval 110 10 210 80 -outline #777 \
-fill #777
.can create rect 230 10 290 60 -outline #777 \
-fill #777
.can create arc 30 200 90 100 -start 0 -extent 210 \
-outline #777 -fill #777
set points [ list 150 100 200 120 240 180 210 \
200 150 150 100 200 ]
.can create polygon $points -outline #777 \
-fill #777
pack .can
wm title . "shapes"
wm geometry . 330x220+300+300
我们在窗口上绘制五个不同的形状:一个圆形,一个椭圆形,一个矩形,一个弧形和一个多边形。 轮廓和内部以相同的灰色绘制。
.can create oval 10 10 80 80 -outline #777 \
-fill #777
create oval创建一个圆。 前四个参数是圆的边界框坐标。 换句话说,它们是在其中绘制圆的框的左上和右下点的 x,y 坐标。
.can create rect 230 10 290 60 -outline #777 \
-fill #777
我们创建一个矩形项目。 坐标还是要绘制的矩形的边界框。
.can create arc 30 200 90 100 -start 0 -extent 210 \
-outline #777 -fill #777
该代码行创建了一条弧。 圆弧是圆的圆周的一部分。 我们提供边界框。 -start选项是圆弧的起始角度。 -extent是角度大小。
set points [ list 150 100 200 120 240 180 210 \
200 150 150 100 200 ]
.can create polygon $points -outline #777 \
-fill #777
创建一个多边形。 它是具有多个角的形状。 要在 Tk 中创建多边形,我们向create polygon命令提供了多边形坐标列表。

图:形状
绘制图像
在以下示例中,我们将在画布上创建一个图像项。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# This program draws an image
# on the canvas widget.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
package require Img
image create photo img -file "tatras.jpg"
set height [image height img]
set width [image width img]
canvas .can -height $height -width $width
.can create image 0 0 -anchor nw -image img
pack .can
wm title . "High Tatras"
wm geometry . +300+300
我们在画布上显示图像。
image create photo img -file "tatras.jpg"
我们从位于当前工作目录中的 JPG 图像创建照片图像。
set height [image height img]
set width [image width img]
我们得到图像的高度和宽度。
canvas .can -height $height -width $width
我们创建canvas小部件。 它考虑了图像的大小。
.can create image 0 0 -anchor nw -image img
我们使用create image命令在画布上创建一个图像项。 为了显示整个图像,它固定在北部和西部。 -image选项提供要显示的照片图像。
绘制文字
在最后一个示例中,我们将在窗口上绘制文本。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# In this script, we draw text
# on the window.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
canvas .can
.can create text 10 30 -anchor w -font Purisa \
-text "Most relationships seem so transitory"
.can create text 10 60 -anchor w -font Purisa \
-text "They're good but not the permanent one"
.can create text 10 110 -anchor w -font Purisa \
-text "Who doesn't long for someone to hold"
.can create text 10 140 -anchor w -font Purisa \
-text "Who knows how to love without being told"
.can create text 10 170 -anchor w -font Purisa \
-text "Somebody tell me why I'm on my own"
.can create text 10 200 -anchor w -font Purisa \
-text "If there's a soulmate for everyone"
pack .can
wm title . "lyrics"
wm geometry . 430x250+300+300
我们在窗口上画一首歌的歌词。
.can create text 10 30 -anchor w -font Purisa \
-text "Most relationships seem so transitory"
前两个参数是文本中心点的 x,y 坐标。 如果我们将文本项锚定在西方,则文本将从该位置开始。 -font选项提供文本的字体,而-text 选项是要显示的文本。

图:绘制文本
在 Tcl/Tk 教程的这一部分中,我们做了一些绘图。
贪食蛇
在 Tcl/Tk 教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。
我们使用canvas小部件来创建游戏。 游戏中的对象是图像。 我们使用画布命令创建图像项。 我们使用画布命令使用标签在画布上查找项目并进行碰撞检测。
#!/usr/bin/wish
# ZetCode Tcl/Tk tutorial
#
# This is simple Nibbles game clone.
#
# author: Jan Bodnar
# last modified: March 2011
# website: www.zetcode.com
package require Img
set WIDTH 300
set HEIGHT 300
set DELAY 100
set DOT_SIZE 10
set ALL_DOTS [expr $WIDTH * $HEIGHT / ($DOT_SIZE * $DOT_SIZE)]
set RAND_POS 27
canvas .c -width $WIDTH -height $HEIGHT -background black
pack .c
proc initGame {} {
set ::left false
set ::right true
set ::up false
set ::down false
set ::inGame true
set dots 3
set ::apple_x 100
set ::apple_y 190
for {set i 0} {$i<$dots} {incr i} {
set x($i) [expr 50 - $i * 10]
set y($i) 50
}
set ::idot [image create photo img1 -file "dot.png"]
set ::ihead [image create photo img2 -file "head.png"]
set ::iapple [image create photo img3 -file "apple.png"]
createObjects
locateApple
bind . "<Key>" "onKeyPressed %K"
after $::DELAY onTimer
}
proc createObjects {} {
.c create image $::apple_x $::apple_y \
-image $::iapple -tag apple -anchor nw
.c create image 50 50 -image $::ihead -tag head -anchor nw
.c create image 30 50 -image $::idot -tag dot -anchor nw
.c create image 40 50 -image $::idot -tag dot -anchor nw
}
proc checkApple {} {
set apple [.c find withtag apple]
set head [.c find withtag head]
set l [.c bbox head]
set overlap [eval .c find overlapping $l]
foreach over $overlap {
if {$over == $apple} {
set crd [.c coords $apple]
set x [lindex $crd 0]
set y [lindex $crd 1]
.c create image $x $y -image $::idot -anchor nw -tag dot
locateApple
}
}
}
proc doMove {} {
set dots [.c find withtag dot]
set head [.c find withtag head]
set items [concat $dots $head]
set z 0
while {$z < [expr [llength $items] - 1]} {
set c1 [.c coords [lindex $items $z]]
set c2 [.c coords [lindex $items [expr $z+1]]]
.c move [lindex $items $z] [expr [lindex $c2 0] - [lindex $c1 0] ] \
[expr [lindex $c2 1] - [lindex $c1 1] ]
incr z
}
if { [string compare $::left true] == 0} {
.c move head -$::DOT_SIZE 0
}
if {[string compare $::right true] == 0} {
.c move head $::DOT_SIZE 0
}
if {[string compare $::up true] == 0} {
.c move head 0 -$::DOT_SIZE
}
if {[string compare $::down true] == 0} {
.c move head 0 $::DOT_SIZE
}
}
proc checkCollisions {} {
set dots [.c find withtag dot]
set head [.c find withtag head]
set l [.c bbox head]
set overlap [eval .c find overlapping $l]
foreach dot $dots {
foreach over $overlap {
if {$over == $dot} {
set ::inGame false
}
}
}
set x1 [lindex $l 0]
set y1 [lindex $l 1]
if {$x1 < 0} {
set ::inGame false
}
if {$x1 > [expr $::WIDTH - $::DOT_SIZE]} {
set ::inGame false
}
if {$y1 < 0} {
set ::inGame false
}
if {$y1 > [expr $::HEIGHT - $::DOT_SIZE]} {
set ::inGame false
}
}
proc locateApple {} {
set apple [.c find withtag apple]
.c delete lindex apple 0
set r [expr round(rand() * $::RAND_POS)]
set ::apple_x [expr $r * $::DOT_SIZE]
set r [expr round(rand() * $::RAND_POS)]
set ::apple_y [expr $r * $::DOT_SIZE]
.c create image $::apple_x $::apple_y -anchor nw \
-image $::iapple -tag apple
}
proc onKeyPressed {key} {
set a1 [ expr [string compare $key Left] == 0]
set a2 [ expr [string compare $::right true] != 0]
if { $a1 && $a2 } {
set ::left true
set ::up false
set ::down false
}
set b1 [ expr [string compare $key Right] == 0]
set b2 [ expr [string compare $::left true] != 0]
if { $b1 && $b2 } {
set ::right true
set ::up false
set ::down false
}
set c1 [ expr [string compare $key Up] == 0]
set c2 [ expr [string compare $::down true] != 0]
if { $c1 && $c2 } {
set ::up true
set ::left false
set ::right false
}
set d1 [ expr [string compare $key Down] == 0]
set d2 [ expr [string compare $::up true] != 0]
if { $d1 && $d2 } {
set ::down true
set ::left false
set ::right false
}
}
proc onTimer {} {
if {$::inGame} {
checkCollisions
checkApple
doMove
after $::DELAY onTimer
} else {
gameOver
}
}
proc gameOver {} {
.c delete all
set x [ expr [winfo width .] / 2 ]
set y [ expr [winfo height .] / 2]
.c create text $x $y -text "Game over" -fill white
}
initGame
wm title . "Nibbles"
wm geometry . +150+150
首先,我们将定义一些在游戏中使用的常量。
WIDTH和HEIGHT常数确定电路板的大小。 DELAY常数确定游戏的速度。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。
initGame过程初始化变量,加载图像并启动超时过程。
set ::idot [image create photo img1 -file "dot.png"]
set ::ihead [image create photo img2 -file "head.png"]
set ::iapple [image create photo img3 -file "apple.png"]
在这些行中,我们加载图像。 贪食蛇游戏中有三个图像。 头,圆点和苹果。
createObjects
locateApple
createObjects过程在画布上创建项目。 locateApple在画布上随机放置一个苹果。
bind . "<Key>" "onKeyPressed %K"
我们将键盘事件绑定到onKeyPressed过程。 游戏由键盘光标键控制。 %K是所按下键的 Tk 符号名称。 它被传递到onKeyPressed过程。
proc createObjects {} {
.c create image $::apple_x $::apple_y \
-image $::iapple -tag apple -anchor nw
.c create image 50 50 -image $::ihead -tag head -anchor nw
.c create image 30 50 -image $::idot -tag dot -anchor nw
.c create image 40 50 -image $::idot -tag dot -anchor nw
}
在createObjects过程中,我们在画布上创建游戏对象。 这些是帆布物品。 它们被赋予初始的 x,y 坐标。 -image选项提供要显示的图像。 -anchor选项设置为nw; 这样,画布项目的坐标就是项目的左上角。 如果我们希望能够在根窗口的边框旁边显示图像,这很重要。 如果您不理解我们的意思,请尝试删除锚点选项。 -tag选项用于识别画布上的项目。 一个标签可用于多个画布项目。
checkApple过程检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形接头并称为locateApple。
set apple [.c find withtag apple]
set head [.c find withtag head]
find withtag命令使用其标签在画布上找到一个项目。 我们需要两个项目。 蛇和苹果的头。
set l [.c bbox head]
set overlap [eval .c find overlapping $l]
bbox命令返回项目的边界框点。 find overlapping命令查找给定坐标的冲突项。
foreach over $overlap {
if {$over == $apple} {
set crd [.c coords $apple]
set x [lindex $crd 0]
set y [lindex $crd 1]
.c create image $x $y -image $::idot -anchor nw -tag dot
locateApple
}
}
如果苹果与头部碰撞,我们将在苹果对象的坐标处创建一个新的点项目。 我们调用locateApple过程,该过程将从画布上删除旧的苹果项目,然后创建并随机放置一个新的项目。
在doMove过程中,我们有了游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
set z 0
while {$z < [expr [llength $items] - 1]} {
set c1 [.c coords [lindex $items $z]]
set c2 [.c coords [lindex $items [expr $z+1]]]
.c move [lindex $items $z] [expr [lindex $c2 0] - [lindex $c1 0] ] \
[expr [lindex $c2 1] - [lindex $c1 1] ]
incr z
}
该代码将关节向上移动。
if { [string compare $::left true] == 0} {
.c move head -$::DOT_SIZE 0
}
将头向左移动。
在checkCollisions程序中,我们确定蛇是否击中了自己或撞墙之一。
set l [.c bbox head]
set overlap [eval .c find overlapping $l]
foreach dot $dots {
foreach over $overlap {
if {$over == $dot} {
set ::inGame false
}
}
}
如果蛇用头撞到关节之一,我们就结束游戏。
if {$y1 > [expr $::HEIGHT - $::DOT_SIZE]} {
set ::inGame false
}
如果蛇击中了棋盘的底部,我们将结束游戏。
locateApple过程会在板上随机找到一个新苹果,然后删除旧的苹果。
set apple [.c find withtag apple]
.c delete lindex apple 0
在这里,我们找到并删除了被蛇吃掉的苹果。
set r [expr round(rand() * $::RAND_POS)]
我们得到一个从 0 到RAND_POS-1的随机数。
set ::apple_x [expr $r * $::DOT_SIZE]
...
set ::apple_y [expr $r * $::DOT_SIZE]
这些行设置了apple对象的 x,y 坐标。
在onKeyPressed程序中,我们确定所按下的键。
set a1 [ expr [string compare $key Left] == 0]
set a2 [ expr [string compare $::right true] != 0]
if { $a1 && $a2 } {
set ::left true
set ::up false
set ::down false
}
如果按左光标键,则将left变量设置为true。 在doMove过程中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
proc onTimer {} {
if {$::inGame} {
checkCollisions
checkApple
doMove
after $::DELAY onTimer
} else {
gameOver
}
}
每DELAY ms,将调用onTimer过程。 如果我们参与了游戏,我们将调用三个构建游戏逻辑的过程。 否则,游戏结束。 计时器基于after命令,该命令仅在DELAY ms 之后调用一次过程。 要重复调用计时器,我们递归调用onTimer过程。
proc gameOver {} {
.c delete all
set x [ expr [winfo width .] / 2 ]
set y [ expr [winfo height .] / 2]
.c create text $x $y -text "Game over" -fill white
}
如果游戏结束,我们将删除画布上的所有项目。 然后,在屏幕中央绘制"Game Over"。

图:贪食蛇
这是用 Tcl/Tk 创建的贪食蛇电脑游戏。
Java SWT 教程
这是 Java SWT 教程。 在本教程中,我们将学习使用 Java SWT 库进行 GUI 编程的基础。 本教程适合初学者和中级程序员。 本教程使用 JDK 8。
目录
SWT
标准窗口小部件工具箱(SWT)是用于 Java 编程语言的图形窗口小部件工具箱。 它最初由 IBM 开发,现已成为 Eclipse Foundation 的一部分。 它是 Swing 和 JavaFX 的替代方法。 SWT 使用 Windows API 或 GTK+ 之类的本地 GUI API 通过 Java 本机接口(JNI)创建其小部件。
相关教程和电子书
有高级 Java Swing 电子书, Java Swing 教程, JavaFX 教程和 Java 教程。
Java SWT 简介
在 Java SWT 编程教程的这一部分中,我们介绍 Java SWT 库并创建我们的第一个程序。
本教程的目的是帮助您开始使用 Java SWT 工具包。 可以在此处下载本教程中使用的图像。 我使用了 Gnome 项目的探戈图标包中的一些图标。
关于
标准窗口小部件工具箱(SWT)是用于 Java 编程语言的图形窗口小部件工具箱。 它最初是由 IBM 开发的。 它是 Swing 和 JavaFX 的替代方法。 SWT 使用 Winapi 和 GTK+ 等本机 GUI API 通过 Java 本机接口(JNI)创建其小部件。
构建 SWT 应用
在 NetBeans 下,我们从官方网站下载了 SWT 包,并将swt.jar添加到项目库中。

图:将swt.jar添加到 NetBeans 项目
对于 Eclipse,我们右键单击项目,然后选择“构建路径”,“配置构建路径”。 我们单击“添加外部 JAR ...”按钮,然后选择平台特定的 JAR 文件。
使窗口居中
在第一个示例中,我们创建一个简单的窗口。 窗口在屏幕上居中。
CenterWindowEx.java
package com.zetcode;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this program, we show a window in
* the center of the screen
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: May 2015
*/
public class CenterWindowEx {
public CenterWindowEx(Display display) {
Shell shell = new Shell(display);
shell.setText("Center");
shell.setSize(250, 200);
centerWindow(shell);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void centerWindow(Shell shell) {
Rectangle bds = shell.getDisplay().getBounds();
Point p = shell.getSize();
int nLeft = (bds.width - p.x) / 2;
int nTop = (bds.height - p.y) / 2;
shell.setBounds(nLeft, nTop, p.x, p.y);
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
CenterWindowEx ex = new CenterWindowEx(display);
display.dispose();
}
}
本示例在屏幕中央显示一个250x200像素的窗口。 在每个 SWT 应用中,都有两个重要的类:Display和Shell。 Display是 SWT 与基础 OS 之间的连接。 它实现了事件循环并提供了有关操作系统的信息。 Shell代表一个窗口。 有顶级的外壳; 这些将Display作为父项。 其他外壳称为辅助外壳。
Shell shell = new Shell(display);
创建一个顶层窗口。
shell.setText("Center");
我们使用setText()方法为窗口设置标题。
shell.setSize(250, 200);
在这里,我们为外壳设置大小。
shell.open();
窗口显示在屏幕上。
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
这些行启动事件主循环。
Rectangle bds = shell.getDisplay().getBounds();
我们得到屏幕的分辨率。 如果使用多个显示器,则可能需要调用getMonitor()方法而不是getDisplay()。
int nLeft = (bds.width - p.x) / 2;
int nTop = (bds.height - p.y) / 2;
我们计算窗口的左坐标和顶坐标。
shell.setBounds(nLeft, nTop, p.x, p.y);
我们使用setBounds()方法设置壳的边界。
Display display = new Display();
创建了Display。
CenterWindowEx ex = new CenterWindowEx(display);
我们实例化示例程序。
display.dispose();
应用终止后,我们释放操作系统资源。
创建工具提示
第二个示例显示了一个工具提示。 工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。
TooltipEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this program, we show a tooltip.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: May 2015
*/
public class TooltipEx {
public TooltipEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setText("Tooltip");
shell.setToolTipText("This is a window");
shell.setSize(250, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
TooltipEx ex = new TooltipEx(display);
display.dispose();
}
}
该示例创建一个窗口。 如果将鼠标指针悬停在窗口区域上方,则会弹出一个工具提示。
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
style参数指定外壳的行为。 传递SWT.CENTER选项可使外壳位于窗口的中心。 SWT.SHELL_TRIM在窗口上装饰。 它启用标题和标题栏按钮,并使窗口可调整大小。 这是外壳的默认样式。
shell.setToolTipText("This is a window");
此行为窗口创建工具提示。

图:工具提示
退出按钮
在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。
QuitButtonEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a button on a window.
* Clicking on the button, we terminate
* the application.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: May 2015
*/
public class QuitButtonEx {
public QuitButtonEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginLeft = 50;
layout.marginTop = 50;
shell.setLayout(layout);
Button quitBtn = new Button(shell, SWT.PUSH);
quitBtn.setText("Quit");
quitBtn.setLayoutData(new RowData(80, 30));
quitBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
shell.getDisplay().dispose();
System.exit(0);
}
});
shell.setText("Quit button");
shell.setSize(250, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
QuitButtonEx ex = new QuitButtonEx(display);
display.dispose();
}
}
该示例中有一个Button小部件; 单击该按钮可终止该应用。
RowLayout layout = new RowLayout();
layout.marginLeft = 50;
layout.marginTop = 50;
shell.setLayout(layout);
RowLayout用于将按钮定位在窗口上。 该布局类将小部件放入简单的行或列中。
Button quitBtn = new Button(shell, SWT.PUSH);
Button小部件已创建。 它的父级是外壳。 SWT.PUSH指定按钮的类型。
quitBtn.setText("Quit");
我们使用setText()方法为按钮设置标签。
quitBtn.setLayoutData(new RowData(80, 30));
setLayoutData()方法指定退出按钮的布局数据。 在这种情况下,这些是按钮的大小。
quitBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
shell.getDisplay().dispose();
System.exit(0);
}
});
我们为按钮添加一个选择监听器。 当我们单击按钮时,将调用widgetSelected()方法。 在此方法内部,我们释放 OS 资源并退出应用。

图:退出按钮
助记符
助记符是用于激活支持助记符的窗口小部件的快捷键。 例如,它们可以与标签,按钮或菜单项一起使用。
助记符是通过在小部件的标签上添加&字符来创建的。 它使下一个字符成为助记符。 字符与无鼠标修饰符(通常为 Alt )结合在一起。 选择的字符带有下划线,但是可以以平台特定的方式强调。 在某些平台上,仅在按下无鼠标修饰符后才对字符加下划线。
MnemonicEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode SWT tutorial
*
* This program creates a mnemonic for
* a button widget.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class MnemonicEx {
public MnemonicEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginLeft = 30;
layout.marginTop = 30;
layout.marginBottom = 150;
layout.marginRight = 150;
shell.setLayout(layout);
Button btn = new Button(shell, SWT.PUSH);
btn.setText("&Button");
btn.setLayoutData(new RowData(80, 30));
btn.addListener(SWT.Selection, event -> System.out.println("Button clicked"));
shell.setText("Mnemonic");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
MnemonicEx ex = new MnemonicEx(display);
display.dispose();
}
}
我们为按钮小部件设置了助记符。 可以使用 Alt + B 键盘快捷键激活。
Button btn = new Button(shell, SWT.PUSH);
btn.setText("&Button");
通过在按钮的标签上添加&字符来创建助记符。 Alt + B 快捷键现在激活该按钮。
btn.addListener(SWT.Selection, event -> System.out.println("Button clicked"));
激活后,该按钮会将消息打印到控制台。 lambda 表达式用于向按钮添加监听器。
目前,有三种激活按钮的方式:单击鼠标左键, Alt + B 快捷方式以及空格键 按钮具有焦点)。
本章是 Java SWT 库的简介。
Java SWT 中的布局管理
在本章中,我们将展示如何在窗口或对话框中布置窗口小部件。
在设计应用的 GUI 时,我们决定使用哪些小部件以及如何在应用中组织这些小部件。 为了组织小部件,我们使用称为布局容器的专用非可见小部件。
Composite是用于放置子窗口小部件的容器。 Composite的布局管理器是通过setLayout()方法设置的。 Shell也是Composite。 它没有默认的布局管理器,在这种情况下,将使用绝对定位来放置小部件。
SWT 具有以下标准布局类:
FillLayoutRowLayoutFormLayoutGridLayout
FillLayout在单个行或列中布置大小相等的小部件。 RowLayout在行或列中布置小部件,并具有填充,环绕和间距选项。 FormLayout通过为小部件的每一侧创建附件来布局小部件。 GridLayout将小部件布置在网格中。
布局类可以具有对应的布局数据类,其中包含特定子项的布局数据。 例如,RowLayout具有名为RowData的布局数据类,GridLayout具有GridData,而FormLayout具有FormData。
绝对定位
在大多数情况下,程序员应使用布局管理器。 在某些情况下,我们也可以使用绝对定位。 在绝对定位中,程序员以像素为单位指定每个小部件的位置和大小。 如果我们调整窗口大小,则小部件的大小和位置不会改变。 在各种平台上,应用看起来都不同,在 Linux 上看起来不错,在 Mac OS 上看起来不太正常。 在应用中更改字体可能会破坏布局。 如果我们将应用翻译成另一种语言,则必须重做布局。 对于所有这些问题,仅在有理由的情况下才使用绝对定位,或者您的应用是简单的测试。
绝对定位是通过setSize(),setLocation()和setBounds()方法完成的。
AbsoluteLayoutEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this program, we position two
* buttons using absolute coordinates.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class AbsoluteLayoutEx {
public AbsoluteLayoutEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Button btn1 = new Button(shell, SWT.PUSH);
btn1.setText("Button");
btn1.setBounds(20, 50, 80, 30);
Button btn2 = new Button(shell, SWT.PUSH);
btn2.setText("Button");
btn2.setSize(80, 30);
btn2.setLocation(50, 100);
shell.setText("Absolute layout");
shell.setSize(300, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
AbsoluteLayoutEx ex = new AbsoluteLayoutEx(display);
display.dispose();
}
}
在我们的示例中,我们使用绝对定位在窗口上放置了两个按钮。
btn1.setBounds(20, 50, 80, 30);
setBounds()方法有两件事:将按钮定位在x = 20和y = 50,并将按钮的大小设置为width = 80和height = 30。
button2.setSize(80, 30);
button2.setLocation(50, 100);
在这里,我们分两个步骤进行相同的操作。 首先,我们使用setSize()方法调整按钮的大小。 然后,我们使用setLocation()方法将其定位在窗口上。

图:绝对布局
FillLayout管理器
FillLayout是最简单的布局类。 它将小部件布置在一行或一列中,迫使它们具有相同的大小。
FillLayoutEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program demonstrates the FillLayout
* manager
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: May 2015
*/
public class FillLayoutEx {
private Image castle;
public FillLayoutEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setLayout(new FillLayout());
loadImage(shell);
Label label = new Label(shell, SWT.IMAGE_PNG);
label.setImage(castle);
shell.setText("FillLayout");
Rectangle rect = castle.getBounds();
shell.setSize(rect.width, rect.height);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void loadImage(Shell shell) {
Device dev = shell.getDisplay();
try {
castle = new Image(dev, "redrock.png");
} catch(Exception e) {
System.out.println("Cannot load image");
System.out.println(e.getMessage());
System.exit(1);
}
}
@Override
public void finalize() {
castle.dispose();
}
public static void main(String[] args) {
Display display = new Display();
FillLayoutEx app = new FillLayoutEx(display);
app.finalize();
display.dispose();
}
}
在我们的示例中,我们使用此管理器显示图像。
shell.setLayout(new FillLayout());
我们将FillLayout设置为外壳的布局类。 使用setLayout()方法设置布局。
Rectangle rect = castle.getBounds();
shell.setSize(rect.width, rect.height);
我们找出图片的大小来调整外壳的大小,以完全适合图像的大小。
Label label = new Label(shell, SWT.IMAGE_PNG);
label.setImage(castle);
我们将图像设置为标签小部件。
private void loadImage(Shell shell) {
Device dev = shell.getDisplay();
try {
castle = new Image(dev, "redrock.png");
} catch(Exception e) {
System.out.println("Cannot load image");
System.out.println(e.getMessage());
System.exit(1);
}
}
loadImage()方法从磁盘加载图像。

图:FillLayout
RowLayout
RowLayout管理器将所有小部件放置在一行或一列中。
RowLayoutEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program demonstrates the RowLayout
* manager.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class RowLayoutEx {
public RowLayoutEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
rowLayout.marginTop = 10;
rowLayout.marginBottom = 10;
rowLayout.marginLeft = 5;
rowLayout.marginRight = 5;
rowLayout.spacing = 10;
shell.setLayout(rowLayout);
Button btn1 = new Button(shell, SWT.PUSH);
btn1.setText("Button");
btn1.setLayoutData(new RowData(80, 30));
Button btn2 = new Button(shell, SWT.PUSH);
btn2.setText("Button");
btn2.setLayoutData(new RowData(80, 30));
Button btn3 = new Button(shell, SWT.PUSH);
btn3.setText("Button");
btn3.setLayoutData(new RowData(80, 30));
shell.setText("RowLayout");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
RowLayoutEx ex = new RowLayoutEx(display);
display.dispose();
}
}
在我们的示例中,我们创建了三个按钮的行。
RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
将创建水平RowLayout。 这些小部件将放置在一行中。
rowLayout.marginTop = 10;
rowLayout.marginBottom = 10;
rowLayout.marginLeft = 5;
rowLayout.marginRight = 5;
边距指定沿容器边缘的空间。
rowLayout.spacing = 10;
spacing属性指定按钮之间的间距。
shell.setLayout(rowLayout);
我们将行布局指定为外壳布局。
Button btn1 = new Button(shell, SWT.PUSH);
btn1.setText("Button");
btn1.setLayoutData(new RowData(80, 30));
创建了Button。 setLayoutData()指定按钮的大小。

图:RowLayout管理器
按钮
在最后一个示例中,我们使用FormLayout管理器创建一个示例。 该管理器使用两个对象FormData和FormAttachment控制子项的位置和大小。
ButtonsEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this program, we position two buttons
* in the bottom right corner of the window.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: May 2015
*/
public class ButtonsEx {
public ButtonsEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
FormLayout layout = new FormLayout();
shell.setLayout(layout);
Button okBtn = new Button(shell, SWT.PUSH);
okBtn.setText("OK");
Button cancBtn = new Button(shell, SWT.PUSH);
cancBtn.setText("Cancel");
FormData cancelData = new FormData(80, 30);
cancelData.right = new FormAttachment(98);
cancelData.bottom = new FormAttachment(95);
cancBtn.setLayoutData(cancelData);
FormData okData = new FormData(80, 30);
okData.right = new FormAttachment(cancBtn, -5, SWT.LEFT);
okData.bottom = new FormAttachment(cancBtn, 0, SWT.BOTTOM);
okBtn.setLayoutData(okData);
shell.setText("Buttons");
shell.setSize(350, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
ButtonsEx ex = new ButtonsEx(display);
display.dispose();
}
}
在此代码示例中,我们在窗口的右下角放置了两个按钮。
FormLayout layout = new FormLayout();
shell.setLayout(layout);
FormLayout管理器已创建。
Button okBtn = new Button(shell, SWT.PUSH);
okBtn.setText("OK");
Button cancBtn = new Button(shell, SWT.PUSH);
cancBtn.setText("Cancel");
创建两个按钮并将其设置到外壳。
FormData cancelData = new FormData(80, 30);
取消按钮的大小为80x30。
cancelData.right = new FormAttachment(98);
cancelData.bottom = new FormAttachment(95);
按钮的右侧附着在窗口宽度的 98% 处。 按钮的底部固定在窗口高度的 95% 处。
okData.right = new FormAttachment(cancelButton, -5, SWT.LEFT);
okData.bottom = new FormAttachment(cancelButton, 0, SWT.BOTTOM);
“确定”按钮的右侧位于“取消”按钮的左侧 5 像素处。 “确定”按钮的底部与“取消”按钮的底部对齐。

图:按钮
新建文件夹
在下面的示例中,我们使用FormLayout和RowLayout管理器创建窗口布局。
NewFolderEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* ZetCode Java SWT tutorial
*
* This program creates a layout using a
* FormLayout and a RowLayout.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class NewFolderEx {
public NewFolderEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setLayout(new FormLayout());
Label lbl = new Label(shell, SWT.LEFT);
lbl.setText("Name:");
FormData data1 = new FormData();
data1.left = new FormAttachment(0, 5);
data1.top = new FormAttachment(0, 10);
lbl.setLayoutData(data1);
Text text = new Text(shell, SWT.SINGLE);
FormData data2 = new FormData();
data2.left = new FormAttachment(lbl, 15);
data2.top = new FormAttachment(0, 10);
data2.right = new FormAttachment(100, -5);
text.setLayoutData(data2);
Composite com = new Composite(shell, SWT.NONE);
RowLayout rowLayout = new RowLayout();
com.setLayout(rowLayout);
Button okBtn = new Button(com, SWT.PUSH);
okBtn.setText("OK");
okBtn.setLayoutData(new RowData(80, 30));
Button closeBtn = new Button(com, SWT.PUSH);
closeBtn.setText("Close");
closeBtn.setLayoutData(new RowData(80, 30));
FormData data3 = new FormData();
data3.bottom = new FormAttachment(100, -5);
data3.right = new FormAttachment(100, 0);
com.setLayoutData(data3);
Text mainText = new Text(shell, SWT.MULTI | SWT.BORDER);
FormData data4 = new FormData();
data4.width = 250;
data4.height = 180;
data4.top = new FormAttachment(text, 10);
data4.left = new FormAttachment(0, 5);
data4.right = new FormAttachment(100, -5);
data4.bottom = new FormAttachment(com, -10);
mainText.setLayoutData(data4);
shell.setText("New folder");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
NewFolderEx ex = new NewFolderEx(display);
display.dispose();
}
}
在示例中,有标签,文本和按钮小部件。
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setLayout(new FormLayout());
FormLayout设置为外壳的主布局管理器。
Label lbl = new Label(shell, SWT.LEFT);
lbl.setText("Name:");
FormData data1 = new FormData();
data1.left = new FormAttachment(0, 5);
data1.top = new FormAttachment(0, 10);
lbl.setLayoutData(data1);
标签窗口小部件附在窗口的左上角。
Text text = new Text(shell, SWT.SINGLE);
FormData data2 = new FormData();
data2.left = new FormAttachment(lbl, 15);
data2.top = new FormAttachment(0, 10);
data2.right = new FormAttachment(100, -5);
text.setLayoutData(data2);
在标签旁边,我们放置一个Text控件。 文本控件的左侧相对于标签放置。
Composite com = new Composite(shell, SWT.NONE);
RowLayout rowLayout = new RowLayout();
com.setLayout(rowLayout);
创建一个Composite并将其设置为RowLayout管理器。 这两个按钮进入该容器。 将RowLayout用于按钮要比直接通过FormLayout进行组织要容易一些。
Button okBtn = new Button(com, SWT.PUSH);
okBtn.setText("OK");
okBtn.setLayoutData(new RowData(80, 30));
Button closeBtn = new Button(com, SWT.PUSH);
closeBtn.setText("Close");
closeBtn.setLayoutData(new RowData(80, 30));
创建两个按钮。 他们的父部件是Composite。
FormData data3 = new FormData();
data3.bottom = new FormAttachment(100, -5);
data3.right = new FormAttachment(100, 0);
com.setLayoutData(data3);
Composite本身与FormLayout一起放置在窗口的底部。 负值是与相邻小部件或窗口边界的偏移量。
Text mainText = new Text(shell, SWT.MULTI | SWT.BORDER);
FormData data4 = new FormData();
data4.width = 250;
data4.height = 180;
data4.top = new FormAttachment(text, 10);
data4.left = new FormAttachment(0, 5);
data4.right = new FormAttachment(100, -5);
data4.bottom = new FormAttachment(com, -10);
mainText.setLayoutData(data4);
最后,创建了Text主窗口小部件。 它占用了大部分窗口区域。 width和height属性指定控件的初始首选大小。

图:新文件夹
GridLayout
GridLayout管理器将其子窗口小部件放入网格中。
GridLayoutEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example presents the GridLayout.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class GridLayoutEx {
public GridLayoutEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Color col = new Color(display, 100, 200, 100);
shell.setBackground(col);
col.dispose();
GridLayout layout = new GridLayout(2, false);
shell.setLayout(layout);
Label lbl1 = new Label(shell, SWT.NONE);
GridData gd1 = new GridData(SWT.FILL, SWT.FILL, true, true);
lbl1.setLayoutData(gd1);
Color col1 = new Color(display, 250, 155, 100);
lbl1.setBackground(col1);
col1.dispose();
Label lbl2 = new Label(shell, SWT.NONE);
GridData gd2 = new GridData(SWT.FILL, SWT.FILL, true, true);
gd2.heightHint = 100;
lbl2.setLayoutData(gd2);
Color col2 = new Color(display, 10, 155, 100);
lbl2.setBackground(col2);
col2.dispose();
Label lbl3 = new Label(shell, SWT.NONE);
GridData gd3 = new GridData(SWT.FILL, SWT.FILL, true, true);
gd3.widthHint = 300;
gd3.heightHint = 100;
gd3.horizontalSpan = 2;
lbl3.setLayoutData(gd3);
Color col3 = new Color(display, 100, 205, 200);
lbl3.setBackground(col3);
col3.dispose();
shell.setText("Grid");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
GridLayoutEx ex = new GridLayoutEx(display);
display.dispose();
}
}
在示例中,我们在网格中放置了三个标签。 每个标签具有不同的背景色。
Color col = new Color(display, 100, 200, 100);
shell.setBackground(col);
col.dispose();
setBackground()方法为外壳设置背景色。
GridLayout layout = new GridLayout(2, false);
shell.setLayout(layout);
实例化GridLayout管理器并将其设置为外壳的布局管理器。 网格由 2 列组成。
Label lbl1 = new Label(shell, SWT.NONE);
GridData gd1 = new GridData(SWT.FILL, SWT.FILL, true, true);
lbl1.setLayoutData(gd1);
第一个标签进入网格的左上角单元格。 GridData类的四个参数使标签组件填充其单元格并在两个方向上扩展。
Label lbl2 = new Label(shell, SWT.NONE);
GridData gd2 = new GridData(SWT.FILL, SWT.FILL, true, true);
gd2.heightHint = 100;
lbl2.setLayoutData(gd2);
第二个标签转到相邻的单元格。 heightHint属性指定标签的首选高度。 请注意,它也会影响先前的窗口小部件,因为该属性有效地设置了行的首选高度。
Label lbl3 = new Label(shell, SWT.NONE);
GridData gd3 = new GridData(SWT.FILL, SWT.FILL, true, true);
gd3.widthHint = 300;
gd3.heightHint = 100;
gd3.horizontalSpan = 2;
lbl3.setLayoutData(gd3);
第三个标签进入第二行。 horizontalSpan属性使标签跨越两列。

图:简单 GridLayout
计算器
在下面的示例中,我们使用GridLayout管理器创建计算器的框架。
CalculatorEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* ZetCode Java SWT tutorial
*
* In this program, we use the GridLayout to
* create a calculator skeleton.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class CalculatorEx {
public CalculatorEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.DIALOG_TRIM | SWT.CENTER);
GridLayout gl = new GridLayout(4, true);
gl.marginHeight = 5;
shell.setLayout(gl);
String[] buttons = {
"Cls", "Bck", "", "Close", "7", "8", "9", "/", "4",
"5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+"
};
Text text = new Text(shell, SWT.SINGLE);
GridData gridData = new GridData();
gridData.horizontalSpan = 4;
gridData.horizontalAlignment = GridData.FILL;
text.setLayoutData(gridData);
for (int i = 0; i < buttons.length; i++) {
if (i == 2) {
Label lbl = new Label(shell, SWT.CENTER);
GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
lbl.setLayoutData(gd);
} else {
Button btn = new Button(shell, SWT.PUSH);
btn.setText(buttons[i]);
GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
gd.widthHint = 50;
gd.heightHint = 30;
btn.setLayoutData(gd);
}
}
shell.setText("Calculator");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
CalculatorEx ex = new CalculatorEx(display);
display.dispose();
}
}
我们使用GridLayout管理器创建计算器的框架。 我们使用三种类型的小部件:文本小部件,标签小部件和几个按钮。
Shell shell = new Shell(display, SWT.DIALOG_TRIM | SWT.CENTER);
使用SWT.DIALOG_TRIM标志,使窗口不可调整大小。
GridLayout gl = new GridLayout(4, true);
gl.marginHeight = 5;
shell.setLayout(gl);
我们创建一个具有 4 列的GridLayout,并提供顶部和底部页边距。
Text text = new Text(shell, SWT.SINGLE);
GridData gridData = new GridData();
gridData.horizontalSpan = 4;
gridData.horizontalAlignment = GridData.FILL;
text.setLayoutData(gridData);
GridData是与GridLayout关联的布局数据对象。 使用horizontalSpan属性,我们使文本小部件跨越所有四列。 设置为GridData.FILL的horizontalAlignment使文本窗口小部件填充布局管理器分配给它的整个区域。
Button btn = new Button(shell, SWT.PUSH);
btn.setText(buttons[i]);
GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
gd.widthHint = 50;
gd.heightHint = 30;
btn.setLayoutData(gd);
在for循环内,我们创建按钮并将其放入网格中。 通过widthHint和heightHint属性,我们可以设置按钮的首选大小。

图:计算机骨架
在 Java SWT 教程的这一部分中,我们讨论了小部件的布局管理。
Java SWT 中的菜单和工具栏
在 Java SWT 教程的这一部分中,我们使用菜单和工具栏。
菜单栏是 GUI 应用的常见部分。 它是位于各个菜单中的一组命令。
简单菜单
在第一个示例中,我们创建一个带有一个文件菜单的菜单栏。 该菜单只有一个菜单项。 通过选择项目,应用结束。
SimpleMenuEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program creates a simple menu.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class SimpleMenuEx {
public SimpleMenuEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Menu menuBar = new Menu(shell, SWT.BAR);
MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE);
cascadeFileMenu.setText("&File");
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
cascadeFileMenu.setMenu(fileMenu);
MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH);
exitItem.setText("&Exit");
shell.setMenuBar(menuBar);
exitItem.addListener(SWT.Selection, event-> {
shell.getDisplay().dispose();
System.exit(0);
});
shell.setText("Simple menu");
shell.setSize(300, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
SimpleMenuEx ex = new SimpleMenuEx(display);
display.dispose();
}
}
这是一个最小的菜单栏功能示例。
Menu menuBar = new Menu(shell, SWT.BAR);
将SWT.BAR选项传递给Menu将创建一个菜单栏。
MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE);
cascadeFileMenu.setText("&File");
顶级菜单项是级联菜单项。 他们收到SWT.CASCADE选项。
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
cascadeFileMenu.setMenu(fileMenu);
下拉菜单是使用SWT.DROP_DOWN选项创建的。
MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH);
exitItem.setText("&Exit");
推菜单项已插入下拉菜单中。 它是使用SWT.PUSH选项创建的。
shell.setMenuBar(menuBar);
setMenuBar()在外壳上设置菜单栏。
exitItem.addListener(SWT.Selection, event-> {
shell.getDisplay().dispose();
System.exit(0);
});
当我们选择退出推送菜单项时,应用终止。

图:简单菜单
子菜单
下一个示例演示如何创建子菜单。
SubMenuEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program creates a submenu.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class SubMenuEx {
public SubMenuEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Menu menuBar = new Menu(shell, SWT.BAR);
MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE);
cascadeFileMenu.setText("&File");
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
cascadeFileMenu.setMenu(fileMenu);
MenuItem cascadeEditMenu = new MenuItem(menuBar, SWT.CASCADE);
cascadeEditMenu.setText("&Edit");
MenuItem subMenuItem = new MenuItem(fileMenu, SWT.CASCADE);
subMenuItem.setText("Import");
Menu submenu = new Menu(shell, SWT.DROP_DOWN);
subMenuItem.setMenu(submenu);
MenuItem feedItem = new MenuItem(submenu, SWT.PUSH);
feedItem.setText("&Import news feed...");
MenuItem bmarks = new MenuItem(submenu, SWT.PUSH);
bmarks.setText("&Import bookmarks...");
MenuItem mailItem = new MenuItem(submenu, SWT.PUSH);
mailItem.setText("&Import mail...");
MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH);
exitItem.setText("&Exit");
shell.setMenuBar(menuBar);
exitItem.addListener(SWT.Selection, event-> {
shell.getDisplay().dispose();
System.exit(0);
});
shell.setText("Submenu");
shell.setSize(300, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
SubMenuEx ex = new SubMenuEx(display);
display.dispose();
}
}
该示例在另一个菜单内创建一个菜单。
MenuItem subMenuItem = new MenuItem(fileMenu, SWT.CASCADE);
subMenuItem.setText("Import");
子菜单的创建类似于创建普通菜单。 首先,我们创建一个层叠菜单项。 唯一的区别是父窗口小部件。 这次,父级是子菜单所属的菜单对象。
MenuItem feedItem = new MenuItem(submenu, SWT.PUSH);
feedItem.setText("&Import news feed...");
我们创建一个推菜单项。 父窗口小部件是子菜单对象。

图:子菜单
CheckMenuItem
CheckMenuItem是带有复选框的菜单项。 它可以用于布尔属性。
CheckMenuItemEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program creates a check menu item.
* It will show or hide a statusbar.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class CheckMenuItemEx {
private Shell shell;
private Label status;
private MenuItem statItem;
public CheckMenuItemEx(Display display) {
initUI(display);
}
public void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Menu menuBar = new Menu(shell, SWT.BAR);
shell.setMenuBar(menuBar);
MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE);
cascadeFileMenu.setText("&File");
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
cascadeFileMenu.setMenu(fileMenu);
MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH);
exitItem.setText("&Exit");
MenuItem cascadeViewMenu = new MenuItem(menuBar, SWT.CASCADE);
cascadeViewMenu.setText("&View");
Menu viewMenu = new Menu(shell, SWT.DROP_DOWN);
cascadeViewMenu.setMenu(viewMenu);
statItem = new MenuItem(viewMenu, SWT.CHECK);
statItem.setSelection(true);
statItem.setText("&View Statusbar");
statItem.addListener(SWT.Selection, new MyStatusListener());
exitItem.addSelectionListener(new MySelectionAdapter());
status = new Label(shell, SWT.BORDER);
status.setText("Ready");
FormLayout layout = new FormLayout();
shell.setLayout(layout);
FormData labelData = new FormData();
labelData.left = new FormAttachment(0);
labelData.right = new FormAttachment(100);
labelData.bottom = new FormAttachment(100);
status.setLayoutData(labelData);
shell.setText("Check menu item");
shell.setSize(300, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private class MyStatusListener implements Listener {
@Override
public void handleEvent(Event event) {
if (statItem.getSelection()) {
status.setVisible(true);
} else {
status.setVisible(false);
}
}
}
private class MySelectionAdapter extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
shell.getDisplay().dispose();
System.exit(0);
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
CheckMenuItemEx ex = new CheckMenuItemEx(display);
display.dispose();
}
}
在我们的代码示例中,我们显示一个检查菜单项。 如果该复选框已激活,则会显示状态栏。 如果不是,状态栏将被隐藏。
statItem = new MenuItem(viewMenu, SWT.CHECK);
SWT.CHECK标志将创建一个检查菜单项。
statItem.setSelection(true);
setSelection()方法选中或取消选中检查菜单项。
if (statItem.getSelection()) {
status.setVisible(true);
} else {
status.setVisible(false);
}
根据检查菜单项的状态,我们显示或隐藏标签小部件。

图:选中菜单项
弹出菜单
在下一个示例中,我们创建一个弹出菜单。 弹出菜单也称为上下文菜单。 当我们右键单击一个对象时,将显示此类型的菜单。
PopupMenuEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program creates a popup menu.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class PopupMenuEx {
public PopupMenuEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Menu menu = new Menu(shell, SWT.POP_UP);
MenuItem minItem = new MenuItem(menu, SWT.PUSH);
minItem.setText("Minimize");
minItem.addListener(SWT.Selection, event -> {
shell.setMinimized(true);
});
MenuItem exitItem = new MenuItem(menu, SWT.PUSH);
exitItem.setText("Exit");
exitItem.addListener(SWT.Selection, event -> {
shell.getDisplay().dispose();
System.exit(0);
});
shell.setText("Popup menu");
shell.setMenu(menu);
shell.setSize(300, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
PopupMenuEx ex = new PopupMenuEx(display);
display.dispose();
}
}
在我们的代码示例中,我们创建一个带有两个菜单项的弹出菜单。 第一个最小化窗口,第二个终止应用。
Menu menu = new Menu(shell, SWT.POP_UP);
使用SWT.POP_UP标志创建弹出菜单。
MenuItem minItem = new MenuItem(menu, SWT.PUSH);
minItem.setText("Minimize");
弹出菜单中的菜单项是普通的推菜单项。
minItem.addListener(SWT.Selection, event -> {
shell.setMinimized(true);
});
setMinimized()方法将窗口最小化。
shell.setMenu(menu);
我们为外壳设置一个弹出菜单。

图:弹出菜单
简单的工具栏
菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 在下面的示例中,我们创建一个简单的工具栏。
SimpleToolBarEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
/**
* ZetCode Java SWT tutorial
*
* This program creates a simple toolbar.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class SimpleToolBarEx {
private Image newi;
private Image opei;
private Image quii;
public SimpleToolBarEx(Display display) {
initUI(display);
}
@SuppressWarnings("unused")
public void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Device dev = shell.getDisplay();
try {
newi = new Image(dev, "new.png");
opei = new Image(dev, "open.png");
quii = new Image(dev, "quit.png");
} catch (Exception e) {
System.out.println("Cannot load images");
System.out.println(e.getMessage());
System.exit(1);
}
ToolBar toolBar = new ToolBar(shell, SWT.BORDER);
ToolItem item1 = new ToolItem(toolBar, SWT.PUSH);
item1.setImage(newi);
ToolItem item2 = new ToolItem(toolBar, SWT.PUSH);
item2.setImage(opei);
ToolItem separator = new ToolItem(toolBar, SWT.SEPARATOR);
ToolItem item3 = new ToolItem(toolBar, SWT.PUSH);
item3.setImage(quii);
toolBar.pack();
item3.addListener(SWT.Selection, event -> {
shell.getDisplay().dispose();
System.exit(0);
});
shell.setText("Simple toolbar");
shell.setSize(300, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@Override
public void finalize() {
newi.dispose();
opei.dispose();
quii.dispose();
}
public static void main(String[] args) {
Display display = new Display();
SimpleToolBarEx ex = new SimpleToolBarEx(display);
ex.finalize();
display.dispose();
}
}
该示例显示了一个工具栏和三个工具项。
ToolBar toolBar = new ToolBar(shell, SWT.BORDER);
工具栏已创建。
ToolItem item1 = new ToolItem(toolBar, SWT.PUSH);
item1.setImage(newi);
我们创建一个带有图像的工具项。
ToolItem separator = new ToolItem(toolBar, SWT.SEPARATOR);
在这里,我们创建一个垂直分隔符。

图:工具栏
在 Java SWT 教程的这一章中,我们展示了如何使用菜单和工具栏。
Java SWT 中的小部件
在 Java SWT 编程教程的这一部分中,我们将介绍一些 SWT 小部件。
小部件是 GUI 应用的基本构建块。 将小部件视为乐高玩具的一部分。 多年来,几个小部件已成为所有 OS 平台上所有工具包中的标准。 例如,按钮,复选框或滚动条。
Label
Label是显示字符串或图像的不可选择的用户界面对象。 此外,它可以显示水平或垂直线。
LabelEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program uses the Label widget to
* show lyrics of a song.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class LabelEx {
final String lyrics =
"And I know that he knows I'm unfaithful\n"+
"And it kills him inside\n"+
"To know that I am happy with some other guy\n"+
"I can see him dyin'\n"+
"\n"+
"I don't wanna do this anymore\n"+
"I don't wanna be the reason why\n"+
"Every time I walk out the door\n"+
"I see him die a little more inside\n"+
"I don't wanna hurt him anymore\n"+
"I don't wanna take away his life\n"+
"I don't wanna be... A murderer";
public LabelEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Label label = new Label(shell, SWT.LEFT);
label.setText(lyrics);
Point p = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
label.setBounds(5, 5, p.x+5, p.y+5);
shell.setText("Unfaithful");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
LabelEx ex = new LabelEx(display);
display.dispose();
}
}
该代码示例在窗口上显示了一些歌词。
String lyrics =
"And I know that he knows I'm unfaithful\n"+
"And it kills him inside\n"+
...
我们构建多行文本。
Label label = new Label(shell, SWT.LEFT);
label.setText(lyrics);
Label小部件已创建; SWT.LEFT选项使其文本保持左对齐。
Point p = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
label.setBounds(5, 5, p.x+5, p.y+5);
我们计算文本的大小,以便在文本周围放置一些空间。
shell.pack();
pack()方法将窗口调整为首选大小。 足够大以显示标签小部件。
CheckBox
在 SWT 中,复选按钮是Button的特例。 它是具有两种状态的窗口小部件:打开和关闭。 接通状态通过复选标记显示。 它用来表示一些布尔属性。
CheckButtonEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program uses a check button
* widget to show/hide the title
* of the window.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class CheckButtonEx {
private Shell shell;
public CheckButtonEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display);
RowLayout layout = new RowLayout();
layout.marginLeft = 30;
layout.marginTop = 30;
shell.setLayout(layout);
Button cb = new Button(shell, SWT.CHECK);
cb.setText("Show title");
cb.setSelection(true);
cb.addListener(SWT.Selection, event -> onButtonSelect(cb));
shell.setText("Check button");
shell.setSize(250, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onButtonSelect(Button cb) {
if (cb.getSelection()) {
shell.setText("Check button");
} else {
shell.setText("");
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
CheckButtonEx ex = new CheckButtonEx(display);
display.dispose();
}
}
窗口标题的显示取决于检查按钮的状态。
Button cb = new Button(shell, SWT.CHECK);
cb.setText("Show title");
通过将SWT.CHECK传递给Button构造器来创建复选按钮小部件。 setText()方法设置按钮的标签。
cb.setSelection(true);
默认情况下标题是可见的,因此我们默认使用setSelection()方法选择复选按钮。
cb.addListener(SWT.Selection, event -> onButtonSelect(cb));
我们向按钮的SWT.Selection事件类型添加一个监听器对象。
private void onButtonSelect(Button cb) {
if (cb.getSelection()) {
shell.setText("Check button");
} else {
shell.setText("");
}
}
在onButtonSelect()方法内部,我们根据选中按钮的状态显示或隐藏窗口的标题。 确认按钮的状态通过getSelection()方法确定。

图:复选按钮
Spinner
Spinner控件允许从一系列值中选择一个数字。 可以通过单击向上和向下箭头或通过按下和向上键或向上键和来选择该值。 向下翻页键。
SpinnerEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
public class SpinnerEx {
private Label label;
public SpinnerEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginLeft = 10;
layout.marginTop = 10;
layout.spacing = 30;
layout.center = true;
shell.setLayout(layout);
Spinner spinner = new Spinner(shell, SWT.BORDER);
spinner.setMinimum(0);
spinner.setMaximum(100);
spinner.setSelection(0);
spinner.setIncrement(1);
spinner.setPageIncrement(10);
spinner.setLayoutData(new RowData(30, -1));
spinner.addListener(SWT.Selection, event -> onSelected(spinner));
label = new Label(shell, SWT.NONE);
label.setText("0");
shell.setText("Spinner");
shell.setSize(200, 150);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onSelected(Spinner spinner) {
String val = spinner.getText();
label.setText(val);
label.pack();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
SpinnerEx ex = new SpinnerEx(display);
display.dispose();
}
}
该示例具有Spinner和Label。 从微调器中选择的值显示在标签中。
Spinner spinner = new Spinner(shell, SWT.BORDER);
创建Spinner控件的实例。
spinner.setMinimum(0);
spinner.setMaximum(100);
spinner.setSelection(0);
spinner.setIncrement(1);
spinner.setPageIncrement(10);
我们使用微调器 API 指定最小,最大,当前,增量和页面增量值。
spinner.addListener(SWT.Selection, event -> onSelected(spinner));
等待微调器选择事件的监听器已添加到控件中。 触发事件时,将调用onSelected()方法。
private void onSelected(Spinner spinner) {
String val = spinner.getText();
label.setText(val);
label.pack();
}
我们获得微调器的当前值,并将其设置为标签组件。

图:旋钮
List小部件
List小部件使用户可以从项目列表中选择一个选项。 列表可以是单选或多选。
ListWidgetEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program shows the List widget.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class ListWidgetEx {
private Label status;
public ListWidgetEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display);
status = new Label(shell, SWT.NONE);
status.setText("Ready");
FormLayout layout = new FormLayout();
layout.marginHeight = 5;
layout.marginWidth = 5;
layout.spacing = 5;
shell.setLayout(layout);
FormData labelData = new FormData();
labelData.left = new FormAttachment(0);
labelData.right = new FormAttachment(100);
labelData.bottom = new FormAttachment(100);
status.setLayoutData(labelData);
List list = new List(shell, SWT.BORDER);
list.add("Aliens");
list.add("Capote");
list.add("Neverending story");
list.add("Starship troopers");
list.add("Exorcist");
list.add("Omen");
list.addListener(SWT.Selection, event -> onListItemSelect(list));
FormData listData = new FormData();
listData.width = 250;
listData.height = 200;
listData.left = new FormAttachment(shell, 0);
listData.top = new FormAttachment(shell, 0);
listData.right = new FormAttachment(100, 0);
listData.bottom = new FormAttachment(status, 0);
list.setLayoutData(listData);
shell.setText("List");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onListItemSelect(List list) {
String[] items = list.getSelection();
status.setText(items[0]);
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
ListWidgetEx ex = new ListWidgetEx(display);
display.dispose();
}
}
在此示例中,从列表小部件中选择的项目显示在状态栏中。
status = new Label(shell, SWT.NONE);
status.setText("Ready");
标签小部件用于状态栏。 SWT 不将本机窗口小部件用于状态栏。
FormLayout layout = new FormLayout();
layout.marginHeight = 5;
layout.marginWidth = 5;
layout.spacing = 5;
shell.setLayout(layout);
我们使用FormLayout小部件在窗口上排列小部件。 设置了一些边距和间距。
FormData labelData = new FormData();
labelData.left = new FormAttachment(0);
labelData.right = new FormAttachment(100);
labelData.bottom = new FormAttachment(100);
status.setLayoutData(labelData);
此代码将状态标签粘贴在窗口底部; 状态栏的通常位置。
List list = new List(shell, SWT.BORDER);
List小部件已创建。 默认选择模式是单选。
list.add("Aliens");
list.add("Capote");
list.add("Neverending story");
list.add("Starship troopers");
list.add("Exorcist");
list.add("Omen");
它充满了数据。
list.addListener(SWT.Selection, event -> onListItemSelect(list));
我们将选择监听器添加到List小部件。 在列表选择事件中,将调用onListItemSelect()方法。
FormData listData = new FormData();
listData.width = 250;
listData.height = 200;
listData.left = new FormAttachment(shell, 0);
listData.top = new FormAttachment(shell, 0);
listData.right = new FormAttachment(100, 0);
listData.bottom = new FormAttachment(status, 0);
list.setLayoutData(listData);
此代码使List小部件占据了窗口区域的大部分。 width和height属性指定列表的首选大小。
private void onListItemSelect(List list) {
String[] items = list.getSelection();
status.setText(items[0]);
}
在onListItemSelect()内部,我们确定列表中的选定项目并将其设置为状态栏。

图:List小部件
Slider
Slider是一个小部件,可让用户通过在有限间隔内滑动旋钮以图形方式选择一个值。 我们的示例将显示音量控制。
SliderEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Slider;
/**
* ZetCode Java SWT tutorial
*
* In this program, we use the slider
* widget to create a volume control
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class SliderEx {
private Shell shell;
private Label label;
private Image mute;
private Image min;
private Image med;
private Image max;
public SliderEx(Display display) {
initUI(display);
}
private void loadImages() {
Device dev = shell.getDisplay();
try {
mute = new Image(dev, "mute.png");
min = new Image(dev, "min.png");
med = new Image(dev, "med.png");
max = new Image(dev, "max.png");
} catch(Exception e) {
System.out.println("Cannot load images");
System.out.println(e.getMessage());
System.exit(1);
}
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
loadImages();
RowLayout layout = new RowLayout();
layout.marginLeft = 30;
layout.marginTop = 30;
layout.spacing = 30;
layout.fill = true;
shell.setLayout(layout);
Slider slider = new Slider(shell, SWT.HORIZONTAL);
slider.setMaximum(100);
slider.setLayoutData(new RowData(180, -1));
label = new Label(shell, SWT.IMAGE_PNG);
label.setImage(mute);
slider.addListener(SWT.Selection, event -> onSelection(slider));
shell.setText("Slider");
shell.setSize(350, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onSelection(Slider slider) {
int value = slider.getSelection();
if (value == 0) {
label.setImage(mute);
label.pack();
} else if (value > 0 && value <= 30) {
label.setImage(min);
} else if (value > 30 && value < 80) {
label.setImage(med);
} else {
label.setImage(max);
}
}
@Override
public void finalize() {
mute.dispose();
med.dispose();
min.dispose();
max.dispose();
}
public static void main(String[] args) {
Display display = new Display();
SliderEx ex = new SliderEx(display);
ex.finalize();
display.dispose();
}
}
在上面的示例中,我们有一个Slider和一个Label小部件。 通过拖动滑块的旋钮,我们可以更改标签中显示的Image。
try {
mute = new Image(dev, "mute.png");
min = new Image(dev, "min.png");
med = new Image(dev, "med.png");
max = new Image(dev, "max.png");
} catch(Exception e) {
System.out.println("Cannot load images");
System.out.println(e.getMessage());
System.exit(1);
}
图像从磁盘加载。
Slider slider = new Slider(shell, SWT.HORIZONTAL);
slider.setMaximum(100);
Slider小部件已创建。 最大值为 100。
label = new Label(shell, SWT.IMAGE_PNG);
label.setImage(mute);
使用SWT.IMAGE_PNG参数,标签窗口小部件显示 PNG 图像。 setImage()方法将图像设置为标签。
slider.addListener(SWT.Selection, event -> onSelection(slider));
监听器已添加到滑块小部件。
int value = slider.getSelection();
在onSelection()方法内部,我们使用getSelection()方法获得滑块控件的值。
if (value == 0) {
label.setImage(mute);
label.pack();
} else if (value > 0 && value <= 30) {
label.setImage(min);
} else if (value > 30 && value < 80) {
label.setImage(med);
} else {
label.setImage(max);
}
根据获得的值,我们在标签小部件中更改图片。
@Override
public void finalize() {
mute.dispose();
med.dispose();
min.dispose();
max.dispose();
}
最后,资源被释放。

图:Slider小部件
Combo小部件
Combo是一个小部件,允许用户从选项的下拉列表中进行选择。
ComboEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this program, we use the Combo
* widget to select an option.
* The selected option is shown in the
* Label widget.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class ComboEx {
private Label label;
public ComboEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout(SWT.VERTICAL);
layout.marginLeft = 50;
layout.marginTop = 30;
layout.spacing = 30;
shell.setLayout(layout);
Combo combo = new Combo(shell, SWT.DROP_DOWN);
combo.add("Ubuntu");
combo.add("Fedora");
combo.add("Arch");
combo.add("Red Hat");
combo.add("Mint");
combo.setLayoutData(new RowData(150, -1));
label = new Label(shell, SWT.LEFT);
label.setText("...");
combo.addListener(SWT.Selection, event -> onSelected(combo));
shell.setText("Combo");
shell.setSize(300, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onSelected(Combo combo) {
label.setText(combo.getText());
label.pack();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
ComboEx ex = new ComboEx(display);
display.dispose();
}
}
该示例显示了一个组合和一个标签。 该组合具有六个选项的列表。 这些是 Linux 发行版的名称。 标签窗口小部件显示了从组合框中选择的选项。
Combo combo = new Combo(shell, SWT.DROP_DOWN);
Combo小部件已创建。
combo.add("Ubuntu");
combo.add("Fedora");
combo.add("Mandriva");
combo.add("Red Hat");
combo.add("Mint");
组合框小部件充满了数据。
private void onSelected(Combo combo) {
label.setText(combo.getText());
label.pack();
}
我们将所选文本设置为标签小部件。 pack()方法使标签适合组合中新字符串的大小。

图:Combo小部件
在 Java SWT 教程的这一部分中,我们描述了 SWT 库的一些小部件。
Table小部件
在 Java SWT 教程的这一部分中,我们介绍Table小部件。 Table是细胞的二维网格。 可以将数据写入这些单元中的每一个中。 该小部件由电子表格应用引入。
Table可以显示列标题,并且当单元格很多时,Table可以显示滚动条。
空表
第一个示例创建一个简单的空表。
TableEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
/**
* ZetCode Java SWT tutorial
*
* In this program, we create a simple, empty
* table widget.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class TableEx {
public TableEx(Display display) {
initUI(display);
}
@SuppressWarnings("unused")
private void initUI(Display display) {
Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
Table table = new Table(shell, SWT.BORDER);
table.setHeaderVisible(true);
table.setLinesVisible(true);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
data.heightHint = 300;
data.widthHint = 350;
table.setLayoutData(data);
String[] titles = { "A", "B", "C" };
for (int i = 0; i < titles.length; i++) {
TableColumn column = new TableColumn(table, SWT.CENTER);
column.setWidth(120);
column.setText(titles[i]);
}
for (int i = 0; i < 15; i++) {
TableItem item = new TableItem(table, SWT.NONE);
}
shell.setText("Empty table");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
TableEx ex = new TableEx(display);
display.dispose();
}
}
该代码示例创建一个具有三列和十五行的表小部件。 表单元为空。
Table table = new Table(shell, SWT.BORDER);
创建Table小部件的实例。
table.setHeaderVisible(true);
setHeaderVisible()使表格标题可见。 标题由表列名称组成。
table.setLinesVisible(true);
setLinesVisible()显示表格单元格的边框。 默认情况下,单元格不可见。
String[] titles = { "A", "B", "C" };
这些是列标题。
for (int i = 0; i < titles.length; i++) {
TableColumn column = new TableColumn(table, SWT.CENTER);
column.setWidth(120);
column.setText(titles[i]);
}
在此for循环中,我们创建表列。 TableColumn的实例表示表窗口小部件中的一列。 setWidth()设置列的宽度,setText()方法设置列的名称。
for (int i = 0; i < 15; i++) {
TableItem item = new TableItem(table, SWT.NONE);
}
TableItem代表表中的一行。 这段代码创建了十五个空单元格。

图:空表
用数据填充单元格
在第二个示例中,表格单元格显示一些数据。
TableEx2.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
/**
* ZetCode Java SWT tutorial
*
* In this program, we fill table cells
* with data.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class TableEx2 {
public TableEx2(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setLayout(new FillLayout());
Table table = new Table(shell, SWT.BORDER);
table.setHeaderVisible(true);
table.setLinesVisible(true);
TableColumn tc1 = new TableColumn(table, SWT.CENTER);
TableColumn tc2 = new TableColumn(table, SWT.CENTER);
TableColumn tc3 = new TableColumn(table, SWT.CENTER);
tc1.setText("First Name");
tc2.setText("Last Name");
tc3.setText("Profession");
tc1.setWidth(70);
tc2.setWidth(70);
tc3.setWidth(80);
TableItem item1 = new TableItem(table, SWT.NONE);
item1.setText(new String[] { "Jane", "Brown", "Accountant" });
TableItem item2 = new TableItem(table, SWT.NONE);
item2.setText(new String[] { "Tim", "Warner", "Lawyer" });
TableItem item3 = new TableItem(table, SWT.NONE);
item3.setText(new String[] { "Bob", "Milton", "Police officer" });
shell.setText("Table widget");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
TableEx2 ex = new TableEx2(display);
display.dispose();
}
}
该示例显示了具有三列三行的表格小部件。 单元格充满数据。
TableItem item1 = new TableItem(table, SWT.NONE);
item1.setText(new String[] { "Jane", "Brown", "Accountant" });
setText()方法用数据填充一行的所有三个单元格。

图:带有数据的表格单元
选择表格行
以下示例介绍了表格项目的选择。
TableEx3.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
/**
* ZetCode Java SWT tutorial
*
* In this program, we show the data from
* the selected row in the statusbar.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class TableEx3 {
private Label label;
private final String data[][] = { { "Ferarri", "33333" },
{ "Skoda", "22000" }, { "Volvo", "18000" }, { "Mazda", "15000" },
{ "Mercedes", "38000" } };
public TableEx3(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setLayout(new GridLayout(1, true));
Table table = new Table(shell, SWT.BORDER);
table.setHeaderVisible(true);
table.setLinesVisible(true);
String[] titles = { "Car", "Price" };
for (int i = 0; i & titles.length; i++) {
TableColumn column = new TableColumn(table, SWT.NULL);
column.setText(titles[i]);
column.setWidth(130);
}
for (int i = 0; i & data.length; i++) {
TableItem item = new TableItem(table, SWT.NULL);
item.setText(0, data[i][0]);
item.setText(1, data[i][1]);
}
label = new Label(shell, SWT.NONE);
table.addListener(SWT.Selection, event -> onTableItemSelected(table));
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
gd.widthHint = 360;
gd.heightHint = 300;
table.setLayoutData(gd);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
shell.setText("Table widget");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
private void onTableItemSelected(Table table) {
TableItem[] sel = table.getSelection();
String msg = String.format("%s: %s", sel[0].getText(0),
sel[0].getText(1));
label.setText(msg);
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
TableEx3 ex = new TableEx3(display);
display.dispose();
}
}
在示例中,所选行中的数据显示在状态栏中。
table.addListener(SWT.Selection, event -> onTableItemSelected(table));
选择监听器将添加到表中。 每当选择表格项目时,都会调用onTableItemSelected()。
private void onTableItemSelected(Table table) {
TableItem[] sel = table.getSelection();
String msg = String.format("%s: %s", sel[0].getText(0),
sel[0].getText(1));
label.setText(msg);
}
getSelection()方法返回一个选定表项的数组。 (由于我们使用默认选择模式(即单选模式),因此仅返回一个表项。)我们使用getText()方法从该行的单元格中检索数据。 我们构建消息并使用其setText()方法将其显示在标签中。

图:选择表项
添加新表项
在以下示例中,新表项被动态添加到表中。
TableEx4.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
/**
* ZetCode Java SWT tutorial
*
* In this program, we add new table items
* to the table.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class TableEx4 {
private Text text1;
private Text text2;
private Table table;
private final String data[][] = { { "Ferarri", "33333" },
{ "Skoda", "22000" }, { "Volvo", "18000" }, { "Mazda", "15000" },
{ "Mercedes", "38000" } };
public TableEx4(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.setLayout(new GridLayout(5, false));
table = new Table(shell, SWT.BORDER | SWT.MULTI);
table.setHeaderVisible(true);
// table.setLinesVisible(true);
String[] titles = { "Car", "Price" };
for (int i = 0; i < titles.length; i++) {
TableColumn column = new TableColumn(table, SWT.NULL);
column.setText(titles[i]);
column.setWidth(130);
}
for (int i = 0; i < data.length; i++) {
TableItem item = new TableItem(table, SWT.NULL);
item.setText(0, data[i][0]);
item.setText(1, data[i][1]);
}
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
gd.horizontalSpan = 5;
gd.widthHint = 360;
gd.heightHint = 300;
table.setLayoutData(gd);
Label carName = new Label(shell, SWT.NONE);
carName.setText("Car:");
text1 = new Text(shell, SWT.BORDER);
Label priceOfCar = new Label(shell, SWT.NONE);
priceOfCar.setText("Price:");
text2 = new Text(shell, SWT.BORDER);
text1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
text2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
Button addBtn = new Button(shell, SWT.PUSH);
addBtn.setText("Insert");
addBtn.addListener(SWT.Selection, event -> onInsertButtonSelected(event));
shell.setText("Table widget");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
private void onInsertButtonSelected(Event event) {
String val1 = text1.getText();
String val2 = text2.getText();
if (val1.isEmpty() || val2.isEmpty()) {
return;
}
TableItem item = new TableItem(table, SWT.NULL);
item.setText(0, val1);
item.setText(1, val2);
text1.setText("");
text2.setText("");
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
TableEx4 ex = new TableEx4(display);
display.dispose();
}
}
在底部,有两个文本小部件和一个按钮。 插入到文本小部件中的数据将插入到新表项的单元格中。
private void onInsertButtonSelected(Event event) {
String val1 = text1.getText();
String val2 = text2.getText();
...
}
在onInsertButtonSelected()方法中,我们从文本小部件中获取插入的文本。
if (val1.isEmpty() || val2.isEmpty()) {
return;
}
我们确保文本小部件不为空。
TableItem item = new TableItem(table, SWT.NULL);
item.setText(0, val1);
item.setText(1, val2);
构造新表项,并获取插入的数据。
text1.setText("");
text2.setText("");
最后,将清除文本小部件。

图:添加新表项
在 Java SWT 教程的这一部分中,我们介绍了Table小部件。
PyQt5 教程
这是 PyQt5 教程。 本教程适合初学者和中级程序员。 阅读完本教程后,您将可以编写非平凡的 PyQt5 应用。
目录
电子书
独特的电子书,涵盖了 PyQt5 库的高级功能:高级 PyQt5 电子书。
相关教程
Qt5 教程以其本机 C++ 语言介绍了 Qt5 库。 PyQt4 教程涵盖了 PyQt 库的早期版本。 为了让您重新了解 Python 语言,在 ZetCode 上有一个 Python 教程。 wxPython 教程, Python Gtk 教程和 Tkinter 教程是其他流行的 Python GUI 绑定的教程。
Java SWT 中的对话框
在 Java SWT 教程的这一部分中,我们介绍对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
MessageDialog
MessageBox是一个简单的对话框,向用户提供一些信息。 它可以包含消息,图标和各种按钮。 以可用的MessageBox样式选择图标和按钮。 例如,SWT.ICON_ERROR标志在对话框上放置一个错误图标。
MessageBoxEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a simple MessageBox.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class MessageBoxEx {
private Shell shell;
public MessageBoxEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginTop = 50;
layout.marginBottom = 150;
layout.marginLeft = 50;
layout.marginRight = 150;
shell.setLayout(layout);
Button msgBtn = new Button(shell, SWT.PUSH);
msgBtn.setText("Show message");
msgBtn.addListener(SWT.Selection, event -> doShowMessageBox());
shell.setText("Message box");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void doShowMessageBox() {
int style = SWT.ICON_INFORMATION | SWT.OK;
MessageBox dia = new MessageBox(shell, style);
dia.setText("Information");
dia.setMessage("Download completed.");
dia.open();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
MessageBoxEx ex = new MessageBoxEx(display);
display.dispose();
}
}
窗口上有一个按钮。 单击该按钮将显示一个信息消息对话框。
int style = SWT.ICON_INFORMATION | SWT.OK;
该对话框包含一个信息图标和一个确定按钮。
MessageBox dia = new MessageBox(shell, style);
创建MessageBox的实例,将样式作为第二个参数。
dia.setText("Information");
对话框的标题通过setText()方法设置。
dia.setMessage("Download completed.");
该消息通过setMessage()方法设置。

图:MessageBox
请求终止
为了防止数据丢失,以数据为中心的应用在关闭时通常会显示一个确认对话框。 仅在确认对话框后,应用才会终止。
MessageBoxEx2.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a confirmation dialog
* when its closing is initiated.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class MessageBoxEx2 {
private Shell shell;
public MessageBoxEx2(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.addListener(SWT.Close, event -> doShowMessageBox(event));
shell.setText("Message box");
shell.setSize(350, 300);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void doShowMessageBox(Event event) {
int style = SWT.APPLICATION_MODAL | SWT.ICON_QUESTION | SWT.YES
| SWT.NO;
MessageBox messageBox = new MessageBox(shell, style);
messageBox.setText("Information");
messageBox.setMessage("Really close application?");
event.doit = messageBox.open() == SWT.YES;
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
MessageBoxEx2 ex = new MessageBoxEx2(display);
display.dispose();
}
}
此示例显示启动关闭对话框时的确认对话框。
shell.addListener(SWT.Close, event -> doShowMessageBox(event));
我们将监听器挂钩到SWT.Close事件类型。
int style = SWT.APPLICATION_MODAL | SWT.ICON_QUESTION | SWT.YES
| SWT.NO;
确认对话框是模式对话框,包含一个“问题”图标以及“是”和“否”按钮。 (模式对话框将阻止主应用,直到将其关闭。)
event.doit = messageBox.open() == SWT.YES;
将doit事件设置为false会取消该事件; 设置为true允许它。
目录对话框
DirectoryDialog是一个对话框,用于选择特定目录的路径。
DirectoryDialogEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a directory dialog.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class DirectoryDialogEx {
private Shell shell;
private Label status;
public DirectoryDialogEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
status = new Label(shell, SWT.BORDER);
status.setText("Ready");
FormLayout layout = new FormLayout();
shell.setLayout(layout);
FormData labelData = new FormData();
labelData.left = new FormAttachment(0);
labelData.right = new FormAttachment(100);
labelData.bottom = new FormAttachment(100);
status.setLayoutData(labelData);
shell.addListener(SWT.MouseDown, event -> onMouseDown());
shell.setText("DirectoryDialog");
shell.setSize(350, 250);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onMouseDown() {
DirectoryDialog dialog = new DirectoryDialog(shell);
String path = dialog.open();
if (path != null) {
status.setText(path);
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
DirectoryDialogEx ex = new DirectoryDialogEx(display);
display.dispose();
}
}
在我们的示例中,我们选择带有DirectoryDialog的目录,并在状态栏中显示其路径。 通过单击窗口区域可以显示该对话框。
shell.addListener(SWT.MouseDown, event -> onMouseDown());
将SWT.MouseDown事件的鼠标监听器添加到外壳中。 当我们在窗口上按下鼠标按钮时,将调用onMouseDown()方法。
DirectoryDialog dialog = new DirectoryDialog(shell);
创建了DirectoryDialog。
String path = dialog.open();
我们获得所选目录的路径。
if (path != null) {
status.setText(path);
}
如果路径不为空,则在状态标签中显示该路径。

图:目录对话框
FontDialog
FontDialog是用于选择字体的对话框。 它通常用于进行一些文本编辑或格式化的应用中。
FontDialogEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a font dialog.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class FontDialogEx {
private Shell shell;
private Label label;
public FontDialogEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginHeight = 100;
layout.marginWidth = 100;
shell.setLayout(layout);
label = new Label(shell, SWT.NONE);
label.setText("ZetCode Java SWT tutorial");
shell.addListener(SWT.MouseDown, event -> onMouseDown());
shell.setText("FontDialog");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onMouseDown() {
FontDialog dialog = new FontDialog(shell);
FontData fdata = dialog.open();
if (fdata != null) {
Font font = new Font(shell.getDisplay(), fdata);
label.setFont(font);
label.pack();
shell.pack();
font.dispose();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
FontDialogEx ex = new FontDialogEx(display);
display.dispose();
}
}
在代码示例中,我们使用FontDialog更改标签的字体。
FontDialog dialog = new FontDialog(shell);
FontDialog已创建。
Font font = new Font(shell.getDisplay(), fdata);
根据字体数据创建Font对象,由字体对话框返回。
label.setFont(font);
字体通过setFont()方法应用于标签。
label.pack();
shell.pack();
pack()方法使标签和外壳适应新的字体类型。
font.dispose();
字体是操作系统资源。 因此,必须在不再需要时将其丢弃。

图:FontDialog
ColorDialog
ColorDialog是用于选择颜色的对话框。
ColorDialogEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a color dialog.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class ColorDialogEx {
private Shell shell;
private Label label;
public ColorDialogEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginHeight = 100;
layout.marginWidth = 100;
shell.setLayout(layout);
label = new Label(shell, SWT.NONE);
label.setText("ZetCode Java SWT tutorial");
shell.addListener(SWT.MouseDown, event -> onMouseDown());
shell.setText("ColorDialog");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onMouseDown() {
ColorDialog dialog = new ColorDialog(shell);
RGB rgb = dialog.open();
if (rgb != null) {
Color col = new Color(shell.getDisplay(), rgb);
label.setForeground(col);
col.dispose();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
ColorDialogEx ex = new ColorDialogEx(display);
display.dispose();
}
}
该示例与上一个示例非常相似。 这次我们更改标签的颜色。 单击窗口区域显示ColorDialog。
ColorDialog dialog = new ColorDialog(shell);
我们创建ColorDialog。
RGB rgb = dialog.open();
使用ColorDialog的open()方法,我们可以获得 RGB 值。
Color col = new Color(shell.getDisplay(), rgb);
label.setForeground(col);
我们获得颜色值并修改标签的颜色。
col.dispose();
Color是 OS 资源,因此我们在不再需要它时将其处理。

图:ColorDialog
文件对话框
FileDialog用于选择文件名。
FileDialogEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This example shows a file dialog.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class FileDialogEx {
private Shell shell;
private Label label;
public FileDialogEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
RowLayout layout = new RowLayout();
layout.marginHeight = 50;
layout.marginWidth = 50;
shell.setLayout(layout);
label = new Label(shell, SWT.NONE);
String homeDir = System.getProperty("user.home");
label.setText(homeDir);
label.pack();
shell.addListener(SWT.MouseDown, event -> onMouseDown());
shell.setText("FileDialog");
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void onMouseDown() {
FileDialog dialog = new FileDialog(shell, SWT.OPEN);
String[] filterNames = new String[]
{"Java sources", "All Files (*)"};
String[] filterExtensions = new String[]
{"*.java", "*"};
dialog.setFilterNames(filterNames);
dialog.setFilterExtensions(filterExtensions);
String path = dialog.open();
if (path != null) {
label.setText(path);
label.pack();
shell.pack();
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
FileDialogEx ex = new FileDialogEx(display);
display.dispose();
}
}
该代码示例使用FileDialog选择文件。 该对话框使用过滤器仅显示 Java 源。 所选文件的名称显示在标签中。
label = new Label(shell, SWT.NONE);
String homeDir = System.getProperty("user.home");
label.setText(homeDir);
label.pack();
首先,标签小部件将显示用户的主目录。
FileDialog dialog = new FileDialog(shell, SWT.OPEN);
我们用SWT.OPEN标志创建一个FileDialog。 该对话框可用于打开或保存文件。 对话框的保存行为通过SWT.SAVE常量启用。
String[] filterNames = new String[]
{"Java sources", "All Files (*)"};
String[] filterExtensions = new String[]
{"*.java", "*"};
dialog.setFilterNames(filterNames);
dialog.setFilterExtensions(filterExtensions);
我们使用两个过滤器; 一种用于 Java 源,一种用于所有文件类型。
String path = dialog.open();
使用FileDialog的open()方法检索路径名。
if (path != null) {
label.setText(path);
label.pack();
shell.pack();
}
路径名通过setText()方法设置为标签。 标签和外壳通过pack()方法适应返回路径的大小。
Java SWT 教程的这一部分是关于 SWT 中的对话框的。
Java SWT 绘图
在 Java SWT 教程的这一部分中,我们进行了一些绘制。
在实现Drawable接口的对象上执行绘制。 这包括Control,Image,Display设备或Printer设备。
org.eclipse.swt.graphics.GC是一个图形上下文,其中封装了可以执行的绘制操作。 使用GC有两种常见方法; 通过使用Drawable实例作为构造器参数创建一个,或使用作为paintEvent回调的一部分提供的GC创建一个。
色彩
在第一个示例中,我们处理颜色。 颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 在 Java SWT 中,有效的 RGB 值在 0 到 255 之间。
ColoursEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program draws three rectangles.
* The interiors are filled with
* different colors.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class ColoursEx {
private Shell shell;
public ColoursEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.addListener(SWT.Paint, event -> drawRectangles(event));
shell.setText("Colours");
shell.setSize(360, 120);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawRectangles(Event e) {
GC gc = e.gc;
Color c1 = new Color(e.display, 50, 50, 200);
gc.setBackground(c1);
gc.fillRectangle(10, 15, 90, 60);
Color c2 = new Color(e.display, 105, 90, 60);
gc.setBackground(c2);
gc.fillRectangle(130, 15, 90, 60);
Color c3 = new Color(e.display, 33, 200, 100);
gc.setBackground(c3);
gc.fillRectangle(250, 15, 90, 60);
c1.dispose();
c2.dispose();
c3.dispose();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
ColoursEx ex = new ColoursEx(display);
display.dispose();
}
}
在我们的示例中,我们绘制了三个矩形,并用三种不同的颜色填充它们。
shell.addListener(SWT.Paint, event -> drawRectangles(event));
我们为绘图事件添加了绘图监听器。
private void drawRectangles(Event e) {
GC gc = e.gc;
...
}
生成绘图事件时将调用drawRectangles()方法。 我们获得了图形上下文的句柄,该上下文是我们在其上执行绘制操作的对象。
Color c1 = new Color(e.display, 50, 50, 200);
我们创建一个颜色对象。
gc.setBackground(c1);
setBackground()方法为绘图文本和形状的内部设置颜色。
gc.fillRectangle(10, 15, 90, 60);
fillRectangle()用背景色填充指定的矩形。
c1.dispose();
c2.dispose();
c3.dispose();
在绘图结束时,释放颜色资源。

图:颜色
直线
drawLine()方法在可绘制对象上绘制一条线。 setLineStyle()方法指定线条的样式。 以下是内置的 SWT 线型:
SWT.LINE_DOTSWT.LINE_DASHSWT.LINE_DASHDOTSWT.LINE_DASHDOTDOTSWT.LINE_SOLID
也可以使用SWT.LINE_CUSTOM选项创建自定义线条样式。
LineStylesEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program draws text on the window.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class LineStylesEx {
public LineStylesEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display);
shell.addListener(SWT.Paint, event -> drawLyrics(event));
shell.setText("Line styles");
shell.setSize(300, 330);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawLyrics(Event e) {
GC gc = e.gc;
gc.setLineWidth(2);
gc.setLineStyle(SWT.LINE_DASHDOT);
gc.drawLine(20, 40, 250, 40);
gc.setLineStyle(SWT.LINE_DASH);
gc.drawLine(20, 80, 250, 80);
gc.setLineStyle(SWT.LINE_DASHDOTDOT);
gc.drawLine(20, 120, 250, 120);
gc.setLineStyle(SWT.LINE_SOLID);
gc.drawLine(20, 160, 250, 160);
gc.setLineStyle(SWT.LINE_DOT);
gc.drawLine(20, 200, 250, 200);
gc.setLineStyle(SWT.LINE_CUSTOM);
gc.setLineDash(new int[] {1, 4, 5, 4});
gc.drawLine(20, 240, 250, 240);
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
LineStylesEx ex = new LineStylesEx(display);
display.dispose();
}
}
该示例绘制了五条标准样式线和一种自定义样式。
gc.setLineWidth(2);
setLineWidth()设置绘制线时使用的宽度。
gc.setLineStyle(SWT.LINE_DASHDOT);
setLineStyle()将线条样式设置为SWT.LINE_DASHDOT。 该线由点划线组成。
gc.drawLine(20, 40, 250, 40);
drawLine()画一条线。 参数是起点和终点的 x 和 y 坐标。
gc.setLineStyle(SWT.LINE_CUSTOM);
gc.setLineDash(new int[] {1, 4, 5, 4});
gc.drawLine(20, 240, 250, 240);
这些线创建自定义线型样式。 整数数组指定行间距和笔划线的宽度。 在我们的示例中,图案为 1 像素笔划线,4 像素间隔,5 像素笔划线和 4 像素间隔。 对整个行重复此模式。

图:线型
基本形状
下一个示例将一些基本形状绘制到窗口上。
BasicShapesEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this program, we draw some
* basic shapes.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class BasicShapesEx {
private Shell shell;
public BasicShapesEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.addListener(SWT.Paint, event -> drawShapes(event));
shell.setText("Basic shapes");
shell.setSize(430, 300);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawShapes(Event e) {
GC gc = e.gc;
gc.setAntialias(SWT.ON);
Color col = new Color(e.display, 150, 150, 150);
gc.setBackground(col);
gc.fillRectangle(20, 20, 120, 80);
gc.fillRectangle(180, 20, 80, 80);
gc.fillOval(290, 20, 120, 70);
gc.fillOval(20, 150, 80, 80);
gc.fillRoundRectangle(150, 150, 100, 80, 25, 25);
gc.fillArc(280, 150, 100, 100, 0, 115);
col.dispose();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
BasicShapesEx ex = new BasicShapesEx(display);
display.dispose();
}
}
在此示例中,我们将创建一个矩形,一个正方形,一个椭圆形,一个圆形,一个圆角矩形和一个圆弧。
gc.fillRectangle(20, 20, 120, 80);
gc.fillRectangle(180, 20, 80, 80);
gc.fillOval(290, 20, 120, 70);
这些线绘制一个矩形,一个正方形和一个椭圆形。
gc.fillOval(20, 150, 80, 80);
在这里fillOval()方法画一个圆。
gc.fillRoundRectangle(150, 150, 100, 80, 25, 25);
gc.fillArc(280, 150, 100, 100, 0, 115);
这两条线绘制了一个圆角的矩形和一个圆弧。

图:基本形状
多边形
多边形是具有直边的二维平面形状。
PolygonEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program draws a star.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class PolygonEx {
private final int points[] = { 0, 85, 75, 75, 100, 10,
125, 75, 200, 85, 150, 125, 160, 190, 100, 150,
40, 190, 50, 125, 0, 85 };
public PolygonEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display);
shell.addListener(SWT.Paint, event -> drawPolygon(event));
shell.setText("Polygon");
shell.setSize(280, 280);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawPolygon(Event e) {
GC gc = e.gc;
Color grayCol = new Color(e.display, 120, 120, 120);
gc.setBackground(grayCol);
gc.fillPolygon(points);
grayCol.dispose();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
PolygonEx ex = new PolygonEx(display);
display.dispose();
}
}
该示例绘制了一个起始对象。
private final int points[] = { 0, 85, 75, 75, 100, 10,
125, 75, 200, 85, 150, 125, 160, 190, 100, 150,
40, 190, 50, 125, 0, 85 };
这些是多边形的坐标。 该数组由成对的 x 和 y 坐标组成。
Color grayCol = new Color(e.display, 120, 120, 120);
多边形以某种灰色绘制。
gc.fillPolygon(points);
fillPolygon()填充封闭多边形的内部,该多边形由指定的整数坐标数组定义。

图:多边形
透明矩形
透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)
TransparentRectanglesEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program draws ten rectangles with different
* levels of transparency.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class TrasparentRectanglesEx {
public TrasparentRectanglesEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.addListener(SWT.Paint, event -> drawRectangles(event));
shell.setText("Transparent rectangles");
shell.setSize(590, 120);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawRectangles(Event e) {
GC gc = e.gc;
Color blueCol = new Color(e.display, 0, 0, 255);
gc.setBackground(blueCol);
for (int i = 1; i < 11; i++) {
gc.setAlpha(i * 25);
gc.fillRectangle(50 * i, 20, 40, 40);
}
blueCol.dispose();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
TrasparentRectanglesEx ex = new TrasparentRectanglesEx(display);
display.dispose();
}
}
在该示例中,我们绘制了十个透明度不同的矩形。
gc.setAlpha(i * 25);
setAlpha()方法设置 alpha 透明度值。

图:透明矩形
甜甜圈
在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。
DonutEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program creates a donut shape.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class DonutEx {
public DonutEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
shell.addListener(SWT.Paint, event -> drawDonut(event));
shell.setText("Donut");
shell.setSize(430, 300);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawDonut(Event e) {
GC gc = e.gc;
int w = e.width;
int h = e.height;
gc.setAntialias(SWT.ON);
Transform tr = new Transform(e.display);
tr.translate(w / 2, h / 2);
gc.setTransform(tr);
for (int rot = 0; rot < 36; rot++) {
tr.rotate(5f);
gc.setTransform(tr);
gc.drawOval(-125, -40, 250, 80);
}
tr.dispose();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
DonutEx ex = new DonutEx(display);
display.dispose();
}
}
在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此称为甜甜圈。
gc.setAntialias(SWT.ON);
我们使用setAntialias()方法打开抗锯齿功能,这可以使绘图更平滑。
Transform tr = new Transform(e.display);
tr.translate(w / 2, h / 2);
gc.setTransform(tr);
我们将轴的中心移到窗口的中心。
for (int rot = 0; rot < 36; rot++) {
tr.rotate(5f);
gc.setTransform(tr);
gc.drawOval(-125, -40, 250, 80);
}
在for循环中,我们进行旋转操作并绘制椭圆。
绘制文字
在下一个示例中,我们在窗口上绘制一些文本。
SoulmateEx.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* This program draws text
* on the window.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class LyricsEx {
public LyricsEx(Display display) {
initUI(display);
}
private void initUI(Display display) {
Shell shell = new Shell(display);
shell.addListener(SWT.Paint, event -> drawLyrics(event));
shell.setText("Soulmate");
shell.setSize(380, 300);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void drawLyrics(Event e) {
GC gc = e.gc;
gc.setAntialias(SWT.ON);
Font font = new Font(e.display, "Purisa", 10, SWT.NORMAL);
Color col = new Color(e.display, 25, 25, 25);
gc.setForeground(col);
gc.setFont(font);
gc.drawText("Most relationships seem so transitory", 20, 30);
gc.drawText("They're good but not the permanent one", 20, 60);
gc.drawText("Who doesn't long for someone to hold", 20, 120);
gc.drawText("Who knows how to love without being told", 20, 150);
gc.drawText("Somebody tell me why I'm on my own", 20, 180);
gc.drawText("If there's a soulmate for everyone", 20, 210);
col.dispose();
font.dispose();
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
LyricsEx ex = new LyricsEx(display);
display.dispose();
}
}
我们显示 Natasha Bedingfields Soulmate 歌曲的部分歌词。
Font font = new Font(e.display, "Purisa", 10, SWT.NORMAL);
在这里,我们指定使用的字体。
gc.drawText("Most relationships seem so transitory", 20, 30);
drawText()方法将文本绘制到窗口上。

图:灵魂伴侣
在 Java SWT 教程的这一章中,我们做了一些绘图。
Java SWT 中的贪食蛇
在 Java SWT 教程的这一部分中,我们创建了贪食蛇游戏克隆。
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的; 后来它被带到 PC 上。 在此游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10 像素。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,窗口中央将显示"Game Over"消息。
Board.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
public class Board extends Canvas {
private final int WIDTH = 300;
private final int HEIGHT = 300;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 900;
private final int RAND_POS = 29;
private final int DELAY = 140;
private int x[] = new int[ALL_DOTS];
private int y[] = new int[ALL_DOTS];
private int dots;
private int apple_x;
private int apple_y;
private boolean left = false;
private boolean right = true;
private boolean up = false;
private boolean down = false;
private boolean inGame = true;
private Image ball;
private Image apple;
private Image head;
private Display display;
private Shell shell;
private Runnable runnable;
public Board(Shell shell) {
super(shell, SWT.NULL);
this.shell = shell;
initBoard();
}
private void initBoard() {
display = shell.getDisplay();
addListener(SWT.Paint, event -> doPainting(event));
addListener(SWT.KeyDown, event -> onKeyDown(event));
addListener(SWT.Dispose, event -> {
ball.dispose();
apple.dispose();
head.dispose();
});
Color col = new Color(shell.getDisplay(), 0, 0, 0);
setBackground(col);
col.dispose();
loadImages();
initGame();
}
private void loadImages() {
ImageData iib = new ImageData("dot.png");
ball = new Image(display, iib);
ImageData iia = new ImageData("apple.png");
apple = new Image(display, iia);
ImageData iih = new ImageData("head.png");
head = new Image(display, iih);
}
private void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z * 10;
y[z] = 50;
}
locateApple();
runnable = new Runnable() {
@Override
public void run() {
if (inGame) {
checkApple();
checkCollision();
move();
}
display.timerExec(DELAY, this);
redraw();
}
};
display.timerExec(DELAY, runnable);
};
private void doPainting(Event e) {
GC gc = e.gc;
Color col = new Color(shell.getDisplay(), 0, 0, 0);
gc.setBackground(col);
col.dispose();
gc.setAntialias(SWT.ON);
if (inGame) {
drawObjects(e);
} else {
gameOver(e);
}
}
private void drawObjects(Event e) {
GC gc = e.gc;
gc.drawImage(apple, apple_x, apple_y);
for (int z = 0; z < dots; z++) {
if (z == 0) {
gc.drawImage(head, x[z], y[z]);
} else {
gc.drawImage(ball, x[z], y[z]);
}
}
}
private void gameOver(Event e) {
GC gc = e.gc;
String msg = "Game Over";
Font font = new Font(e.display, "Helvetica", 12, SWT.NORMAL);
Color whiteCol = new Color(e.display, 255, 255, 255);
gc.setForeground(whiteCol);
gc.setFont(font);
Point size = gc.textExtent(msg);
gc.drawText(msg, (WIDTH - size.x) / 2, (HEIGHT - size.y) / 2);
font.dispose();
whiteCol.dispose();
display.timerExec(-1, runnable);
}
private void checkApple() {
if ((x[0] == apple_x) && (y[0] == apple_y)) {
dots++;
locateApple();
}
}
private void move() {
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
if (left) {
x[0] -= DOT_SIZE;
}
if (right) {
x[0] += DOT_SIZE;
}
if (up) {
y[0] -= DOT_SIZE;
}
if (down) {
y[0] += DOT_SIZE;
}
}
public void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
if (y[0] > HEIGHT - DOT_SIZE) {
inGame = false;
}
if (y[0] < 0) {
inGame = false;
}
if (x[0] > WIDTH - DOT_SIZE) {
inGame = false;
}
if (x[0] < 0) {
inGame = false;
}
}
public void locateApple() {
int r = (int) (Math.random() * RAND_POS);
apple_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
apple_y = ((r * DOT_SIZE));
}
private void onKeyDown(Event e) {
int key = e.keyCode;
if ((key == SWT.ARROW_LEFT) && (!right)) {
left = true;
up = false;
down = false;
}
if ((key == SWT.ARROW_RIGHT) && (!left)) {
right = true;
up = false;
down = false;
}
if ((key == SWT.ARROW_UP) && (!down)) {
up = true;
right = false;
left = false;
}
if ((key == SWT.ARROW_DOWN) && (!up)) {
down = true;
right = false;
left = false;
}
}
}
首先,我们将定义一些在游戏中使用的全局变量。
WIDTH和HEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。
private int x[] = new int[ALL_DOTS];
private int y[] = new int[ALL_DOTS];
这两个数组存储蛇的所有可能关节的 x 和 y 坐标。
initGame()方法初始化变量,加载图像并启动超时功能。
runnable = new Runnable() {
@Override
public void run() {
if (inGame) {
checkApple();
checkCollision();
move();
}
display.timerExec(DELAY, this);
redraw();
}
};
每隔DELAY ms,将调用run()方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。
if (inGame) {
drawObjects(e);
} else {
gameOver(e);
}
在doPainting()方法内部,我们检查inGame变量。 如果是真的,我们将绘制对象-苹果和蛇关节。 否则,我们显示"Game Over"文本。
private void drawObjects(Event e) {
GC gc = e.gc;
gc.drawImage(apple, apple_x, apple_y);
for (int z = 0; z < dots; z++) {
if (z == 0) {
gc.drawImage(head, x[z], y[z]);
} else {
gc.drawImage(ball, x[z], y[z]);
}
}
}
drawObjects()方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。
public void checkApple() {
if ((x[0] == apple_x) && (y[0] == apple_y)) {
dots++;
locateApple();
}
}
checkApple()方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locateApple()方法,该方法将随机放置一个新的Apple对象。
在move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
该代码将关节向上移动。
if (left) {
x[0] -= DOT_SIZE;
}
将头向左移动。
在checkCollision()方法中,我们确定蛇是否击中了自己或撞墙之一。
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
如果蛇用头撞到关节之一,我们就结束游戏。
if (y[0] > HEIGHT - DOT_SIZE) {
inGame = false;
}
如果蛇击中了棋盘的底部,我们就结束了游戏。
locateApple()方法在板上随机放置一个苹果。
int r = (int) (Math.random() * RAND_POS);
我们得到一个从 0 到RAND_POS-1的随机数。
apple_x = ((r * DOT_SIZE));
...
apple_y = ((r * DOT_SIZE));
这些行设置了apple对象的 x 和 y 坐标。
在onKeyDown()方法中,我们确定按下的键。
if ((key == SWT.ARROW_LEFT) && (!right)) {
left = true;
up = false;
down = false;
}
如果按左光标键,则将left变量设置为true。 在move()方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
Nibbles.java
package com.zetcode;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* ZetCode Java SWT tutorial
*
* In this code example, we create
* a Nibbles game clone
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class Nibbles {
private final int WIDTH = 300;
private final int HEIGHT = 300;
public Nibbles(Display display) {
initUI(display);
}
@SuppressWarnings("unused")
private void initUI(Display display) {
Shell shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
FillLayout layout = new FillLayout();
shell.setLayout(layout);
Board board = new Board(shell);
shell.setText("Nibbles");
int borW = shell.getSize().x - shell.getClientArea().width;
int borH = shell.getSize().y - shell.getClientArea().height;
shell.setSize(WIDTH + borW, HEIGHT + borH);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
@SuppressWarnings("unused")
public static void main(String[] args) {
Display display = new Display();
Nibbles ex = new Nibbles(display);
display.dispose();
}
}
在这个类中,我们设置了贪食蛇游戏。
int borW = shell.getSize().x - shell.getClientArea().width;
int borH = shell.getSize().y - shell.getClientArea().height;
shell.setSize(WIDTH + borW, HEIGHT + borH);
在设置外壳大小时,我们还需要考虑窗口装饰。

图:贪食蛇
这是使用 SWT 库和 Java 编程语言编程的贪食蛇电脑游戏。
C# Winforms Mono 教程
这是 C# Winforms Mono 教程。 在本教程中,我们将学习使用 C# & Winforms 进行 GUI 编程。 C# Winforms Mono 教程适合初学者。
目录
Winforms
Windows Forms 是图形用户界面应用编程接口(API),包含在 Microsoft .NET Framework 中。 截至 2008 年 5 月 13 日,Mono 的System.Windows.Forms 2.0 已完成 API。 简而言之,Winforms 是一个用于创建 GUI 应用的库。
相关教程
您可能还会对以下相关教程感兴趣: C# 教程, GTK# 教程, ASP.NET Core 教程。
Mono Winforms 简介
Mono Winforms 教程的第一部分介绍了 Mono 平台和 Winforms 库。
关于本教程
这是 Mono C# Winforms 教程。 Mono Winforms 教程适用于初学者。 本教程的目的是向读者介绍 Mono Winforms 中 GUI 编程的基础知识。 本教程是在 Linux 上创建并测试的。 但是,它也可以在其他操作系统上使用。 大多数示例都应运行而无需修改。 可以在此处下载本教程中使用的图像。
Mono
Mono Project 是 Xamarin 赞助的一项开放开发计划,目的是开发 Microsoft.NET 开发平台的开源 UNIX 版本。 它是.NET 兼容的工具集,其中包括 C# 编译器,公共语言运行库,ADO.NET,ASP.NET 和 Winforms 库。
单声道可分为三组:
- 核心组成
- Gnome 开发栈
- Microsoft 兼容性栈
核心组件是 C# 语言和公共语言运行时。 Gnome 开发栈包括 GTK# 库和各种数据库连接库。 最后,Microsoft 兼容性栈包括 ADO.NET,ASP.NET 和 Winforms 库。
Mono 是多平台编程平台。 它可以在 Linux,BSD,Mac OS X,Solaris 和 Windows 操作系统上运行。 这是一种多语言的工作。 目前,仅完全支持 C# 语言。 诸如 Visual Basic 或 IronPython 之类的语言尚未完成。
Winforms
Windows Forms 是图形用户界面应用编程接口(API),包含在 Microsoft .NET Framework 中。 截至 2008 年 5 月 13 日,Mono 的 System.Windows.Forms 2.0 已完成 API。 简而言之,Winforms 是一个用于创建 GUI 应用的库。
编译示例
我们的教程使用 C# 语言。 要编译本教程中的所有示例,我们使用gmcs编译器。 gmcs编译器实现了完整的 C# 2.0 规范,包括泛型。 C# 源文件必须以.cs 扩展名结尾。
$ gmcs --about
The Mono C# compiler is (C) 2001-2008, Novell, Inc.
The compiler source code is released under the terms of the GNU GPL
For more information on Mono, visit the project Web site
http://www.mono-project.com
The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig,
Marek Safar, Raja R Harinath, Atushi Enomoto
我们有一个简单的 C# 源文件。 稍后我们将讨论源代码。 现在,我们专注于编译过程。
simple.cs
using System.Windows.Forms;
using System.Drawing;
public class Simple : Form
{
public Simple()
{
Text = "Simple";
Size = new Size(250, 200);
CenterToScreen();
}
static public void Main()
{
Application.Run(new Simple());
}
}
C# 应用使用称为程序集的模块。 默认情况下,Mono C# 编译器仅引用三个程序集:mscorlib.dll,System.dll和System.Xml.dll。 必须使用编译器的命令行选项指定任何其他程序集。 在我们的简单示例中,我们需要System.Windows.Forms.dll和System.Drawing.dll程序集。
gmcs -r:System.Windows.Forms.dll -r:System.Drawing.dll simple.cs
在这里,我们编译simple.cs源文件。 如果未指定输出文件名,则将获得名称等于源文件名的 exe 文件。 在我们的示例中,为simple.exe文件。
gmcs -r:System.Windows.Forms.dll -r:System.Drawing.dll simple.cs -out:simpleexample.exe
在这里,我们指定输出文件名。 我们编译源文件并获取simpleexample.exe文件。
参考
这是 Mono Winforms 库的简介。
Mono Winforms 中的第一步
在 Mono Winforms 教程的这一部分中,我们介绍 Winforms 编程库中的一些基本程序。
简单
这是一个简单的 Winforms 应用。
simple.cs
using System.Windows.Forms;
using System.Drawing;
public class Simple : Form
{
public Simple()
{
Text = "Simple";
Size = new Size(250, 200);
CenterToScreen();
}
static public void Main()
{
Application.Run(new Simple());
}
}
此代码示例在屏幕上显示一个小窗口。
using System.Windows.Forms;
using System.Drawing;
在这里,我们使用using指令,该指令允许我们使用适当名称空间中的类型,而无需使用完全限定的名称。 例如,我们现在可以编写Form而不是System.Windows.Forms.Form。
public class Simple : Form
{
...
}
在 Winforms 中,任何窗口或对话框都是Form。 该控件是一个基本容器,其目的是显示其他子控件。 我们的类Simple继承自表单。 这样,它本身就成为一种形式。
Text = "Simple";
Size = new Size(250, 200);
Text和Size是表单的属性。 更改这些属性,我们将修改表单控件。 第一行在表单控件的标题栏中显示文本"Simple"。 第二行将表单的大小设置为250x200px。
CenterToScreen();
这种方法将我们的应用集中在屏幕上。
static public void Main()
{
Application.Run(new Simple());
}
编译并运行后,将首先执行Main方法。 该代码实例化Simple类并运行它。
$ gmcs -r:System.Windows.Forms.dll -r:System.Drawing.dll simple.cs
这是我们编译源代码的方式。 如果没有犯任何错误,则应在当前工作目录中包含simple.exe文件。

图:简单
图标
Mono 在西班牙语中意为猴子。 如果我们不为应用提供图标,则默认情况下,我们的头是猴子。 下一个示例显示如何更改此设置。
icon.cs
using System.Windows.Forms;
using System.Drawing;
using System;
public class MForm : Form
{
public MForm()
{
Text = "Icon";
Size = new Size(250, 200);
try {
Icon = new Icon("web.ico");
} catch (Exception e) {
Console.WriteLine(e.Message);
Environment.Exit(1);
}
CenterToScreen();
}
static public void Main()
{
Application.Run(new MForm());
}
}
该代码示例在窗体的左上角显示一个图标。 表单的图标是代表任务栏中表单的图片以及为表单的控制框显示的图标。
try {
Icon = new Icon("web.ico");
} catch (Exception e) {
Console.WriteLine(e.Message);
Environment.Exit(1);
}
最好将所有输入输出工作放在try/catch关键字之间。 web.ico文件必须在当前工作目录中可用。 这是我们执行(./icon.exe)应用的目录。

图:图标
工具提示
工具提示是一个小的矩形弹出窗口,当用户将指针放在控件上时,它会显示控件目的的简短说明。
tooltips.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Tooltips";
Size = new Size(250, 200);
ToolTip btnTlp = new ToolTip();
btnTlp.SetToolTip(this, "This is a Form");
Button button = new Button();
btnTlp.SetToolTip(button, "This is a Button Control");
button.Text = "Button";
button.Location = new Point(30, 70);
button.Parent = this;
CenterToScreen();
}
}
class MApplication {
static void Main() {
Application.Run(new MForm());
}
}
我们的代码示例为两个控件创建一个工具提示。 Button控件和Form控件。
ToolTip btnTlp = new ToolTip();
在这里,我们创建ToolTip控件。 此实例用于为两个控件提供工具提示。
btnTlp.SetToolTip(this, "This is a Form");
在这里,我们为表单设置工具提示。
btnTlp.SetToolTip(button, "This is a Button Control");
这里是我们的按钮。
Button button = new Button();
btnTlp.SetToolTip(button, "This is a Button Control");
button.Text = "Button";
button.Location = new Point(30, 70);
button.Parent = this;
注意Button控件的创建。 Text属性是按钮的标签。 Location属性将按钮放在x = 30,y = 70px坐标的表单上。 最后,Parent属性确定按钮所在的容器。

图:工具提示 s
按钮
我们的最后一个代码示例显示了一个有效的按钮控件。
button.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Button";
Size = new Size(250, 200);
Button button = new Button();
button.Location = new Point(30, 20);
button.Text = "Quit";
button.Click += new EventHandler(OnClick);
button.MouseEnter += new EventHandler(OnEnter);
Controls.Add(button);
CenterToScreen();
}
void OnClick(object sender, EventArgs e) {
Close();
}
void OnEnter(object sender, EventArgs e) {
Console.WriteLine("Button Entered");
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
所有 GUI 编程都是事件驱动的编程。 在我们的示例中,我们在表单容器上显示了一个按钮控件。 该按钮将收听两个事件。 Click和MouseEnter事件。
button.Click += new EventHandler(OnClick);
此代码行将事件处理器插入Click事件。 当我们单击按钮时,将调用OnClick()方法。
button.MouseEnter += new EventHandler(OnEnter);
当我们使用鼠标指针进入按钮区域时,将触发MouseEnter事件。 在这种情况下,我们的代码将调用OnEnter()方法。
void OnClick(object sender, EventArgs e) {
Close();
}
该方法关闭应用。
void OnEnter(object sender, EventArgs e) {
Console.WriteLine("Button Entered");
}
当我们使用鼠标指针进入按钮控制区域时,终端中将显示"Button Entered"文本。
Mono Winforms 教程的这一部分显示了一些入门代码示例,以帮助您开始使用 Winforms 编程库。
Mono Winforms 中的布局管理
Mono Winforms 教程继续进行控件的布局管理。 在将控件放置在其父容器上之后,我们必须确保其布局正确。
Anchor
控件的Anchor属性确定如何使用其父控件调整其大小。 锚是海洋世界中的一个术语。 当船锚掉入水中时,船就固定在某个地方。 Winforms 控件也是如此。
Winforms 中的每个控件都可以具有以下AnchorStyles值之一:
TopLeftRightBottom
注意,控件不限于一个值。 他们可以使用|组合这些值。 运算符。
Anchor基本示例
下面的示例显示一个非常基本的示例,演示Anchor属性。
anchor.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Anchor";
Size = new Size(210, 210);
Button btn1 = new Button();
btn1.Text = "Button";
btn1.Parent = this;
btn1.Location = new Point(30, 30);
Button btn2 = new Button();
btn2.Text = "Button";
btn2.Parent = this;
btn2.Location = new Point(30, 80);
btn2.Anchor = AnchorStyles.Right;
CenterToScreen();
}
}
class MApplication {
public static void Main() {
MForm mf = new MForm();
Application.Run(mf);
}
}
这是一个非常基本的代码示例,清楚地显示了Anchor属性的含义。 我们在表单上有两个按钮。 第一个按钮具有默认的AnchorStyles值,即AnchorStyles.Left。 第二个按钮明确设置了AnchorStyles.Right。
btn2.Anchor = AnchorStyles.Right;
我们将第二个按钮的Anchor属性明确设置为AnchorStyles.Right值。
现在看看以下两个图像。 左边的是开始时显示的应用。 调整大小后,右侧显示相同的应用。 第一个按钮与表单的左边界和上边界保持距离。 第二个按钮与表单的右边框保持距离。 但是它在垂直方向上没有保持任何距离。


图:调整大小前后
Dock
Dock属性允许我们将控件粘贴到父窗体或控件的特定边缘。
以下是可能的DockStyle值。
TopLeftRightBottomFillNone
编辑器骨架
以下代码示例演示了正在使用的Dock属性。
editor.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Editor";
Size = new Size(210, 180);
MainMenu mainMenu = new MainMenu();
MenuItem file = mainMenu.MenuItems.Add("&File");
file.MenuItems.Add(new MenuItem("E&xit",
new EventHandler(this.OnExit), Shortcut.CtrlX));
Menu = mainMenu;
TextBox tb = new TextBox();
tb.Parent = this;
tb.Dock = DockStyle.Fill;
tb.Multiline = true;
StatusBar sb = new StatusBar();
sb.Parent = this;
sb.Text = "Ready";
CenterToScreen();
}
void OnExit(object sender, EventArgs e) {
Close();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们显示一个菜单栏和一个状态栏。 其余区域由TextBox控件占用。
TextBox tb = new TextBox();
tb.Parent = this;
在这里,我们创建TextBox控件。 Form容器被设置为文本框的父级。
tb.Dock = DockStyle.Fill;
此代码行使TextBox控件占用了表单容器内的剩余空间。

图:编辑器骨架
固定按钮
下一个示例显示了位于窗体右下角的两个按钮。
anchoredbuttons.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private int WIDTH = 250;
private int HEIGHT = 150;
private int BUTTONS_SPACE = 15;
private int PANEL_SPACE = 8;
private int CLOSE_SPACE = 10;
public MForm() {
Text = "Anchor";
Size = new Size(WIDTH, HEIGHT);
Button ok = new Button();
int PANEL_HEIGHT = ok.Height + PANEL_SPACE;
Panel panel = new Panel();
panel.Height = PANEL_HEIGHT;
panel.Dock = DockStyle.Bottom;
panel.Parent = this;
int x = ok.Width * 2 + BUTTONS_SPACE;
int y = (PANEL_HEIGHT - ok.Height) / 2;
ok.Text = "Ok";
ok.Parent = panel;
ok.Location = new Point(WIDTH-x, y);
ok.Anchor = AnchorStyles.Right;
Button close = new Button();
x = close.Width;
close.Text = "Close";
close.Parent = panel;
close.Location = new Point(WIDTH-x-CLOSE_SPACE, y);
close.Anchor = AnchorStyles.Right;
CenterToScreen();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
该示例在对话框的右下角显示确定,关闭按钮,这在对话框窗口中很常见。
private int WIDTH = 250;
private int HEIGHT = 150;
WIDTH和HEIGHT变量确定应用窗口的宽度和高度。
private int BUTTONS_SPACE = 15;
private int PANEL_SPACE = 8;
private int CLOSE_SPACE = 10;
BUTTONS_SPACE是“确定”和“关闭”按钮之间的空间。 PANEL_SPACE是面板和表单底部之间的空间。 最后,CLOSE_SPACE变量设置“关闭”按钮和表单右边框之间的间隔。
int PANEL_HEIGHT = ok.Height + PANEL_SPACE;
在这里,我们计算面板的高度。 面板的高度基于“确定”按钮的高度。 并且我们添加了一些额外的空间,以使按钮不会太靠近边框。
Panel panel = new Panel();
panel.Height = PANEL_HEIGHT;
panel.Dock = DockStyle.Bottom;
panel.Parent = this
在这里,我们创建和管理Panel控件。 在此示例中,它用作按钮的容器。 它被粘贴到表单的底部边框。 然后将按钮放置在面板内。
ok.Text = "Ok";
ok.Parent = panel;
ok.Location = new Point(WIDTH-x, y);
ok.Anchor = AnchorStyles.Right;
“确定”按钮的父级设置为面板小部件。 计算位置。 并且Anchor属性设置为右侧。 另一个按钮的创建类似。

图:固定按钮
播放器骨架
Mono Winforms 教程这一部分的最后一个示例显示了一个更复杂的示例。 它是音乐播放器的骨架。
player.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Player";
Size = new Size(350, 280);
MainMenu mainMenu = new MainMenu();
MenuItem file = mainMenu.MenuItems.Add("&File");
MenuItem playm = mainMenu.MenuItems.Add("&Play");
MenuItem view = mainMenu.MenuItems.Add("&View");
MenuItem tools = mainMenu.MenuItems.Add("&Tools");
MenuItem favourites = mainMenu.MenuItems.Add("&Favourites");
MenuItem help = mainMenu.MenuItems.Add("&Help");
file.MenuItems.Add(new MenuItem("E&xit",
new EventHandler(this.OnExit), Shortcut.CtrlX));
Menu = mainMenu;
Panel panel = new Panel();
panel.Parent = this;
panel.BackColor = Color.Black;
panel.Dock = DockStyle.Fill;
Panel buttonPanel = new Panel();
buttonPanel.Parent = this;
buttonPanel.Height = 40;
buttonPanel.Dock = DockStyle.Bottom;
Button pause = new Button();
pause.FlatStyle = FlatStyle.Popup;
pause.Parent = buttonPanel;
pause.Location = new Point(5, 10);
pause.Size = new Size(25, 25);
pause.Image = new Bitmap("pause.png");
Button play = new Button();
play.FlatStyle = FlatStyle.Popup;
play.Parent = buttonPanel;
play.Location = new Point(35, 10);
play.Size = new Size(25, 25);
play.Image = new Bitmap("play.png");
Button forward = new Button();
forward.FlatStyle = FlatStyle.Popup;
forward.Parent = buttonPanel;
forward.Location = new Point(80, 10);
forward.Size = new Size(25, 25);
forward.Image = new Bitmap("forward.png");
Button backward = new Button();
backward.FlatStyle = FlatStyle.Popup;
backward.Parent = buttonPanel;
backward.Location = new Point(110, 10);
backward.Size = new Size(25, 25);
backward.Image = new Bitmap("backward.png");
TrackBar tb = new TrackBar();
tb.Parent = buttonPanel;
tb.TickStyle = TickStyle.None;
tb.Size = new Size(150, 25);
tb.Location = new Point(200, 10);
tb.Anchor = AnchorStyles.Right;
Button audio = new Button();
audio.FlatStyle = FlatStyle.Popup;
audio.Parent = buttonPanel;
audio.Size = new Size(25, 25);
audio.Image = new Bitmap("audio.png");
audio.Location = new Point(170, 10);
audio.Anchor = AnchorStyles.Right;
StatusBar sb = new StatusBar();
sb.Parent = this;
sb.Text = "Ready";
CenterToScreen();
}
void OnExit(object sender, EventArgs e) {
Close();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
这是一个更复杂的示例,它同时显示了Dock和Anchor属性。
MainMenu mainMenu = new MainMenu();
MenuItem file = mainMenu.MenuItems.Add("&File");
...
Menu = mainMenu;
在这里,我们创建菜单栏。
Panel panel = new Panel();
panel.Parent = this;
panel.BackColor = Color.Black;
panel.Dock = DockStyle.Fill;
这是黑色的面板,占据了菜单栏,状态栏和控制面板剩余的所有剩余空间。
Panel buttonPanel = new Panel();
buttonPanel.Parent = this;
buttonPanel.Height = 40;
buttonPanel.Dock = DockStyle.Bottom;
这是控制面板。 它的父级是表单容器。 它被粘贴到表格的底部。 高度为 40 像素。 在此控制面板内部,我们放置了所有按钮和轨迹仪。
Button pause = new Button();
pause.FlatStyle = FlatStyle.Popup;
pause.Parent = buttonPanel;
pause.Location = new Point(5, 10);
pause.Size = new Size(25, 25);
pause.Image = new Bitmap("pause.png");
暂停按钮是具有默认Anchor属性值的四个按钮之一。 该按钮的样式设置为平面,因为它看起来更好。 我们在按钮上放置一个位图。
tb.Anchor = AnchorStyles.Right;
...
audio.Anchor = AnchorStyles.Right;
最后两个控件固定在右侧。

图:播放器骨架
Mono Winforms 教程的这一部分是关于控件的布局管理的。 我们实践了 Winforms 库提供的各种可能性。
Mono Winforms 中的菜单和工具栏
在 Mono Winforms 教程的这一部分中,我们将讨论菜单和工具栏。
菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。
简单菜单
在第一个示例中,我们创建一个简单的菜单。
simplemenu.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Simple menu";
MenuStrip ms = new MenuStrip();
ms.Parent = this;
ToolStripMenuItem file = new ToolStripMenuItem("&File");
ToolStripMenuItem exit = new ToolStripMenuItem("&Exit", null,
new EventHandler(OnExit));
exit.ShortcutKeys = Keys.Control | Keys.X;
file.DropDownItems.Add(exit);
ms.Items.Add(file);
MainMenuStrip = ms;
Size = new Size(250, 200);
CenterToScreen();
}
void OnExit(object sender, EventArgs e) {
Close();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在我们的示例中,我们有一个菜单栏和一个菜单。 菜单内有一个菜单项。 如果选择菜单项,则应用关闭。
注意如何关闭应用。 我们可以使用 Ctrl + X 快捷方式或按 Alt , F , E 键关闭它 。
MenuStrip ms = new MenuStrip();
MenuStrip为我们的表单创建一个菜单系统。 我们将ToolStripMenuItem对象添加到 MenuStrip 中,这些对象代表菜单结构中的各个菜单命令。 每个ToolStripMenuItem可以是您的应用的命令,也可以是其他子菜单项的父菜单。
ToolStripMenuItem file = new ToolStripMenuItem("&File");
在这里,我们创建一个菜单。
ToolStripMenuItem exit = new ToolStripMenuItem("&Exit", null,
new EventHandler(OnExit));
此行创建退出菜单项。
exit.ShortcutKeys = Keys.Control | Keys.X;
我们提供了退出菜单项的快捷方式。
file.DropDownItems.Add(exit);
退出菜单项将添加到菜单对象的下拉项中。
ms.Items.Add(file);
在这里,我们将菜单对象添加到菜单栏中。
MainMenuStrip = ms;
MenuStrip已插入表格。

图:简单菜单
子菜单
每个菜单项也可以有一个子菜单。 这样,我们可以将类似的命令分组。 例如,我们可以将隐藏/显示各种工具栏(例如个人栏,地址栏,状态栏或导航栏)的命令放置在称为工具栏的子菜单中。
submenu.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Submenu";
MenuStrip ms = new MenuStrip();
ms.Parent = this;
ToolStripMenuItem file = new ToolStripMenuItem("&File");
ToolStripMenuItem exit = new ToolStripMenuItem("&Exit", null,
new EventHandler(OnExit));
ToolStripMenuItem import = new ToolStripMenuItem();
import.Text = "Import";
file.DropDownItems.Add(import);
ToolStripMenuItem temp = new ToolStripMenuItem();
temp.Text = "Import newsfeed list...";
import.DropDownItems.Add(temp);
temp = new ToolStripMenuItem();
temp.Text = "Import bookmarks...";
import.DropDownItems.Add(temp);
temp = new ToolStripMenuItem();
temp.Text = "Import mail...";
import.DropDownItems.Add(temp);
file.DropDownItems.Add(exit);
ms.Items.Add(file);
MainMenuStrip = ms;
Size = new Size(380, 200);
CenterToScreen();
}
void OnExit(object sender, EventArgs e) {
Close();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在此示例中,我们创建一个子菜单。 子菜单导入具有三个菜单项。
ToolStripMenuItem import = new ToolStripMenuItem();
import.Text = "Import";
ToolStripMenuItem可以是菜单或菜单项。 在这里它将作为子菜单。
ToolStripMenuItem temp = new ToolStripMenuItem();
temp.Text = "Import newsfeed list...";
import.DropDownItems.Add(temp);
在这里,我们创建一个菜单项并将其添加到“导入”子菜单。

图:子菜单
复选菜单项
下一个代码示例演示如何创建复选菜单项。
checkmenuitem.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private StatusBar sb;
private MenuItem viewStatusBar;
public MForm() {
Text = "Check menu item";
sb = new StatusBar();
sb.Parent = this;
sb.Text = "Ready";
MainMenu mainMenu = new MainMenu();
MenuItem file = mainMenu.MenuItems.Add("&File");
file.MenuItems.Add(new MenuItem("E&xit",
new EventHandler(OnExit), Shortcut.CtrlX));
MenuItem view = mainMenu.MenuItems.Add("&View");
viewStatusBar = new MenuItem("View StatusBar");
viewStatusBar.Checked = true;
viewStatusBar.Click += new EventHandler(ToggleStatusBar);
view.MenuItems.Add(viewStatusBar);
Menu = mainMenu;
Size = new Size(250, 200);
CenterToScreen();
}
void OnExit(object sender, EventArgs e) {
Close();
}
void ToggleStatusBar(object sender, EventArgs e) {
bool check = viewStatusBar.Checked;
if (check) {
sb.Visible = false;
viewStatusBar.Checked = false;
} else {
sb.Visible = true;
viewStatusBar.Checked = true;
}
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们有两个菜单。 文件和查看。 “查看”菜单具有一个菜单项,用于切换状态栏的可见性。
MainMenu mainMenu = new MainMenu();
在此示例中,我们使用MainMenu控件。 要创建菜单栏,我们可以使用MainMenu或MenuStrip控件。 后者具有一些附加功能。
viewStatusBar.Checked = true;
默认情况下会选中此菜单项,因为状态栏从应用的开始就可见。
bool check = viewStatusBar.Checked;
if (check) {
sb.Visible = false;
viewStatusBar.Checked = false;
} else {
sb.Visible = true;
viewStatusBar.Checked = true;
}
我们确定菜单项是否被选中。 我们根据check值显示和隐藏状态栏和复选标记。

图:选中菜单项
图像,分隔符
我们将进一步增强对MenuStrip控件的了解。 我们将创建一个带有图像的菜单项,并显示如何使用分隔符将其分开。
menustrip.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "MenuStrip";
Size = new Size(250, 200);
MenuStrip menuStrip = new MenuStrip();
ToolStripMenuItem titem1 = new ToolStripMenuItem("File");
menuStrip.Items.Add(titem1);
ToolStripMenuItem titem2 = new ToolStripMenuItem("Tools");
menuStrip.Items.Add(titem2);
ToolStripMenuItem subm1 = new ToolStripMenuItem("New");
subm1.Image = Image.FromFile("new.png");
titem1.DropDownItems.Add(subm1);
ToolStripMenuItem subm2 = new ToolStripMenuItem("Open");
subm2.Image = Image.FromFile("open.png");
titem1.DropDownItems.Add(subm2);
titem1.DropDownItems.Add(new ToolStripSeparator());
ToolStripMenuItem subm3 = new ToolStripMenuItem("Exit");
subm3.Image = Image.FromFile("exit.png");
titem1.DropDownItems.Add(subm3);
subm3.Click += OnExit;
Controls.Add(menuStrip);
MainMenuStrip = menuStrip;
CenterToScreen();
}
public static void Main() {
Application.Run(new MForm());
}
void OnExit(object sender, EventArgs e) {
Close();
}
}
我们的代码示例中有两个菜单。 文件和工具。 在文件中,我们有三个带有图像的菜单项。 我们还有一个分隔符。 在此示例中,PNG 图像必须位于当前工作目录中。
ToolStripMenuItem subm1 = new ToolStripMenuItem("New");
subm1.Image = Image.FromFile("new.png");
titem1.DropDownItems.Add(subm1);
在这里,我们创建第一个菜单项。 要将图像添加到项目,我们将Image属性设置为图像。 我们使用静态FromFile()方法从指定的文件创建一个Image。
titem1.DropDownItems.Add(new ToolStripSeparator());
在这里,我们向“文件”菜单添加分隔符。

图:图像 s and separator
工具栏
菜单将我们可以在应用中使用的所有命令分组。 使用工具栏可以快速访问最常用的命令。 ToolBar控件用于显示ToolBarButton控件。 我们可以通过创建ImageList将图像分配给按钮。 然后,我们将图像列表分配给工具栏的ImageList属性,并为每个ToolBarButton将图像索引值分配给ImageIndex属性。
toolbar.cs
using System;
using System.Drawing;
using System.Windows.Forms;
public class MForm : Form
{
private ImageList toolBarIcons;
private ToolBarButton save;
private ToolBarButton exit;
private ToolBar toolBar;
public MForm()
{
Size = new Size(250, 200);
Text = "Simple toolbar";
toolBar = new ToolBar();
toolBar.Parent = this;
toolBarIcons = new ImageList();
save = new ToolBarButton();
exit = new ToolBarButton();
save.ImageIndex = 0;
save.Tag = "Save";
exit.ImageIndex = 1;
exit.Tag = "Exit";
toolBar.ImageList = toolBarIcons;
toolBar.ShowToolTips = true;
toolBar.Buttons.AddRange(new ToolBarButton[] {save, exit});
toolBar.ButtonClick += new ToolBarButtonClickEventHandler(OnClicked);
toolBarIcons.Images.Add(new Icon("new.ico"));
toolBarIcons.Images.Add(new Icon("exit.ico"));
CenterToScreen();
}
static void Main()
{
Application.Run(new MForm());
}
void OnClicked(object sender, ToolBarButtonClickEventArgs e)
{
if (e.Button.Tag.Equals("Exit"))
Close();
}
}
在我们的示例中,我们在工具栏上显示了两个按钮。
toolBar = new ToolBar();
在这里,我们创建ToolBar控件。
toolBarIcons = new ImageList();
创建图像列表。
save = new ToolBarButton();
exit = new ToolBarButton();
这是两个工具栏按钮。
save.ImageIndex = 0;
我们确定图像列表中的哪个图标将用于保存工具栏按钮。
toolBar.Buttons.AddRange(new ToolBarButton[] {save, exit});
ToolBarButton控件已添加到工具栏。
toolBarIcons.Images.Add(new Icon("new.ico"));
toolBarIcons.Images.Add(new Icon("exit.ico"));
图标将添加到图像列表。
if (e.Button.Tag.Equals("Exit"))
Close();
如果按钮的标签等于"Exit",我们将关闭该应用。

图:工具栏
Winforms 教程的这一部分是关于菜单和工具栏的。
Mono Winforms 中的基本控件
Mono Winforms 编程教程的这一部分将介绍基本控件。
Winforms 控件是应用的基本构建块。 Winforms 具有各种各样的控件。按钮,复选框,滑块,列表框等。程序员完成工作所需的一切。 在本教程的这一部分中,我们将描述几个有用的控件。
Label控件
Label是用于显示文本或图像的简单控件。 它没有得到关注。
label.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
string text = @"Sometimes I feel I've got to
Run away I've got to
Get away
From the pain that you drive
into the heart of me
The love we share
Seems to go nowhere
I've lost my lights
I toss and turn I can't sleep at night
Once I ran to you (I ran)
Now I'll run from you
This tainted love you've given
I give you all a boy could give you
Take my tears and that's not nearly all
Tainted love
Tainted love";
public MForm() {
Text = "Tainted Love";
Font font = new Font("Serif", 10);
Label lyrics = new Label();
lyrics.Parent = this;
lyrics.Text = text;
lyrics.Font = font;
lyrics.Location = new Point(10, 10);
lyrics.Size = new Size (290, 290);
CenterToScreen();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在我们的示例中,我们显示了 Tainted Love 歌曲的歌词。
Label lyrics = new Label();
Label控件已创建。
string text = @"Sometimes I feel I've got ...
@字符用于表示多行字符串。
Font font = new Font("Serif", 10);
...
lyrics.Font = font;
标签文本的字体设置为 Serif,10px。

图:Label
CheckBox
CheckBox是具有两个状态的控件:开和关。 它是带有标签或图像的盒子。 如果选中了CheckBox,则在方框中用勾号表示。 CheckBox可用于在启动时显示/隐藏启动屏幕,切换工具栏的可见性等。
checkbox.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private CheckBox cb;
public MForm() {
Text = "CheckBox";
Size = new Size(220, 170);
cb = new CheckBox();
cb.Parent = this;
cb.Location = new Point(30, 30);
cb.Text = "Show Title";
cb.Checked = true;
cb.CheckedChanged += new EventHandler(OnChanged);
CenterToScreen();
}
void OnChanged(object sender, EventArgs e) {
if (cb.Checked) {
Text = "CheckBox";
} else {
Text = "";
}
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们的代码示例根据窗口的状态显示或隐藏窗口的标题。
cb = new CheckBox();
CheckBox控件已创建。
cb.Text = "Show Title";
cb.Checked = true;
当应用启动时,我们显示标题。 然后将CheckBox控件设置为选中状态。
cb.CheckedChanged += new EventHandler(OnChanged);
当我们单击CheckBox控件时,将触发CheckedChanged事件。
if (cb.Checked) {
Text = "CheckBox";
} else {
Text = "";
}
在这里,我们切换窗口的标题。

图:CheckBox
TrackBar
TrackBar是一个组件,使用户可以通过在有限的间隔内滑动旋钮来以图形方式选择一个值。 我们的示例将显示音量控制。
trackbar.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
PictureBox pb;
TrackBar tb;
Bitmap mute, min, med, max;
public MForm() {
Text = "TrackBar";
Size = new Size(260, 190);
tb = new TrackBar();
tb.Parent = this;
tb.Size = new Size(160, 30);
tb.Location = new Point(20, 40);
tb.TickStyle = TickStyle.None;
tb.ValueChanged += new EventHandler(OnChanged);
LoadImages();
pb = new PictureBox();
pb.Parent = this;
pb.Location = new Point(210, 50);
pb.Image = mute;
CenterToScreen();
}
void LoadImages() {
mute = new Bitmap("mute.png");
min = new Bitmap("min.png");
med = new Bitmap("med.png");
max = new Bitmap("max.png");
}
void OnChanged(object sender, EventArgs e) {
int val = tb.Value;
if (val == 0) {
pb.Image = mute;
} else if (val > 0 && val <= 3) {
pb.Image = min;
} else if (val > 3 && val < 8) {
pb.Image = med;
} else {
pb.Image = max;
}
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在代码示例中,我们显示了TrackBar和PictureBox。 通过拖动轨迹栏,我们可以在PictureBox控件上更改图像。
tb = new TrackBar();
TrackBar控件已创建。
tb.TickStyle = TickStyle.None;
我们对此TrackBar没有显示任何报价。
pb = new PictureBox();
...
pb.Image = mute;
PictureBox控件已创建。 它用于显示图像。 开始时,它会显示静音图像。
void LoadImages() {
mute = new Bitmap("mute.png");
min = new Bitmap("min.png");
med = new Bitmap("med.png");
max = new Bitmap("max.png");
}
在这里,我们将加载四个将要使用的图像。
int val = tb.Value;
if (val == 0) {
pb.Image = mute;
} else if (val > 0 && val <= 3) {
pb.Image = min;
} else if (val > 3 && val < 8) {
pb.Image = med;
} else {
pb.Image = max;
}
我们确定TrackBar的值。 根据其值,我们更新PictureBox控件。

图:TrackBar
ComboBox
ComboBox是一个组合了按钮或可编辑字段和下拉列表的控件。 用户可以从下拉列表中选择一个值,该列表应用户的要求出现。 如果使组合框可编辑,则组合框将包含一个可编辑字段,用户可以在其中输入值。
combobox.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private ComboBox cb;
private Label label;
public MForm() {
Text = "ComboBox";
Size = new Size(240, 240);
cb = new ComboBox();
cb.Parent = this;
cb.Location = new Point(50, 30);
cb.Items.AddRange(new object[] {"Ubuntu",
"Mandriva",
"Red Hat",
"Fedora",
"Gentoo"});
cb.SelectionChangeCommitted += new EventHandler(OnChanged);
label = new Label();
label.Location = new Point(50, 140);
label.Parent = this;
label.Text = "...";
CenterToScreen();
}
void OnChanged(object sender, EventArgs e) {
ComboBox combo = (ComboBox) sender;
label.Text = combo.Text;
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们的代码编程示例显示了一个包含五个项目的组合框。 所选项目显示在标签控件中。
cb = new ComboBox();
ComboBox控件已创建。
cb.Items.AddRange(new object[] {"Ubuntu",
"Mandriva",
"Red Hat",
"Fedora",
"Gentoo"});
ComboBox控件中充满了项目。
cb.SelectionChangeCommitted += new EventHandler(OnChanged);
如果我们从组合框中选择一个项目,则会触发SelectionChangeCommitted事件。
void OnChanged(object sender, EventArgs e) {
ComboBox combo = (ComboBox) sender;
label.Text = combo.Text;
}
在这里,将从组合框中选择的文本复制到标签。

图:ComboBox
MonthCalendar
在下一个示例中,我们将显示MonthCalendar控件。 MonthCalendar控件允许用户使用视觉显示选择日期。
monthcalendar.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private MonthCalendar calendar;
private Label date;
public MForm() {
Text = "Month Calendar";
Size = new Size(240, 240);
calendar = new MonthCalendar();
calendar.Parent = this;
calendar.Location = new Point(20, 20);
calendar.DateSelected += new DateRangeEventHandler(OnSelected);
date = new Label();
date.Location = new Point(40, 170);
date.Parent = this;
DateTime dt = calendar.SelectionStart;
date.Text = dt.Month + "/" + dt.Day + "/" + dt.Year;
CenterToScreen();
}
void OnSelected(object sender, EventArgs e) {
DateTime dt = calendar.SelectionStart;
date.Text = dt.Month + "/" + dt.Day + "/" + dt.Year;
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在示例中,我们显示了MonthCalendar和Label。
private MonthCalendar calendar;
private Label date;
我们有两个控件。 一个MonthCalendar和一个Label。 后者显示当前选择的日期。
void OnSelected(object sender, EventArgs e) {
DateTime dt = calendar.SelectionStart;
date.Text = dt.Month + "/" + dt.Day + "/" + dt.Year;
}
当我们从MonthCalendar中选择一个日期时,就会调用OnSelected()方法。 SelectionStart属性获取所选日期范围的开始日期。

图:MonthCalendar
TextBox
TextBox控件用于显示或接受某些文本。 文本可以是单行或多行。 此控件还可以进行密码屏蔽。
textbox.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private Label text;
public MForm() {
Text = "TextBox";
Size = new Size(250, 200);
CenterToScreen();
text = new Label();
text.Parent = this;
text.Text = "...";
text.Location = new Point(60, 40);
text.AutoSize = true;
TextBox tbox = new TextBox();
tbox.Parent = this;
tbox.Location = new Point(60, 100);
tbox.KeyUp += new KeyEventHandler(OnKeyUp);
}
void OnKeyUp(object sender, KeyEventArgs e) {
TextBox tb = (TextBox) sender;
this.text.Text = tb.Text;
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
本示例显示一个文本框和一个标签。 我们在文本框中键入的文本将立即显示在标签控件中。
text = new Label();
...
text.AutoSize = true;
Label控件已创建。 AutoSize属性可确保Label增长以显示文本。
TextBox tbox = new TextBox();
...
tbox.KeyUp += new KeyEventHandler(OnKeyUp);
我们插入KeyUp事件。 释放按键时,将调用OnKeyUp()方法。
void OnKeyUp(object sender, KeyEventArgs e) {
TextBox tb = (TextBox) sender;
this.text.Text = tb.Text;
}
在OnKeyUp()方法中,我们使用文本框控件中的文本更新了标签控件。

图:TextBox
我们已经完成了 Mono Winforms 教程的这一章,专门介绍基本控件。
Mono Winforms 中的高级控件
在 Mono Winforms 教程的这一部分中,我们介绍一些更高级的控件。 即ListBox,ListView和TreeView控件。
ListBox
ListBox控件用于显示项目列表。 用户可以通过单击选择一个或多个项目。
listbox.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private StatusBar sb;
public MForm() {
Text = "ListBox";
Size = new Size(210, 210);
ListBox lb = new ListBox();
lb.Parent = this;
lb.Items.Add("Jessica");
lb.Items.Add("Rachel");
lb.Items.Add("Angelina");
lb.Items.Add("Amy");
lb.Items.Add("Jennifer");
lb.Items.Add("Scarlett");
lb.Dock = DockStyle.Fill;
lb.SelectedIndexChanged += new EventHandler(OnChanged);
sb = new StatusBar();
sb.Parent = this;
CenterToScreen();
}
void OnChanged(object sender, EventArgs e) {
ListBox lb = (ListBox) sender;
sb.Text = lb.SelectedItem.ToString();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们的示例显示了一个具有六个名称的列表框。 所选项目显示在状态栏中。
ListBox lb = new ListBox();
lb.Parent = this;
ListBox控件已创建。
lb.Items.Add("Jessica");
这就是我们向ListBox控件添加新项目的方式。 该控件具有Items属性。 该属性是对列表框中项目列表的引用。 使用此引用,我们可以添加,删除或获取列表框中的项目数。
lb.SelectedIndexChanged += new EventHandler(OnChanged);
当我们选择一个项目时,会触发SelectedIndexChanged事件。
ListBox lb = (ListBox) sender;
sb.Text = lb.SelectedItem.ToString();
在OnChange()方法内部,我们获得对列表框的引用,并将所选文本设置为状态栏。

图:ListBox
ListView
ListView控件用于显示项目集合。 它是比ListBox控件更复杂的控件。 它可以在各种视图中显示数据,主要用于在多列视图中显示数据。
listview.cs
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
public class Actress
{
public string name;
public int year;
public Actress(string name, int year)
{
this.name = name;
this.year = year;
}
}
class MForm : Form {
private StatusBar sb;
public MForm() {
Text = "ListView";
Size = new Size(350, 300);
List<Actress> actresses = new List<Actress>();
actresses.Add(new Actress("Jessica Alba", 1981));
actresses.Add(new Actress("Angelina Jolie", 1975));
actresses.Add(new Actress("Natalie Portman", 1981));
actresses.Add(new Actress("Rachel Weiss", 1971));
actresses.Add(new Actress("Scarlett Johansson", 1984));
ColumnHeader name = new ColumnHeader();
name.Text = "Name";
name.Width = -1;
ColumnHeader year = new ColumnHeader();
year.Text = "Year";
SuspendLayout();
ListView lv = new ListView();
lv.Parent = this;
lv.FullRowSelect = true;
lv.GridLines = true;
lv.AllowColumnReorder = true;
lv.Sorting = SortOrder.Ascending;
lv.Columns.AddRange(new ColumnHeader[] {name, year});
lv.ColumnClick += new ColumnClickEventHandler(ColumnClick);
foreach (Actress act in actresses) {
ListViewItem item = new ListViewItem();
item.Text = act.name;
item.SubItems.Add(act.year.ToString());
lv.Items.Add(item);
}
lv.Dock = DockStyle.Fill;
lv.Click += new EventHandler(OnChanged);
sb = new StatusBar();
sb.Parent = this;
lv.View = View.Details;
ResumeLayout();
CenterToScreen();
}
void OnChanged(object sender, EventArgs e) {
ListView lv = (ListView) sender;
string name = lv.SelectedItems[0].SubItems[0].Text;
string born = lv.SelectedItems[0].SubItems[1].Text;
sb.Text = name + ", " + born;
}
void ColumnClick(object sender, ColumnClickEventArgs e)
{
ListView lv = (ListView) sender;
if (lv.Sorting == SortOrder.Ascending) {
lv.Sorting = SortOrder.Descending;
} else {
lv.Sorting = SortOrder.Ascending;
}
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在我们的示例中,我们有一个包含两列的列表视图。 在第一列中,我们显示女演员的名字。 在第二个他们的出生日期。 数据存储在List集合中。 通过选择一行,一行中的数据将显示在状态栏中。 另外,通过单击列标题,可以对数据进行排序。
public class Actress
{
...
}
我们使用Actress类存储数据。
List<Actress> actresses = new List<Actress>();
actresses.Add(new Actress("Jessica Alba", 1981));
actresses.Add(new Actress("Angelina Jolie", 1975));
...
我们创建项目并在项目中填充项目。
ColumnHeader name = new ColumnHeader();
name.Text = "Name";
name.Width = -1;
对于列表视图中的每一列,我们创建一个ColumnHeader。 通过将Width设置为-1,列的宽度等于列中最长的项目。
ListView lv = new ListView();
lv.Parent = this;
ListView控件已创建。
lv.FullRowSelect = true;
lv.GridLines = true;
lv.AllowColumnReorder = true;
lv.Sorting = SortOrder.Ascending;
在这里,我们设置控件的四个属性。 该代码行支持全行选择,显示网格线,通过拖动列对列进行重新排序并以升序对数据进行排序。
lv.Columns.AddRange(new ColumnHeader[] {name, year});
在这里,我们将两个ColumnHeader添加到ListView控件中。
foreach (Actress act in actresses) {
ListViewItem item = new ListViewItem();
item.Text = act.name;
item.SubItems.Add(act.year.ToString());
lv.Items.Add(item);
}
此循环填充ListView控件。 每行都作为ListViewItem类添加到列表视图。
lv.View = View.Details;
ListView控件可以具有不同的视图。 不同的视图以不同的方式显示数据。
ListView lv = (ListView) sender;
string name = lv.SelectedItems[0].SubItems[0].Text;
string born = lv.SelectedItems[0].SubItems[1].Text;
sb.Text = name + ", " + born;
在OnChanged()方法内部,我们从选定的行中获取数据并将其显示在状态栏上。
if (lv.Sorting == SortOrder.Ascending) {
lv.Sorting = SortOrder.Descending;
} else {
lv.Sorting = SortOrder.Ascending;
}
在这里,我们切换列的排序顺序。

图:ListView
TreeView
TreeView控件显示项目的分层集合。 此控件中的每个项目都由TreeNode对象表示。
treeview.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
StatusBar sb;
public MForm() {
Text = "TreeView";
Size = new Size(250, 250);
TreeView tv = new TreeView();
TreeNode root = new TreeNode();
root.Text = "Languages";
TreeNode child1 = new TreeNode();
child1.Text = "Python";
TreeNode child2 = new TreeNode();
child2.Text = "Ruby";
TreeNode child3 = new TreeNode();
child3.Text = "Java";
root.Nodes.AddRange(new TreeNode[] {child1, child2, child3});
tv.Parent = this;
tv.Nodes.Add(root);
tv.Dock = DockStyle.Fill;
tv.AfterSelect += new TreeViewEventHandler(AfterSelect);
sb = new StatusBar();
sb.Parent = this;
CenterToScreen();
}
void AfterSelect(object sender, TreeViewEventArgs e)
{
sb.Text = e.Node.Text;
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
这是TreeView控件的非常简单的演示。 我们有一个根项目和三个子项。
TreeView tv = new TreeView();
我们创建TreeView控件。
TreeNode root = new TreeNode();
root.Text = "Languages";
...
tv.Nodes.Add(root);
在这里,我们创建一个根节点。
TreeNode child1 = new TreeNode();
child1.Text = "Python";
子节点以类似的方式创建。
root.Nodes.AddRange(new TreeNode[] {child1, child2, child3});
子节点插入到根节点的Nodes属性中。

图:TreeView
目录
下面的代码示例将更深入地研究TreeView控件。
directories.cs
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
public class MForm : Form
{
private TreeView tv;
private Button expand;
private Button expandAll;
private Button collapse;
private Button collapseAll;
private StatusBar sb;
private const string HOME_DIR = "/home/vronskij";
public MForm()
{
Size = new Size(400, 400);
Text = "Directories";
tv = new TreeView();
SuspendLayout();
tv.Parent = this;
tv.Location = new Point(10,10);
tv.Size = new Size(ClientSize.Width - 20, Height - 200);
tv.Anchor = AnchorStyles.Top | AnchorStyles.Left |
AnchorStyles.Right ;
tv.FullRowSelect = false;
tv.ShowLines = true;
tv.ShowPlusMinus = true;
tv.Scrollable = true;
tv.AfterSelect += new TreeViewEventHandler(AfterSelect);
expand = new Button();
expand.Parent = this;
expand.Location = new Point(20, tv.Bottom + 20);
expand.Text = "Expand";
expand.Anchor = AnchorStyles.Left | AnchorStyles.Top;
expand.Click += new EventHandler(OnExpand);
expandAll = new Button();
expandAll.Parent = this;
expandAll.Location = new Point(20, expand.Bottom + 5);
expandAll.Text = "Expand All";
expandAll.Anchor = AnchorStyles.Left | AnchorStyles.Top;
expandAll.Click += new EventHandler(OnExpandAll);
collapse = new Button();
collapse.Parent = this;
collapse.Location = new Point(expandAll.Right + 5, expand.Top );
collapse.Text = "Collapse";
collapse.Anchor = AnchorStyles.Left | AnchorStyles.Top;
collapse.Click += new EventHandler(OnCollapse);
collapseAll = new Button();
collapseAll.Parent = this;
collapseAll.Location = new Point(collapse.Left, collapse.Bottom + 5);
collapseAll.Text = "Collapse All";
collapseAll.Anchor = AnchorStyles.Left | AnchorStyles.Top;
collapseAll.Click += new EventHandler(OnCollapseAll);
sb = new StatusBar();
sb.Parent = this;
ShowDirectories(tv.Nodes, HOME_DIR);
ResumeLayout();
CenterToScreen();
}
void AfterSelect(object sender, TreeViewEventArgs e)
{
sb.Text = e.Node.Text;
}
void ShowDirectories(TreeNodeCollection trvNode, string path)
{
DirectoryInfo dirInfo = new DirectoryInfo(path);
if (dirInfo != null)
{
DirectoryInfo[] subDirs = dirInfo.GetDirectories();
TreeNode tr = new TreeNode(dirInfo.Name);
if (subDirs.Length > 0)
{
foreach (DirectoryInfo dr in subDirs)
{
if (!dr.Name.StartsWith("."))
ShowDirectories(tr.Nodes, dr.FullName);
}
}
trvNode.Add(tr);
}
}
void OnExpand(object sender, EventArgs e)
{
tv.SelectedNode.Expand();
}
void OnExpandAll(object sender, EventArgs e)
{
tv.ExpandAll();
}
void OnCollapse(object sender, EventArgs e)
{
tv.SelectedNode.Collapse();
}
void OnCollapseAll(object sender, EventArgs e)
{
tv.CollapseAll();
}
static void Main()
{
Application.Run(new MForm());
}
}
我们的代码示例在TreeView控件中显示指定主目录的目录。 该应用启动有些延迟,因为它首先读取主目录的目录结构。 表单上还有四个按钮。 这些按钮以编程方式展开和折叠节点。
tv.Scrollable = true;
我们使TreeView控件可滚动,因为该控件显示了大量目录。
ShowDirectories(tv.Nodes, HOME_DIR);
ShowDirectories()方法使用指定主目录中可用的目录填充TreeView控件的节点。
if (subDirs.Length > 0)
{
...
}
我们检查是否有任何子目录。
foreach (DirectoryInfo dr in subDirs)
{
if (!dr.Name.StartsWith("."))
ShowDirectories(tr.Nodes, dr.FullName);
}
我们遍历所有目录。 为此,我们使用了递归算法。 我们还跳过隐藏的目录。 它们以 Unix 系统上的点开头。
trvNode.Add(tr);
此代码行实际上将目录添加到TreeView控件。
void OnExpand(object sender, EventArgs e)
{
tv.SelectedNode.Expand();
}
所有四个按钮都将事件插入到方法中。 这是展开按钮的方法。 它调用当前所选节点的Expand()方法。

图:Directories
在 Mono Winforms 教程的这一部分中,我们介绍了 Winforms 库中可用的几个高级控件。
PyQt5 简介
这是 PyQt5 入门教程。 本教程的目的是帮助您开始使用 PyQt5 工具包。 该教程已在 Linux 上创建并测试。 PyQt4 教程涵盖了 PyQt4,它是 Python 语言(2.x 和 3.x)与 Qt4 库的融合。
关于 PyQt5
PyQt5 是 Digia 的一组 Qt5 应用框架的 Python 绑定。 它适用于 Python 2.x 和 3.x。 本教程使用 Python3。Qt 库是功能最强大的 GUI 库之一。 PyQt5 的官方主页是 www.riverbankcomputing.co.uk/news 。 PyQt5 由 Riverbank Computing 开发。
PyQt5 被实现为一组 Python 模块。 它具有 620 多个类和 6000 种功能和方法。 它是一个多平台工具包,可在所有主要操作系统(包括 Unix,Windows 和 Mac OS)上运行。 PyQt5 是双重许可的。 开发者可以在 GPL 和商业许可之间进行选择。
PyQt5 安装
$ sudo pip3 install pyqt5
我们可以使用pip3工具安装 PyQt5。
PyQt5 模块
PyQt5 的类分为几个模块,包括以下模块:
QtCoreQtGuiQtWidgetsQtMultimediaQtBluetoothQtNetworkQtPositioning- Enginio
QtWebSocketsQtWebKitQtWebKitWidgetsQtXmlQtSvgQtSqlQtTest
QtCore模块包含核心的非 GUI 功能。 该模块用于处理时间,文件和目录,各种数据类型,流,URL,mime 类型,线程或进程。 QtGui包含用于窗口系统集成,事件处理,2D 图形,基本图像,字体和文本的类。 QtWidgets模块包含的类提供了一组 UI 元素,以创建经典的桌面样式用户界面。 QtMultimedia包含用于处理多媒体内容的类和用于访问相机和收音机功能的 API。
QtBluetooth模块包含用于扫描设备以及与它们连接和交互的类。 QtNetwork模块包含用于网络编程的类。 这些类通过使网络编程更加容易和可移植性,来简化 TCP/IP 和 UDP 客户端和服务器的编码。 QtPositioning包含用于通过使用各种可能的来源(包括卫星,Wi-Fi 或文本文件)确定位置的类。 Enginio模块实现了用于访问 Qt 云服务托管应用运行时的客户端库。 QtWebSockets模块包含实现 WebSocket 协议的类。 QtWebKit包含基于 WebKit2 库的 Web 浏览器实现的类。 QtWebKitWidgets包含用于基于 WebKit1 的 Web 浏览器实现的类,以用于基于QtWidgets的应用。
QtXml包含用于处理 XML 文件的类。 该模块提供了 SAX 和 DOM API 的实现。 QtSvg模块提供了用于显示 SVG 文件内容的类。 可伸缩向量图形(SVG)是一种用于描述 XML 中的二维图形和图形应用的语言。 QtSql模块提供用于处理数据库的类。 QtTest包含可对 PyQt5 应用进行单元测试的功能。
PyQt4 和 PyQt5 的区别
PyQt5 与 PyQt4 向后不兼容。 PyQt5 有几个重大变化。 但是,将旧代码调整为新库并不是很困难。 除其他外,差异如下:
- Python 模块已重新组织。 一些模块已删除(
QtScript),其他模块已拆分为子模块(QtGui,QtWebKit)。 - 引入了新模块,包括
QtBluetooth,QtPositioning或Enginio。 - PyQt5 仅支持新型信号和槽处理器。 不再支持对
SIGNAL()或SLOT()的调用。 - PyQt5 不支持 Qt v5.0 中标记为已弃用或过时的 Qt API 的任何部分。
Python
Python 是一种通用的,动态的,面向对象的编程语言。 Python 语言的设计目的强调程序员的生产力和代码可读性。 Python 最初是由 Guido van Rossum 开发的。 它于 1991 年首次发布。Python 受 ABC,Haskell,Java,Lisp,Icon 和 Perl 编程语言的启发。 Python 是一种高级通用通用解释型语言。 Python 是一种简约语言。 它最明显的功能之一是它不使用分号或方括号。 它改用缩进。 目前,Python 有两个主要分支:Python 2.x 和 Python3.x。 Python 3.x 打破了与早期版本 Python 的向后兼容性。 它的创建是为了纠正该语言的某些设计缺陷并使该语言更简洁。 Python 由世界各地的一大批志愿者维护。 Python 是开源软件。 对于那些想学习编程的人来说,Python 是一个理想的起点。
本教程使用 Python 3.x 版本。
Python 编程语言支持多种编程样式。 它不会强迫程序员采用特定的示例。 Python 支持面向对象和过程编程。 对函数式编程的支持也很有限。
Python 编程语言的官方网站是 python.org
Perl,Python 和 Ruby 是广泛使用的脚本语言。 它们具有许多相似之处,并且是紧密的竞争对手。
本章是对 PyQt5 工具包的介绍。
对话框
在 Mono Winforms 教程的这一部分中,我们将讨论对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
基本上有两种类型的对话框。 预定义对话框和自定义对话框。
FolderBrowserDialog
此对话框提示用户选择一个文件夹。
folderbrowserdialog.cs
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private ToolBar toolbar;
private ToolBarButton open;
private StatusBar statusbar;
public MForm() {
Text = "FolderBrowserDialog";
toolbar = new ToolBar();
open = new ToolBarButton();
statusbar = new StatusBar();
statusbar.Parent = this;
toolbar.Buttons.Add(open);
toolbar.ButtonClick += new ToolBarButtonClickEventHandler(OnClicked);
Controls.Add(toolbar);
CenterToScreen();
}
void OnClicked(object sender, ToolBarButtonClickEventArgs e) {
FolderBrowserDialog dialog = new FolderBrowserDialog();
if (dialog.ShowDialog(this) == DialogResult.OK) {
statusbar.Text = dialog.SelectedPath;
}
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们有一个工具栏和一个工具栏按钮。 点击按钮,FolderBrowserDialog出现在屏幕上。 所选文件夹的名称显示在状态栏中。
FolderBrowserDialog dialog = new FolderBrowserDialog();
FolderBrowserDialog已创建。
if (dialog.ShowDialog(this) == DialogResult.OK) {
statusbar.Text = dialog.SelectedPath;
}
ShowDialog()方法在屏幕上显示对话框。 如果单击对话框的“确定”按钮,则所选的目录路径将显示在状态栏上。

图:FolderBrowserDialog
ColorDialog
此对话框显示可用的颜色以及使用户能够定义自定义颜色的控件。
colordialog.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private ToolBar toolbar;
private ToolBarButton open;
private Color color;
private int rectWidth = 100;
private int rectHeight = 100;
private Rectangle r;
public MForm() {
Text = "ColorDialog";
color = Color.Blue;
toolbar = new ToolBar();
open = new ToolBarButton();
toolbar.Buttons.Add(open);
toolbar.ButtonClick += new ToolBarButtonClickEventHandler(OnClicked);
LocateRect();
SetStyle (ControlStyles.ResizeRedraw, true);
Controls.Add(toolbar);
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
LocateRect();
SolidBrush brush = new SolidBrush(color);
g.FillRectangle(brush, r);
}
void OnClicked(object sender, ToolBarButtonClickEventArgs e) {
ColorDialog dialog = new ColorDialog();
if (dialog.ShowDialog(this) == DialogResult.OK) {
color = dialog.Color;
Invalidate();
}
}
void LocateRect() {
int x = (ClientSize.Width - rectWidth) / 2;
int y = (ClientSize.Height - rectHeight) / 2;
r = new Rectangle(x, y, rectWidth, rectHeight);
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在此代码示例中,我们使用ColorDialog为位于窗体控件中间的矩形选择颜色。
color = Color.Blue;
开始时,矩形的颜色是蓝色。 我们使用color变量来确定矩形的颜色。
ColorDialog dialog = new ColorDialog();
ColorDialog已创建。
if (dialog.ShowDialog(this) == DialogResult.OK) {
color = dialog.Color;
Invalidate();
}
该代码显示颜色对话框。 如果单击“确定”按钮,则将获得选定的颜色并调用Invalidate()方法。 该方法会使控件的整个表面无效,并使控件重画。 结果是用新的颜色值绘制了矩形。

图:ColorDialog
FontDialog
FontDialog用于选择字体。
fontdialog.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private Label text;
private ToolBar toolbar;
private ToolBarButton open;
public MForm() {
Text = "FontDialog";
text = new Label();
text.Parent = this;
text.Text = "Winforms tutorial";
LocateText();
toolbar = new ToolBar();
toolbar.Parent = this;
open = new ToolBarButton();
toolbar.Buttons.Add(open);
toolbar.ButtonClick += new ToolBarButtonClickEventHandler(OnClicked);
text.AutoSize = true;
Resize += new EventHandler(OnResize);
CenterToScreen();
}
void OnResize(object sender, EventArgs e){
LocateText();
}
void OnClicked(object sender, ToolBarButtonClickEventArgs e) {
FontDialog dialog = new FontDialog();
if (dialog.ShowDialog(this) == DialogResult.OK) {
text.Font = dialog.Font;
LocateText();
}
}
void LocateText() {
text.Top = (this.ClientSize.Height - text.Height) / 2;
text.Left = (this.ClientSize.Width - text.Width) / 2;
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们在表单控件的中间绘制一些文本。 我们使用字体对话框更改此文本的字体。
FontDialog dialog = new FontDialog();
创建了FontDialog。
if (dialog.ShowDialog(this) == DialogResult.OK) {
text.Font = dialog.Font;
LocateText();
}
单击“确定”按钮时,将为Label控件设置新选择的字体。 由于文本的大小会随着字体的变化而变化,因此我们必须调用LocateText()方法,该方法将文本定位在表单控件的中间。

图:FontDialog
OpenDialog
此对话框用于打开文件。
opendialog.cs
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private ToolBar toolbar;
private ToolBarButton open;
private TextBox textbox;
public MForm() {
Text = "OpenFileDialog";
toolbar = new ToolBar();
open = new ToolBarButton();
textbox = new TextBox();
textbox.Multiline = true;
textbox.ScrollBars = ScrollBars.Both;
textbox.WordWrap = false;
textbox.Parent = this;
textbox.Dock = DockStyle.Fill;
toolbar.Buttons.Add(open);
toolbar.ButtonClick += new ToolBarButtonClickEventHandler(OnClicked);
Controls.Add(toolbar);
Controls.Add(textbox);
CenterToScreen();
}
void OnClicked(object sender, ToolBarButtonClickEventArgs e) {
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "C# files (*.cs)|*.cs";
if (dialog.ShowDialog(this) == DialogResult.OK) {
StreamReader reader = new StreamReader(dialog.FileName);
string data = reader.ReadToEnd();
reader.Close();
textbox.Text = data;
}
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们使用OpenDialog控件打开 C# 源文件。 我们有一个TextBox控件,用于显示文件。
OpenFileDialog dialog = new OpenFileDialog();
OpenDialog已创建。
dialog.Filter = "C# files (*.cs)|*.cs";
我们将Filter属性设置为 C# 源文件。 此对话框实例只能选择 C# 文件。
if (dialog.ShowDialog(this) == DialogResult.OK) {
StreamReader reader = new StreamReader(dialog.FileName);
string data = reader.ReadToEnd();
reader.Close();
textbox.Text = data;
}
单击确定后,我们读取所选文件的内容并将其放入TextBox控件。

图:OpenDialog
在 Mono Winforms 教程的这一部分中,我们显示了各种对话框。
Mono Winforms 中的拖放
Mono Winforms 教程的这一部分将专门用于拖放操作。
在计算机图形用户界面中,拖放是单击虚拟对象并将其拖动到其他位置或另一个虚拟对象上的动作(或支持以下动作)。 通常,它可用于调用多种动作,或在两个抽象对象之间创建各种类型的关联。 (维基百科)
拖放功能是图形用户界面最明显的方面之一。 拖放操作使您可以直观地完成复杂的事情。
拖动按钮
在第一个示例中,我们将在按钮控件上执行拖放操作。 该示例在拖放&放置协议之外执行作业。
dragbutton.cs
using System;
using System.Drawing;
using System.Windows.Forms;
public class MForm : Form
{
private bool isDragging = false;
private int oldX, oldY;
private Button button;
public MForm()
{
Text = "Drag & drop button";
Size = new Size(270, 180);
button = new Button();
button.Parent = this;
button.Cursor = Cursors.Hand;
button.Text = "Button";
button.Location = new Point(20, 20);
button.MouseDown += new MouseEventHandler(OnMouseDown);
button.MouseUp += new MouseEventHandler(OnMouseUp);
button.MouseMove += new MouseEventHandler(OnMouseMove);
CenterToScreen();
}
public static void Main()
{
Application.Run(new MForm());
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
isDragging = true;
oldX = e.X;
oldY = e.Y;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
button.Top = button.Top + (e.Y - oldY);
button.Left = button.Left + (e.X - oldX);
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
isDragging = false;
}
}
该代码示例将一个常规按钮控件放在表单容器上。 通过单击按钮表面并同时用鼠标拖动它,我们可以重新放置按钮。
private bool isDragging = false;
private int oldX, oldY;
这些是我们示例的支持变量。 isDragging变量告诉我们是否正在拖动对象。 oldX和oldY变量在拖动过程开始之前存储 x,y 坐标。
button.MouseDown += new MouseEventHandler(OnMouseDown);
button.MouseUp += new MouseEventHandler(OnMouseUp);
button.MouseMove += new MouseEventHandler(OnMouseMove);
我们为按钮插入了三种不同的鼠标处理器。 它们实现了拖放过程的三个不同阶段。 当我们单击按钮时,过程开始。 这由OnMouseDown()方法处理。 第二部分是机芯。 这是当我们将对象移动到新位置时。 它以OnMouseMove()方法处理。 最后一部分是过程停止的时间。 当我们释放鼠标按钮时会发生这种情况。 适当的任务委托给OnMouseUp()方法。
private void OnMouseDown(object sender, MouseEventArgs e)
{
isDragging = true;
oldX = e.X;
oldY = e.Y;
}
OnMouseDown()方法实现了过程的第一部分。 它设置了三个必要的变量。
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
button.Top = button.Top + (e.Y - oldY);
button.Left = button.Left + (e.X - oldX);
}
}
在OnMouseMove()方法中,我们重新定位按钮。 我们计算存储的 x,y 坐标与鼠标指针的新坐标之间的差。 差异将添加到按钮的Top和Left属性中,从而将其移动到新位置。

图:拖动按钮
拖动文字
在前面的示例中,我们确实拖放了控件。 接下来,我们将对文本数据进行拖放操作。 在这里,我们将使用 Winforms 库提供的拖放协议。
拖放操作是 Winforms 中的标准通信协议。 我们有两个基本对象。 拖动源和放置目标。
dragtext.cs
using System;
using System.Drawing;
using System.Windows.Forms;
public class MForm : Form
{
private TextBox textBox;
private Button button;
public MForm()
{
InitForm();
CenterToScreen();
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
TextBox txt = (TextBox) sender;
txt.DoDragDrop(txt.Text, DragDropEffects.Copy);
}
private void OnDragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void OnDragDrop(object sender, DragEventArgs e)
{
Button button = (Button) sender;
button.Text = (string) e.Data.GetData(DataFormats.Text);
}
private void InitForm()
{
Text = "Drag & drop";
button = new Button();
textBox = new TextBox();
SuspendLayout();
button.AllowDrop = true;
button.Location = new Point(150, 50);
textBox.Location = new Point(15, 50);
button.DragDrop += new DragEventHandler(OnDragDrop);
button.DragEnter += new DragEventHandler(OnDragEnter);
textBox.MouseDown += new MouseEventHandler(OnMouseDown);
ClientSize = new Size(250, 200);
Controls.Add(button);
Controls.Add(textBox);
ResumeLayout();
}
public static void Main(string[] args)
{
Application.Run(new MForm());
}
}
我们在表单上有两个控件。 一个按钮和一个文本框。 我们将文本从文本框中拖放到按钮上。
InitForm();
表单的设置委托给InitForm()方法。 这通常在较大的应用中完成。
SuspendLayout();
...
ResumeLayout();
我们在这两个方法调用之间布置控件。 这是一个优化。 它应该消除闪烁。
button.AllowDrop = true;
我们将AllowDrop属性设置为true。 默认情况下不启用删除。
button.DragDrop += new DragEventHandler(OnDragDrop);
button.DragEnter += new DragEventHandler(OnDragEnter);
textBox.MouseDown += new MouseEventHandler(OnMouseDown);
同样,拖放过程分为三个步骤。 对于每个特定步骤,我们有三种方法。
private void OnMouseDown(object sender, MouseEventArgs e)
{
TextBox txt = (TextBox) sender;
txt.DoDragDrop(txt.Text, DragDropEffects.Copy);
}
在OnMouseDown()方法中,我们初始化了拖放过程。 我们使用DoDragDrop()方法启动该过程。 DragDropEffects.Copy参数指定操作的类型。 实质上,我们可以在拖放操作期间复制文本或移动文本。
private void OnDragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
当鼠标指针进入放置目标控件的区域时,将启动DragEnter事件。 必须设置Effect属性。 拖动源和放置目标的DragDropEffects必须相等。 否则,该操作将无法进行。
private void OnDragDrop(object sender, DragEventArgs e)
{
Button button = (Button) sender;
button.Text = (string) e.Data.GetData(DataFormats.Text);
}
最后,我们有OnDragDrop()方法。 在这里,我们从事件对象获取数据并将其设置为按钮Text属性。

图:文本拖放
拖动图像
在最后一个示例中,我们将拖放图像拖到窗体上。
dragimage.cs
using System;
using System.Drawing;
using System.Windows.Forms;
public class ImageDragDrop : Form
{
private bool isDragging;
private int oldX, oldY;
private Rectangle dropRect;
private PictureBox picBox;
private Bitmap image;
private Brush brush;
public ImageDragDrop()
{
ClientSize = new Size(350, 250);
Text = "Dragging Image";
Paint += new PaintEventHandler(OnPaint);
isDragging = false;
dropRect = new Rectangle(10, 10, 200, 160);
brush = Brushes.Gray;
picBox = new PictureBox();
loadImage();
picBox.Parent = this;
picBox.Location = new Point(100, 50);
picBox.Size = new Size(image.Width, image.Height);
picBox.Image = image;
picBox.Cursor = Cursors.Hand;
picBox.MouseDown += new MouseEventHandler(OnMouseDown);
picBox.MouseUp += new MouseEventHandler(OnMouseUp);
picBox.MouseMove += new MouseEventHandler(OnMouseMove);
CenterToScreen();
}
void loadImage() {
try {
image = new Bitmap("image.jpg");
} catch {
Console.WriteLine("Error reading image");
Environment.Exit(1);
}
}
public static void Main()
{
Application.Run(new ImageDragDrop());
}
private void OnMouseDown(object sender, MouseEventArgs e)
{
isDragging = true;
oldX = e.X;
oldY = e.Y;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
picBox.Top = picBox.Top + (e.Y - oldY);
picBox.Left = picBox.Left + (e.X - oldX);
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
isDragging = false;
if(dropRect.Contains(picBox.Bounds)) {
brush = Brushes.Gold;
} else {
brush = Brushes.Gray;
}
Refresh();
}
private void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(brush, dropRect);
}
}
在我们的示例中,我们有一个PictureBox,并绘制了一个灰色矩形。 如果将图片放在矩形内,则矩形的颜色变为金色。
brush = Brushes.Gray;
brush变量保存矩形的笔刷。 默认情况下为灰色。
void loadImage() {
try {
image = new Bitmap("image.jpg");
} catch {
Console.WriteLine("Error reading image");
Environment.Exit(1);
}
}
loadImage()加载PictureBox控件的位图。
if (dropRect.Contains(picBox.Bounds)) {
brush = Brushes.Gold;
} else {
brush = Brushes.Gray;
}
在OnMouseUp()方法中,我们确定矩形的笔刷。 如果图片框的边界在矩形内,则画笔为金色;否则,画笔为金色。 否则为灰色。
Refresh();
我们必须调用Refresh()方法来激活新的画笔颜色。
本章专门使用 Mono Winforms 库拖放操作。
Mono Winforms 中的绘图
在 Mono Winforms 教程的这一部分中,我们将进行绘图。 当我们想要更改或增强现有控件时,将使用绘图。 或者,如果我们要从头开始创建自定义控件。 要进行绘图,我们使用 Winforms 库提供的绘图 API。 绘图是在一种方法中完成的,我们将其插入Paint事件。
System.Drawing名称空间提供对GDI+基本图形功能的访问。 System.Drawing.Drawing2D,System.Drawing.Imaging和System.Drawing.Text命名空间中提供了更高级的功能。 Graphics类提供了在表单上绘图的方法。
直线
我们的第一个示例将在Form控件上绘制线条。
lines.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Lines";
Size = new Size(280, 270);
ResizeRedraw = true;
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen pen = new Pen(Color.Black, 1);
pen.DashStyle = DashStyle.Dot;
g.DrawLine(pen, 20, 40, 250, 40);
pen.DashStyle = DashStyle.DashDot;
g.DrawLine(pen, 20, 80, 250, 80);
pen.DashStyle = DashStyle.Dash;
g.DrawLine(pen, 20, 120, 250, 120);
pen.DashStyle = DashStyle.DashDotDot;
g.DrawLine(pen, 20, 160, 250, 160);
pen.DashPattern = new float[] {6f, 8f, 1f, 1f, 1f, 1f, 1f, 1f };
g.DrawLine(pen, 20, 200, 250, 200);
g.Dispose();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们在表格上画了五行。 每行具有不同的DashStyle。
ResizeRedraw = true;
调整表单大小时,它会自动重绘。 这不是默认行为。
Paint += new PaintEventHandler(OnPaint);
绘图事件将传递给OnPaint()方法。
void OnPaint(object sender, PaintEventArgs e)
{
...
}
这是OnPaint()方法的签名。
Graphics g = e.Graphics;
为了在表单上绘图,我们必须获取Graphics对象。 在窗体上绘图实际上是在调用Graphics对象的各种方法。
Pen pen = new Pen(Color.Black, 1);
pen.DashStyle = DashStyle.Dot;
g.DrawLine(pen, 20, 40, 250, 40);
我们创建一个Pen对象。 该对象用于绘制形状的轮廓。 比我们设置点划线DashStyle。 最后,我们用DrawLine()方法画线。 第一个参数是钢笔对象。 接下来的四个值是线的起点和终点的 x 和 y 值。
pen.DashPattern = new float[] {6f, 8f, 1f, 1f, 1f, 1f, 1f, 1f };
有几个内置的DashStyle值。 我们可以使用DashPattern属性来创建自己的样式。 乍一看可能很难。 但是模式只是填充和空值的数组。
g.Dispose();
我们知道 C# 语言使用垃圾回收。 那么,为什么我们要明确释放资源? 这是为了提高效率。 我们正在帮助垃圾收集器。

图:直线
色彩
Winforms 库中的颜色表示 ARGB(alpha,红色,绿色,蓝色)颜色。 它是 Alpha,红色,绿色和蓝色(RGB)强度值的组合。 还有一些可以在绘图中使用的预定义颜色名称。
colors.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Colors";
Size = new Size(360, 300);
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(Brushes.Sienna, 10, 15, 90, 60);
g.FillRectangle(Brushes.Green, 130, 15, 90, 60);
g.FillRectangle(Brushes.Maroon, 250, 15, 90, 60);
g.FillRectangle(Brushes.Chocolate, 10, 105, 90, 60);
g.FillRectangle(Brushes.Gray, 130, 105, 90, 60);
g.FillRectangle(Brushes.Coral, 250, 105, 90, 60);
g.FillRectangle(Brushes.Brown, 10, 195, 90, 60);
g.FillRectangle(Brushes.Teal, 130, 195, 90, 60);
g.FillRectangle(Brushes.Goldenrod, 250, 195, 90, 60);
g.Dispose();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们用 9 种不同的颜色绘制 9 个矩形。
g.FillRectangle(Brushes.Sienna, 10, 15, 90, 60);
FillRectagle()方法用画笔填充指定的矩形。 画笔可以是颜色或图案。 有一些预定义的颜色可用。 我们可以从Brushes枚举中获取它们。 最后四个值是左上角点的 x,y 值以及矩形的宽度和高度。

图:颜色
HatchBrush
HatchBrush对象用于填充形状的内部。 我们可以使用几种内置模式。
hatches.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Hatches";
Size = new Size(360, 300);
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
HatchBrush hb = new HatchBrush(HatchStyle.Cross, Color.Black, this.BackColor);
g.FillRectangle(hb, 10, 15, 90, 60);
hb = new HatchBrush(HatchStyle.Percent05, Color.Black, this.BackColor);
g.FillRectangle(hb, 130, 15, 90, 60);
hb = new HatchBrush(HatchStyle.SolidDiamond, Color.Black, this.BackColor);
g.FillRectangle(hb, 250, 15, 90, 60);
hb = new HatchBrush(HatchStyle.DiagonalBrick, Color.Black, this.BackColor);
g.FillRectangle(hb, 10, 105, 90, 60);
hb = new HatchBrush(HatchStyle.Divot, Color.Black, this.BackColor);
g.FillRectangle(hb, 130, 105, 90, 60);
hb = new HatchBrush(HatchStyle.Wave, Color.Black, this.BackColor);
g.FillRectangle(hb, 250, 105, 90, 60);
hb = new HatchBrush(HatchStyle.ZigZag, Color.Black, this.BackColor);
g.FillRectangle(hb, 10, 195, 90, 60);
hb = new HatchBrush(HatchStyle.Sphere, Color.Black, this.BackColor);
g.FillRectangle(hb, 130, 195, 90, 60);
hb = new HatchBrush(HatchStyle.Shingle, Color.Black, this.BackColor);
g.FillRectangle(hb, 250, 195, 90, 60);
hb.Dispose();
g.Dispose();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
这次,我们用九种不同的图案(称为剖面线)填充了九个矩形。
HatchBrush hb = new HatchBrush(HatchStyle.Cross, Color.Black, this.BackColor);
在这里,我们创建一个HatchBrush对象。 参数是图案填充样式以及前景色和背景色。 背景颜色设置为表单的颜色,因此看起来就像我们在表单上绘制的一样。
g.FillRectangle(hb, 10, 15, 90, 60);
我们使用指定的阴影刷填充矩形。

图:通口
渐变
在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers.com)
gradients.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "Gradients";
Size = new Size(350, 350);
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point pt1 = new Point(5, 5);
Point pt2 = new Point(25, 25);
Brush lg = new LinearGradientBrush(pt1, pt2, Color.Red, Color.Black);
g.FillRectangle(lg, 20, 20, 300, 40);
pt1 = new Point(5, 25);
pt2 = new Point(20, 2);
lg = new LinearGradientBrush(pt1, pt2, Color.Yellow, Color.Black);
g.FillRectangle(lg, 20, 80, 300, 40);
pt1 = new Point(5, 25);
pt2 = new Point(2, 2);
lg = new LinearGradientBrush(pt1, pt2, Color.Green, Color.Black);
g.FillRectangle(lg, 20, 140, 300, 40);
pt1 = new Point(25, 25);
pt2 = new Point(15, 25);
lg = new LinearGradientBrush(pt1, pt2, Color.Blue, Color.Black);
g.FillRectangle(lg, 20, 200, 300, 40);
pt1 = new Point(0, 10);
pt2 = new Point(0, 20);
lg = new LinearGradientBrush(pt1, pt2, Color.Orange, Color.Black);
g.FillRectangle(lg, 20, 260, 300, 40);
lg.Dispose();
g.Dispose();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
我们绘制五个矩形,这些矩形填充有不同的线性渐变。
Point pt1 = new Point(5, 5);
Point pt2 = new Point(25, 25);
这两个是线性渐变画笔的控制点。
Brush lg = new LinearGradientBrush(pt1, pt2, Color.Red, Color.Black);
我们创建LinearGradientBrush对象。 我们使用两个控制点和两种混合颜色。

图:渐变
画线
要在 Winforms Form上绘制字符串,我们使用DrawString()方法。
lyrics.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
class MForm : Form {
public MForm() {
Text = "You know I'm No Good";
Size = new Size(380, 450);
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Font ft = new Font("Purisa", 10);
SolidBrush br = new SolidBrush(Color.Black);
PointF pt = new PointF(20.0f, 20.0f);
g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt);
pt = new PointF(20.0f, 50.0f);
g.DrawString("Your rolled up sleeves and your skull t-shirt", ft, br, pt);
pt = new PointF(20.0f, 80.0f);
g.DrawString("You say why did you do it with him today?", ft, br, pt);
pt = new PointF(20.0f, 110.0f);
g.DrawString("And sniffed me out like I was tanqueray", ft, br, pt);
pt = new PointF(20.0f, 160.0f);
g.DrawString("Cause you’re my fella, my guy", ft, br, pt);
pt = new PointF(20.0f, 190.0f);
g.DrawString("Hand me your stella and fly", ft, br, pt);
pt = new PointF(20.0f, 220.0f);
g.DrawString("By the time I’m out the door", ft, br, pt);
pt = new PointF(20.0f, 250.0f);
g.DrawString("You tear me down like roger moore", ft, br, pt);
pt = new PointF(20.0f, 300.0f);
g.DrawString("I cheated myself", ft, br, pt);
pt = new PointF(20.0f, 330.0f);
g.DrawString("Like I knew I would", ft, br, pt);
pt = new PointF(20.0f, 360.0f);
g.DrawString("I told ya, I was trouble", ft, br, pt);
pt = new PointF(20.0f, 390.0f);
g.DrawString("You know that I’m no good", ft, br, pt);
g.Dispose();
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
在我们的示例中,我们在 Winforms 窗体上绘制歌曲的歌词。
Font ft = new Font("Purisa", 10);
我们使用 10 磅高的 Purisa 字体。
PointF pt = new PointF(20.0f, 20.0f);
要在表单上绘制字符串,我们必须使用浮点值。
g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt);
DrawString()方法采用以下参数:要绘制的文本,字体,笔刷和PointF对象。

图:歌词
绘制图像
在最后一个示例中,我们将在Form控件上绘制图像。
redrock.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class MForm : Form {
private Bitmap castle;
public MForm() {
Text = "Red Rock";
loadImage();
ClientSize = new Size(castle.Width, castle.Height);
Paint += new PaintEventHandler(OnPaint);
CenterToScreen();
}
void loadImage() {
try {
castle = new Bitmap("redrock.png");
} catch (Exception e) {
Console.WriteLine(e.Message);
Environment.Exit(1);
}
}
void OnPaint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = new Rectangle(1, 1, castle.Width, castle.Height);
g.DrawImage(castle, r);
}
}
class MApplication {
public static void Main() {
Application.Run(new MForm());
}
}
此代码示例在窗体上绘制城堡的图像。
try {
castle = new Bitmap("redrock.png");
} catch (Exception e) {
Console.WriteLine(e.Message);
Environment.Exit(1);
}
我们加载城堡的图像。
Rectangle r = new Rectangle(1, 1, castle.Width, castle.Height);
我们确定将要绘制的矩形。
g.DrawImage(castle, r);
这条线实际上绘制图像。

图:图像
在本章中,我们在 Mono Winforms 库中做了一些绘图。
Mono Winforms 中的贪食蛇
在 Mono Winforms 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇游戏
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。 该游戏有时称为 Nibbles 。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 通过按下光标键之一开始游戏。 如果游戏结束,我们将在棋盘中间显示"Game Over"消息。
Board.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
public class Board : UserControl {
private const int WIDTH = 300;
private const int HEIGHT = 300;
private const int DOT_SIZE = 10;
private const int ALL_DOTS = 900;
private const int RAND_POS = 27;
private int[] x = new int[ALL_DOTS];
private int[] y = new int[ALL_DOTS];
private int dots;
private int apple_x;
private int apple_y;
private bool left = false;
private bool right = true;
private bool up = false;
private bool down = false;
private bool inGame = true;
private Timer timer;
private Bitmap dot;
private Bitmap apple;
private Bitmap head;
private IContainer components;
public int BORDER_WIDTH;
public int TITLEBAR_HEIGHT;
public Board() {
components = new Container();
BackColor = Color.Black;
DoubleBuffered = true;
this.ClientSize = new Size(WIDTH, HEIGHT);
try {
dot = new Bitmap("dot.png");
apple = new Bitmap("apple.png");
head = new Bitmap("head.png");
} catch (Exception e) {
Console.WriteLine(e.Message);
Environment.Exit(1);
}
initGame();
}
private void OnTick(object sender, EventArgs e) {
if (inGame) {
checkApple();
checkCollision();
move();
}
this.Refresh();
}
private void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z * 10;
y[z] = 50;
}
locateApple();
KeyUp += new KeyEventHandler(OnKeyUp);
timer = new Timer(this.components);
timer.Enabled = true;
timer.Interval = 100;
timer.Tick += new System.EventHandler(this.OnTick);
Paint += new PaintEventHandler(this.OnPaint);
}
private void OnPaint(object sender, PaintEventArgs e) {
Graphics g = e.Graphics;
if (inGame) {
g.DrawImage(apple, apple_x, apple_y);
for (int z = 0; z < dots; z++) {
if (z == 0) {
g.DrawImage(head, x[z], y[z]);
} else {
g.DrawImage(dot, x[z], y[z]);
}
}
} else {
gameOver(g);
}
}
private void gameOver(Graphics g) {
String msg = "Game Over";
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
g.DrawString(msg, Font, Brushes.White, ClientRectangle, format);
timer.Stop();
}
private void checkApple() {
if ((x[0] == apple_x) && (y[0] == apple_y)) {
dots++;
locateApple();
}
}
private void move() {
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
if (left) {
x[0] -= DOT_SIZE;
}
if (right) {
x[0] += DOT_SIZE;
}
if (up) {
y[0] -= DOT_SIZE;
}
if (down) {
y[0] += DOT_SIZE;
}
}
private void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
if (y[0] > HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT - BORDER_WIDTH) {
inGame = false;
}
if (y[0] < 0) {
inGame = false;
}
if (x[0] > WIDTH - DOT_SIZE - 2 * BORDER_WIDTH) {
inGame = false;
}
if (x[0] < 0) {
inGame = false;
}
}
private void locateApple() {
Random rand = new Random();
int r = (int)(rand.Next(RAND_POS));
apple_x = ((r * DOT_SIZE));
r = (int)(rand.Next(RAND_POS));
apple_y = ((r * DOT_SIZE));
}
private void OnKeyUp(object sender, KeyEventArgs e) {
int key = (int) e.KeyCode;
if ((key == (int) Keys.Left) && (!right)) {
left = true;
up = false;
down = false;
}
if ((key == (int) Keys.Right) && (!left)) {
right = true;
up = false;
down = false;
}
if ((key == (int) Keys.Up) && (!down)) {
up = true;
right = false;
left = false;
}
if ((key == (int) Keys.Down) && (!up)) {
down = true;
right = false;
left = false;
}
}
}
首先,我们将定义游戏中使用的常量。
WIDTH和HEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 (900 = 300 * 300 / 10 * 10)RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。
private int[] x = new int[ALL_DOTS];
private int[] y = new int[ALL_DOTS];
这两个数组存储蛇的所有关节的 x,y 坐标。
在move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
该代码将关节向上移动。
if (left) {
x[0] -= DOT_SIZE;
}
将头向左移动。
在checkCollision()方法中,我们确定蛇是否击中了自己或撞墙之一。
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
如果蛇用头撞到关节之一,我们就结束游戏。
if (y[0] > HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT - BORDER_WIDTH) {
inGame = false;
}
如果蛇击中了棋盘的底部,我们就结束了游戏。
Snake.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class Snake : Form {
public Snake() {
Text = "Snake";
DoubleBuffered = true;
FormBorderStyle = FormBorderStyle.FixedSingle;
int borderWidth = (this.Width - this.ClientSize.Width) / 2;
int titleBarHeight = this.Height - this.ClientSize.Height - borderWidth;
Board board = new Board();
board.BORDER_WIDTH = borderWidth;
board.TITLEBAR_HEIGHT = titleBarHeight;
Controls.Add(board);
CenterToScreen();
}
}
class MApplication {
public static void Main() {
Application.Run(new Snake());
}
}
这是主要的类。

图:贪食蛇
这是使用 Mono Winforms 库编程的贪食蛇游戏。
Java Gnome 教程
这是 Java Gnome 教程。 在本教程中,我们将学习 Java Gnome 中 GUI 编程的基础。 Java Gnome 教程适合初学者。
目录
Java Gnome
Java Gnome 是 Java 编程语言的 GTK+ 和 Gnome 的包装。
相关教程
其他 Java GUI 工具箱的教程包括 Java Swing 教程, Java SWT 教程和 QtJambi 教程。
Java Gnome 简介
关于本教程
这是 Java Gnome 编程入门。 本教程针对 Java 编程语言。 它已在 Linux 上创建并经过测试。 Java Gnome 编程教程适合新手和中级程序员。
您可以在此处下载本教程中使用的图像。
Java Gnome
Java Gnome 是 Java 编程语言的 GTK+ 和 Gnome 的包装。
GTK+ 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK+ 库也称为 GIMP 工具包。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK+ 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK+ 中创建的。 GTK+ 是面向对象的应用编程接口。 有几种其他编程语言的绑定。 存在用于 C++ ,Python,Perl,Java,C# 和其他编程语言的语言绑定。
GNOME 是一个桌面环境,是在计算机操作系统之上运行的图形用户界面。 这是一个国际项目,包括创建软件开发框架,为桌面选择应用软件,以及开发用于管理应用启动,文件处理以及窗口和任务管理的程序。 GNOME 是 GNU Project 的一部分,可以与各种类似 Unix 的操作系统一起使用。 (wikipedia.org)
开发提示
请使用官方的 Sun JDK。 不要使用任何其他派生或替代。 你被警告了。
下载最新版本的 Java Gnome 库。 手动编译并安装。
./configure --jdk=/home/vronskij/bin/jdk1.6.0_11/
如果您的系统上安装了 OpenJDK 或任何其他非标准 Java 开发工具包,则运行配置脚本,其路径指向已安装的 Sun JDK。
成功构建 Java Gnome 库之后,应该有两个文件。 gtk-4.0.jar和libgtkjni-4.0.10.so。 如果您已经下载了java-gnome 4.0.10。
启动 Java Gnome 应用存在一个长期存在的问题。 通常,您需要创建一个脚本来启动每个 Java gnome 应用。 根据该网站,此问题已解决,但对我不起作用。 我通过复制gtk-4.0.jar所在的libgtkjni-4.0.10.so进行了变通。
$ ls
gtk-4.0.jar libgtkjni-4.0.10.so
$ pwd
/usr/local/share/java
我使用 JDeveloper,此解决方法解决了此问题。

图:添加 Java Gnome 库
在 JDeveloper 中创建新项目时,需要将gtk-4.0.jar添加到项目库中。 从这一点来看,运行应用很简单。
数据来源
这是 Java Gnome 库的简介。
Java Gnome 的第一步
在 Java Gnome 编程教程的这一部分中,我们将进行编程的第一步。 我们将创建简单的程序。
简单的例子
第一个代码示例是一个简单的示例,它显示了居中的窗口。
simple.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program is a simple Java Gnome
* application.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GSimple extends Window {
public GSimple() {
setTitle("Simple");
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 150);
setPosition(WindowPosition.CENTER);
show();
}
public static void main(String[] args) {
Gtk.init(args);
new GSimple();
Gtk.main();
}
}
该代码示例在屏幕中央显示一个小窗口。
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
我们导入所有必要的对象。
public class GSimple extends Window {
我们的应用基于Window类。 窗口是顶级小部件,用作其他小部件的容器。
setTitle("Simple");
在这里,我们设置窗口的标题。
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
如果关闭窗口,此代码将完全退出 Java Gnome 应用。
setDefaultSize(250, 150);
此行设置窗口的默认大小。
setPosition(WindowPosition.CENTER);
这条线使窗口在屏幕上居中。
show();
现在我们显示窗口。 在调用show()方法之前,该窗口不可见。
public static void main(String[] args) {
Gtk.init(args);
new GSimple();
Gtk.main();
}
main()方法是应用的入口点。 它启动并运行程序。

图:简单
图标
在下一个示例中,我们显示应用图标。 大多数窗口管理器在标题栏的左上角以及任务栏上都显示图标。
icon.java
package com.zetcode;
import java.io.FileNotFoundException;
import org.gnome.gdk.Event;
import org.gnome.gdk.Pixbuf;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows a small icon
* in the top left corner of the window.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GIcon extends Window implements Window.DeleteEvent {
Pixbuf icon;
public GIcon() {
setTitle("Icon");
initUI();
connect(this);
setDefaultSize(250, 150);
setPosition(WindowPosition.CENTER);
show();
}
public void initUI() {
try {
icon = new Pixbuf("web.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
setIcon(icon);
}
public boolean onDeleteEvent(Widget widget, Event event) {
Gtk.mainQuit();
return false;
}
public static void main(String[] args) {
Gtk.init(args);
new GIcon();
Gtk.main();
}
}
该代码示例显示了应用图标。
initUI();
我们将用户界面的设置委托给initUI()方法。
try {
icon = new Pixbuf("web.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
我们从磁盘上的文件加载pixbuf。
setIcon(icon);
setIcon()方法为窗口设置图标。

图:图标
按钮
在下一个示例中,我们将使用 Java Gnome 库进一步增强我们的编程技能。
buttons.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Button;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Stock;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows four buttons
* with different characteristics.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GButtons extends Window {
public GButtons() {
setTitle("Buttons");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Fixed fix = new Fixed();
Button btn1 = new Button("Button");
btn1.setSensitive(false);
Button btn2 = new Button("Button");
Button btn3 = new Button(Stock.CLOSE);
Button btn4 = new Button("Button");
btn4.setSizeRequest(80, 40);
fix.put(btn1, 20, 30);
fix.put(btn2, 100, 30);
fix.put(btn3, 20, 80);
fix.put(btn4, 100, 80);
add(fix);
}
public static void main(String[] args) {
Gtk.init(args);
new GButtons();
Gtk.main();
}
}
我们在窗口上显示四个不同的按钮。 我们将看到容器窗口小部件和子窗口小部件之间的区别,并将更改子窗口小部件的某些属性。
Fixed fix = new Fixed();
Fixed小部件是不可见的容器小部件。 其目的是包含其他子窗口小部件。
Button btn1 = new Button("Button");
Button是子窗口小部件。 子窗口小部件放置在容器内。
btn1.setSensitive(false);
我们使此按钮不敏感。 这意味着我们无法单击它。 图形化的小部件为灰色。
Button btn3 = new Button(Stock.CLOSE);
第三个按钮在其区域内显示图像。 Java Gnome 库具有我们可以使用的内置图像库。
btn4.setSizeRequest(80, 40);
在这里,我们更改按钮的大小。
fix.put(btn1, 20, 30);
fix.put(btn2, 100, 30);
...
在这里,我们将按钮小部件放置在固定容器小部件内。
add(fix);
我们将Fixed容器设置为Window小部件的主要容器。
showAll();
我们可以调用showAll()方法(它一次显示所有窗口小部件),也可以在每个窗口小部件上调用show()方法。 包括容器。

图:按钮
在本章中,我们在 Java Gnome 编程库中创建了第一个程序。
Java Gnome 中的布局管理
在本章中,我们将展示如何在窗口或对话框中布置窗口小部件。
在设计应用的 GUI 时,我们决定要使用哪些小部件以及如何在应用中组织这些小部件。 为了组织小部件,我们使用专门的不可见小部件,称为布局容器。 在本章中,我们将提到Alignment,Fixed,VBox,HBox和Table。
Fixed
Fixed容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 在大多数应用中,我们不使用此容器。 我们在某些专业领域使用它。 例如游戏,使用图表的专用应用,可以移动的可调整大小的组件(如电子表格应用中的图表),小型教育示例。
fixed.java
package com.zetcode;
import java.io.FileNotFoundException;
import org.gnome.gdk.Color;
import org.gnome.gdk.Event;
import org.gnome.gdk.Pixbuf;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Image;
import org.gnome.gtk.StateType;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows how to use
* the Fixed container.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GAbsolute extends Window {
private Pixbuf rotunda;
private Pixbuf bardejov;
private Pixbuf mincol;
public GAbsolute() {
setTitle("Absolute");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(300, 280);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
modifyBackground(StateType.NORMAL, new Color(15000, 15000, 15000));
try {
bardejov = new Pixbuf("bardejov.jpg");
rotunda = new Pixbuf("rotunda.jpg");
mincol = new Pixbuf("mincol.jpg");
} catch (FileNotFoundException e) {
System.out.println("Could not load images.");
System.out.println(e.getMessage());
}
Image image1 = new Image(bardejov);
Image image2 = new Image(rotunda);
Image image3 = new Image(mincol);
Fixed fix = new Fixed();
fix.put(image1, 20, 20);
fix.put(image2, 40, 160);
fix.put(image3, 170, 50);
add(fix);
}
public static void main(String[] args) {
Gtk.init(args);
new GAbsolute();
Gtk.main();
}
}
在我们的示例中,我们在窗口上显示了三个小图像。 我们明确指定放置这些图像的 x,y 坐标。
modifyBackground(StateType.NORMAL, new Color(15000, 15000, 15000));
为了获得更好的视觉体验,我们将背景色更改为深灰色。
bardejov = new Pixbuf("bardejov.jpg");
我们将图像从磁盘加载到Pixbuf对象。
Image image1 = new Image(bardejov);
Image image2 = new Image(rotunda);
Image image3 = new Image(mincol);
Image是用于显示图像的小部件。 它在构造器中使用Pixbuf对象。
Fixed fix = new Fixed();
我们创建Fixed容器。
fix.put(image1, 20, 20);
我们将第一个图像放置在x = 20,y = 20坐标处。
add(fix);
最后,我们将Fixed容器添加到窗口中。

图:固定
Alignment
Alignment容器控制其子窗口小部件的对齐方式和大小。
alignment.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Alignment;
import org.gnome.gtk.Button;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HBox;
import org.gnome.gtk.Label;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program places two buttons
* in the right bottom corner of
* the window.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GAlignment extends Window {
public GAlignment() {
setTitle("Alignment");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(260, 150);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 5);
HBox hbox = new HBox(true, 3);
vbox.packStart(new Label(""));
Button ok = new Button("OK");
ok.setSizeRequest(70, 30);
Button close = new Button("Close");
hbox.add(ok);
hbox.add(close);
Alignment halign = new Alignment(1, 0, 0, 0);
halign.add(hbox);
vbox.packStart(halign, false, false, 3);
add(vbox);
setBorderWidth(5);
}
public static void main(String[] args) {
Gtk.init(args);
new GAlignment();
Gtk.main();
}
}
在代码示例中,我们在窗口的右下角放置了两个按钮。 为此,我们使用一个水平框和一个垂直框以及一个对齐容器。
vbox.packStart(new Label(""));
该行将在垂直框中放置一个空标签。 标签水平和垂直扩展。 这将使附加到垂直框的下一个小部件出现在窗口底部。
Button ok = new Button("OK");
ok.setSizeRequest(70, 30);
Button close = new Button("Close");
hbox.add(ok);
hbox.add(close);
这两个按钮将添加到水平框中。
Alignment halign = new Alignment(1, 0, 0, 0);
halign.add(hbox);
水平框将添加到对齐小部件。 对齐小部件将使按钮向右对齐。 我们必须记住,对齐容器仅包含一个子窗口小部件。 这就是为什么我们必须使用盒子。
vbox.packStart(halign, false, false, 3);
对齐小部件包装在垂直框中。

图:对齐
Table
Table小部件按行和列排列小部件。
calculator.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Button;
import org.gnome.gtk.Entry;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.Menu;
import org.gnome.gtk.MenuBar;
import org.gnome.gtk.MenuItem;
import org.gnome.gtk.Table;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* Java Gnome tutorial
*
* This program creates a calculator skeleton
* with the Table container widget.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GCalculator extends Window {
public GCalculator() {
setTitle("Calculator");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 230);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 2);
MenuBar mb = new MenuBar();
Menu filemenu = new Menu();
MenuItem file = new MenuItem("File");
file.setSubmenu(filemenu);
mb.append(file);
vbox.packStart(mb, false, false, 0);
Table table = new Table(5, 4, true);
table.attach(new Button("Cls"), 0, 1, 0, 1);
table.attach(new Button("Bck"), 1, 2, 0, 1);
table.attach(new Label(""), 2, 3, 0, 1);
table.attach(new Button("Close"), 3, 4, 0, 1);
table.attach(new Button("7"), 0, 1, 1, 2);
table.attach(new Button("8"), 1, 2, 1, 2);
table.attach(new Button("9"), 2, 3, 1, 2);
table.attach(new Button("/"), 3, 4, 1, 2);
table.attach(new Button("4"), 0, 1, 2, 3);
table.attach(new Button("5"), 1, 2, 2, 3);
table.attach(new Button("6"), 2, 3, 2, 3);
table.attach(new Button("*"), 3, 4, 2, 3);
table.attach(new Button("1"), 0, 1, 3, 4);
table.attach(new Button("2"), 1, 2, 3, 4);
table.attach(new Button("3"), 2, 3, 3, 4);
table.attach(new Button("-"), 3, 4, 3, 4);
table.attach(new Button("0"), 0, 1, 4, 5);
table.attach(new Button("."), 1, 2, 4, 5);
table.attach(new Button("="), 2, 3, 4, 5);
table.attach(new Button("+"), 3, 4, 4, 5);
vbox.packStart(new Entry(), false, false, 0);
vbox.packStart(table, true, true, 0);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GCalculator();
Gtk.main();
}
}
我们使用Table小部件创建一个计算器框架。
Table table = new Table(5, 4, true);
我们创建一个具有 5 行 4 列的表小部件。 第三个参数是同质参数。 如果设置为true,则表中的所有小部件都具有相同的大小。 所有窗口小部件的大小等于表容器中最大的窗口小部件。
table.attach(new Button("Cls"), 0, 1, 0, 1);
我们在表格容器上附加一个按钮。 到表格的左上方单元格。 前两个参数是单元格的左侧和右侧,后两个参数是单元格的顶部和左侧。 换句话说,它到达表容器的第一个单元格。 在0, 0。
vbox.packStart(new Entry(), false, false, 0);
我们首先打包Entry小部件。
vbox.packStart(table, true, true, 0);
然后,我们添加表格小部件。 我们使其扩展所有剩余空间。

图:计算机骨架
fill和expand
下面的示例说明Box容器的fill和expand参数之间的关系。
boxes.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Button;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HBox;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program explains the
* relationship between the fill
* and expand parameters of the Box
* container.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GBoxes extends Window {
public GBoxes() {
setTitle("Boxes");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 150);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 5);
HBox hbox1 = new HBox(false, 0);
HBox hbox2 = new HBox(false, 0);
HBox hbox3 = new HBox(false, 0);
Button button1 = new Button("Button");
Button button2 = new Button("Button");
Button button3 = new Button("Button");
hbox1.packStart(button1, false, false, 0);
hbox2.packStart(button2, true, false, 0);
hbox3.packStart(button3, true, true, 0);
vbox.packStart(hbox1, false, false, 0);
vbox.packStart(hbox2, false, false, 0);
vbox.packStart(hbox3, false, false, 0);
add(vbox);
setBorderWidth(8);
}
public static void main(String[] args) {
Gtk.init(args);
new GBoxes();
Gtk.main();
}
}
在我们的代码示例中,我们有一个垂直的基本框和另外三个水平的框。 我们有三个按钮。 我们将看到expand和fill参数的组合将如何影响布局。
我们在垂直框内放置三个按钮。 packStart()方法的第一个参数是小部件,我们将其放入容器中。 第二个参数是扩展,第三个参数是填充,最后一个参数是填充。
hbox1.packStart(button1, false, false, 0);
在这种情况下,expand和fill均为假。 该按钮不会增长,并保持其初始位置。
hbox2.packStart(button2, true, false, 0);
在这里,expand参数设置为true。 此按钮窗口小部件将获得额外的空间,但窗口小部件不会增大或缩小。 因此,在我们的例子中,按钮将水平居中并保持其初始大小。
hbox3.packStart(button3, true, true, 0);
最后,我们将两个参数都设置为true。 这将导致按钮占用分配给它的所有水平空间。

图:展开和填充参数
这是 Java Gnome 中布局管理的第一部分。
Java Gnome 中的布局管理 II
在本章中,我们将继续使用 Java Gnome 工具箱中的布局。
新建文件夹
以下代码示例将创建一个新的文件夹对话框。
newfolder.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Alignment;
import org.gnome.gtk.Button;
import org.gnome.gtk.Entry;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HBox;
import org.gnome.gtk.Label;
import org.gnome.gtk.TextView;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a new
* folder window.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GNewFolder extends Window {
public GNewFolder() {
setTitle("New Folder");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(270, 290);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 10);
Label label = new Label("Name:");
Entry entry = new Entry();
HBox hbox1 = new HBox(false, 5);
hbox1.packStart(label, false, false, 0);
hbox1.packStart(entry, true, true, 0);
vbox.packStart(hbox1, false, false, 0);
TextView tw = new TextView();
vbox.packStart(tw);
HBox hbox2 = new HBox(true, 5);
Button ok = new Button("OK");
Button close = new Button("Close");
close.setSizeRequest(65, 30);
hbox2.packStart(ok);
hbox2.packStart(close);
Alignment halign = new Alignment(1, 0, 0, 0);
halign.add(hbox2);
vbox.packStart(halign, false, false, 0);
add(vbox);
setBorderWidth(10);
}
public static void main(String[] args) {
Gtk.init(args);
new GNewFolder();
Gtk.main();
}
}
这是 Java Gnome 中的新文件夹窗口。
VBox vbox = new VBox(false, 10);
垂直框是基础容器。
HBox hbox1 = new HBox(false, 5);
hbox1.packStart(label, false, false, 0);
hbox1.packStart(entry, true, true, 0);
vbox.packStart(hbox1, false, false, 0);
标签和输入小部件放置在水平框中。 标签应保留其默认大小,条目窗口小部件可水平扩展。
TextView tw = new TextView();
vbox.packStart(tw);
文本视图占据了大部分区域。 它可以水平和垂直扩展。
hbox2.packStart(ok);
hbox2.packStart(close);
单击确定和关闭按钮进入水平框。
Alignment halign = new Alignment(1, 0, 0, 0);
halign.add(hbox2);
vbox.packStart(halign, false, false, 0);
上面提到的水平框已添加到对齐小部件中。 这将使按钮向右对齐,并使它们保持默认大小。

图:新文件夹
窗口
接下来,我们将创建一个更高级的示例。 我们显示一个窗口,可以在 JDeveloper IDE 中找到它。
windows.java
package com.zetcode;
import org.gnome.gdk.Color;
import org.gnome.gdk.Event;
import org.gnome.gtk.Alignment;
import org.gnome.gtk.Button;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HBox;
import org.gnome.gtk.Label;
import org.gnome.gtk.StateType;
import org.gnome.gtk.TextView;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
public class GWindows extends Window {
public GWindows() {
setTitle("Windows");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(350, 300);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 9);
// first row
Label windows = new Label("Windows");
windows.setAlignment(0, 0);
vbox.packStart(windows, false, false, 5);
// second row
HBox hbox1 = new HBox(false, 9);
TextView view = new TextView();
hbox1.packStart(view);
VBox vbox2 = new VBox(false, 5);
Button activate = new Button("Activate");
activate.setSizeRequest(80, 30);
Alignment align2 = new Alignment(0, 0, 0, 0);
align2.add(activate);
Button close = new Button("Close");
close.setSizeRequest(80, 30);
Alignment align3 = new Alignment(0, 0, 0, 0);
align3.add(close);
vbox2.packStart(align2, false, false, 0);
vbox2.packStart(align3, false, false, 0);
hbox1.packStart(vbox2, false, false, 0);
vbox.packStart(hbox1);
// third row
HBox hbox2 = new HBox(true, 0);
Button help = new Button("Help");
help.setSizeRequest(80, 30);
Alignment alignHelp = new Alignment(0, 0, 0, 0);
alignHelp.add(help);
Button ok = new Button("OK");
ok.setSizeRequest(80, 30);
Alignment alignOk = new Alignment(1, 0, 0, 0);
alignOk.add(ok);
hbox2.packStart(alignHelp);
hbox2.packStart(alignOk);
vbox.packStart(hbox2, false, false, 0);
add(vbox);
setBorderWidth(9);
}
public static void main(String[] args) {
Gtk.init(args);
new GWindows();
Gtk.main();
}
}
我们将窗口布局分为几个部分。 主容器是垂直盒。 我们在此垂直框中放入三行。 第一个是简单的标签小部件。 第二个是水平框,由视图小部件和一个附加的垂直框组成。 最后,第三行是具有两个按钮的水平框。
VBox vbox = new VBox(false, 9);
这是主要的垂直框。
Button activate = new Button("Activate");
activate.setSizeRequest(80, 30);
Alignment align2 = new Alignment(0, 0, 0, 0);
align2.add(activate);
激活按钮的大小已调整为80x30像素。 它放置在Alignment小部件内,因此它不会缩小或增长。
Button ok = new Button("OK");
ok.setSizeRequest(80, 30);
Alignment alignOk = new Alignment(1, 0, 0, 0);
alignOk.add(ok);
确定按钮右对齐。

图:窗口
查找/替换窗口
在以下示例中,我们将创建一个窗口,您可以在 Eclipse IDE 中找到该窗口。
replace.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Alignment;
import org.gnome.gtk.AttachOptions;
import org.gnome.gtk.Button;
import org.gnome.gtk.CheckButton;
import org.gnome.gtk.Frame;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HBox;
import org.gnome.gtk.Label;
import org.gnome.gtk.Table;
import org.gnome.gtk.TextComboBox;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a complicated
* layout. It uses both box and table
* containers.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GReplace extends Window {
public GReplace() {
setTitle("Replace/Find");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 10);
Label findLabel = new Label("Find");
Label replaceLabel = new Label("Replace With");
TextComboBox combo1 = new TextComboBox();
combo1.appendText("");
TextComboBox combo2 = new TextComboBox();
combo2.appendText("");
HBox hbox1 = new HBox(false, 10);
hbox1.packStart(findLabel, false, false, 0);
hbox1.packStart(combo1);
HBox hbox2 = new HBox(false, 10);
hbox2.packStart(replaceLabel, false, false, 0);
hbox2.packStart(combo2);
vbox.packStart(hbox1, false, false, 0);
vbox.packStart(hbox2, false, false, 0);
// second row
Frame direction = new Frame("Direction");
VBox v1 = new VBox(true, 0);
v1.setBorderWidth(5);
CheckButton cb1 = new CheckButton("Forward");
CheckButton cb2 = new CheckButton("Backward");
v1.packStart(cb1);
v1.packStart(cb2);
direction.add(v1);
Frame scope = new Frame("Scope");
VBox v2 = new VBox(true, 0);
v2.setBorderWidth(5);
CheckButton cb3 = new CheckButton("All");
CheckButton cb4 = new CheckButton("Selected Lines");
v2.packStart(cb3);
v2.packStart(cb4);
scope.add(v2);
HBox framesBox = new HBox(true, 5);
framesBox.packStart(direction);
framesBox.packStart(scope);
vbox.packStart(framesBox, false, false, 0);
// third row
Frame options = new Frame("Options");
Table table1 = new Table(3, 2, false);
CheckButton cb5 = new CheckButton("Case Sensitive");
CheckButton cb6 = new CheckButton("Whole World");
CheckButton cb7 = new CheckButton("Regular Expressions");
CheckButton cb8 = new CheckButton("Wrap Search");
CheckButton cb9 = new CheckButton("Incremental");
table1.attach(cb5, 0, 1, 0, 1, AttachOptions.FILL,
AttachOptions.FILL, 0, 0);
table1.attach(cb6, 0, 1, 1, 2, AttachOptions.FILL,
AttachOptions.FILL, 0, 0);
table1.attach(cb7, 0, 1, 2, 3, AttachOptions.FILL,
AttachOptions.FILL, 0, 0);
table1.attach(cb8, 1, 2, 0, 1, AttachOptions.FILL,
AttachOptions.FILL, 0, 0);
table1.attach(cb9, 1, 2, 1, 2, AttachOptions.FILL,
AttachOptions.FILL, 0, 0);
table1.setBorderWidth(5);
options.add(table1);
vbox.packStart(options, true, true, 0);
// fourth row
Table table2 = new Table(2, 2, true);
Button find = new Button("Find");
Button replace = new Button("Replace");
Button replaceFind = new Button("Replace/Find");
Button replaceAll = new Button("Replace All");
table2.attach(find, 0, 1, 0, 1);
table2.attach(replace, 0, 1, 1, 2);
table2.attach(replaceFind, 1, 2, 0, 1);
table2.attach(replaceAll, 1, 2, 1, 2);
vbox.packStart(table2, false, false, 0);
// fifth row
Button close = new Button("Close");
close.setSizeRequest(80, -1);
Alignment halign = new Alignment(1, 0, 0, 0);
halign.add(close);
vbox.packStart(halign);
add(vbox);
setBorderWidth(15);
}
public static void main(String[] args) {
Gtk.init(args);
new GReplace();
Gtk.main();
}
}
这个例子看起来很复杂。 但是,如果将布局分为几部分,它将变得更加容易。 在本例中,我们将布局分为五行。
HBox hbox1 = new HBox(false, 10);
hbox1.packStart(findLabel, false, false, 0);
hbox1.packStart(combo1);
在第一行中,我们创建两个标签和两个组合框。 在上面的代码中,标签保留其位置和大小。 当我们调整窗口大小时,组合框会扩展和增长。 所有这些都由expand和fill参数控制。
在第二行中,我们有两个框架。 每个框架都有两个复选框。
VBox v1 = new VBox(true, 0);
v1.setBorderWidth(5);
CheckButton cb1 = new CheckButton("Forward");
CheckButton cb2 = new CheckButton("Backward");
v1.packStart(cb1);
v1.packStart(cb2);
direction.add(v1);
我们为框架小部件设置一个垂直框。 在此框内,我们放置复选按钮。
HBox framesBox = new HBox(true, 5);
framesBox.packStart(direction);
framesBox.packStart(scope);
vbox.packStart(framesBox, false, false, 0);
这两个框架放在水平框内。 然后水平盒放入基本的垂直盒容器中。
第三行是框架,它在水平和垂直方向都可以扩展。 这次,我们在框架内放置了五个复选框。 为此,我们使用表容器。
第四行包含四个按钮。 它们都是相同的大小。 表格小部件非常适合这种布局。
Button close = new Button("Close");
close.setSizeRequest(80, -1);
Alignment halign = new Alignment(1, 0, 0, 0);
halign.add(close);
最后,最后一行。 我们使用Alignment小部件将按钮右对齐。 Alignment小部件还使按钮保留其初始值。 换句话说,它不会增长或收缩。

图:查找/替换窗口
在 Java Gnome 教程的这一部分中,我们创建了一些更复杂的布局。
Java Gnome 中的菜单
在 Java Gnome 编程教程的这一部分中,我们将使用菜单。
菜单栏是 GUI 应用中最常见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 这些公认的标准可进一步减少学习新应用的时间。
简单菜单
在第一个示例中,我们将创建一个带有一个文件菜单的菜单栏。 该菜单将只有一个菜单项。 通过选择项目,应用退出。
simplemenu.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Menu;
import org.gnome.gtk.MenuBar;
import org.gnome.gtk.MenuItem;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a simple menu.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GSimpleMenu extends Window {
public GSimpleMenu() {
setTitle("Simple menu");
initUI();
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
private void initUI() {
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
VBox vbox = new VBox(false, 0);
MenuBar menuBar = new MenuBar();
MenuItem fileItem = new MenuItem("File");
menuBar.append(fileItem);
Menu quitMenu = new Menu();
MenuItem quitItem = new MenuItem("Quit");
quitItem.connect(new MenuItem.Activate() {
public void onActivate(MenuItem menuItem) {
Gtk.mainQuit();
}
});
quitMenu.append(quitItem);
fileItem.setSubmenu(quitMenu);
vbox.packStart(menuBar, false, false, 3);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GSimpleMenu();
Gtk.main();
}
}
这是一个最小的菜单栏功能示例。
MenuBar menuBar = new MenuBar();
MenuBar小部件已创建。
MenuItem fileItem = new MenuItem("File");
menuBar.append(fileItem);
创建顶层MenuItem。
Menu quitMenu = new Menu();
MenuItem quitItem = new MenuItem("Quit");
...
quitMenu.append(quitItem);
创建退出MenuItem并将其附加到退出Menu。
fileItem.setSubmenu(quitMenu);
退出菜单设置为顶级文件菜单项的子菜单。
VBox vbox = new VBox(false, 0);
...
vbox.packStart(menuBar, false, false, 3);
add(vbox);
与其他工具包不同,我们必须自己照顾菜单栏的布局管理。 我们将菜单栏放入垂直框中。

图:简单菜单
图像菜单
在下一个示例中,我们将进一步探索菜单。 我们将使用图像和分隔符。
imagemenu.java
package com.zetcode;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.ImageMenuItem;
import org.gnome.gtk.Menu;
import org.gnome.gtk.MenuBar;
import org.gnome.gtk.MenuItem;
import org.gnome.gtk.SeparatorMenuItem;
import org.gnome.gtk.Stock;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows images in
* the menu.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GImageMenu extends Window {
public GImageMenu() {
setTitle("ImageMenu");
initUI();
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
private void initUI() {
VBox vbox = new VBox(false, 0);
add(vbox);
MenuBar menuBar = new MenuBar();
MenuItem file = new MenuItem("_File");
Menu fileMenu = new Menu();
ImageMenuItem imnew = new ImageMenuItem(Stock.NEW);
ImageMenuItem imopen = new ImageMenuItem(Stock.OPEN);
ImageMenuItem imquit = new ImageMenuItem(Stock.QUIT);
imquit.connect(new MenuItem.Activate() {
public void onActivate(MenuItem menuItem) {
Gtk.mainQuit();
}
});
fileMenu.append(imnew);
fileMenu.append(imopen);
fileMenu.append(new SeparatorMenuItem());
fileMenu.append(imquit);
file.setSubmenu(fileMenu);
menuBar.append(file);
vbox.packStart(menuBar, false, false, 3);
}
public static void main(String[] args) {
Gtk.init(args);
new GImageMenu();
Gtk.main();
}
}
该代码示例显示了如何在 Java Gnome 的菜单中使用图像和分隔符。
ImageMenuItem imnew = new ImageMenuItem(Stock.NEW);
ImageMenuItem imopen = new ImageMenuItem(Stock.OPEN);
ImageMenuItem imquit = new ImageMenuItem(Stock.QUIT);
ImageMenuItem对象已创建。 图像来自图像的内部库存。
fileMenu.append(new SeparatorMenuItem());
此代码行创建一个分隔符。 它用于将菜单项分成逻辑组。

图:图像 menu
CheckMenuItem
CheckMenuItem是带有复选框的菜单项。 它可以用于布尔属性。
checkmenuitem.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.CheckMenuItem;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.Menu;
import org.gnome.gtk.MenuBar;
import org.gnome.gtk.MenuItem;
import org.gnome.gtk.Statusbar;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates checked menu
* item.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GCheckMenuItem extends Window {
private Statusbar statusbar;
public GCheckMenuItem() {
setTitle("Check menu item");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
private void initUI() {
VBox vbox = new VBox(false, 0);
MenuBar menuBar = new MenuBar();
MenuItem fileItem = new MenuItem("File");
menuBar.append(fileItem);
Menu fileMenu = new Menu();
MenuItem quitItem = new MenuItem("Quit");
quitItem.connect(new MenuItem.Activate() {
public void onActivate(MenuItem menuItem) {
Gtk.mainQuit();
}
});
fileMenu.append(quitItem);
fileItem.setSubmenu(fileMenu);
Menu viewmenu = new Menu();
MenuItem view = new MenuItem("View");
view.setSubmenu(viewmenu);
CheckMenuItem stat = new CheckMenuItem("View Statusbar");
stat.setActive(true);
viewmenu.append(stat);
menuBar.append(view);
statusbar = new Statusbar();
statusbar.setMessage("Ready");
vbox.packStart(menuBar, false, false, 0);
vbox.packStart(new Label(""), true, false, 0);
vbox.packStart(statusbar, false, false, 0);
stat.connect(new MenuItem.Activate() {
public void onActivate(MenuItem menuItem) {
CheckMenuItem item = (CheckMenuItem) menuItem;
if (item.getActive()) {
statusbar.show();
} else {
statusbar.hide();
}
}
});
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GCheckMenuItem();
Gtk.main();
}
}
在我们的代码示例中,我们显示一个检查菜单项。 如果该复选框已激活,则显示状态栏小部件。 如果不是,状态栏将被隐藏。
CheckMenuItem stat = new CheckMenuItem("View Statusbar");
CheckMenuItem小部件已创建。
stat.setActive(true);
setActive()方法选中/取消选中检查菜单项。
if (item.getActive()) {
statusbar.show();
} else {
statusbar.hide();
}
根据CheckMenuItem的状态,我们显示或隐藏状态栏小部件。

图:CheckMenuItem
子菜单
我们的最后一个示例演示了如何在 Java Gnome 中创建子菜单。
submenu.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Menu;
import org.gnome.gtk.MenuBar;
import org.gnome.gtk.MenuItem;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a submenu.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GSubmenu extends Window {
public GSubmenu() {
setTitle("Simple menu");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
private void initUI() {
VBox vbox = new VBox(false, 0);
MenuBar menuBar = new MenuBar();
MenuItem fileItem = new MenuItem("File");
menuBar.append(fileItem);
Menu fileMenu = new Menu();
MenuItem quitItem = new MenuItem("Quit");
quitItem.connect(new MenuItem.Activate() {
public void onActivate(MenuItem menuItem) {
Gtk.mainQuit();
}
});
// submenu creation
Menu imenu = new Menu();
MenuItem importm = new MenuItem("Import");
importm.setSubmenu(imenu);
MenuItem inews = new MenuItem("Import news feed...");
MenuItem ibookmarks = new MenuItem("Import bookmarks...");
MenuItem imail = new MenuItem("Import mail...");
imenu.append(inews);
imenu.append(ibookmarks);
imenu.append(imail);
fileMenu.append(importm);
fileMenu.append(quitItem);
fileItem.setSubmenu(fileMenu);
vbox.packStart(menuBar, false, false, 3);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GSubmenu();
Gtk.main();
}
}
子菜单创建。
Menu imenu = new Menu();
子菜单是Menu。
MenuItem importm = new MenuItem("Import");
importm.setSubmenu(imenu);
它是菜单项的子菜单,它会登录到顶级文件菜单。
MenuItem inews = new MenuItem("Import news feed...");
MenuItem ibookmarks = new MenuItem("Import bookmarks...");
MenuItem imail = new MenuItem("Import mail...");
imenu.append(inews);
imenu.append(ibookmarks);
imenu.append(imail);
子菜单有其自己的菜单项。

图:子菜单
在 Java Gnome 编程库的这一章中,我们展示了如何使用菜单。
Windows API 教程
这是针对 C 编程语言的 Windows API 教程。 这是纯 Windows API 教程。 它不涵盖 MFC。 阅读完本教程后,您将能够编写非常规的 Windows 应用。 即使您使用高级编程语言进行编程,本教程也将为您提供有价值的知识,让您深入了解事物的工作原理。 请注意,本教程使用 C99。
目录
电子书
ZetCode 上有独特的电子书 Windows API 编程简介; PDF 格式,包含 244 页和 114 个代码示例。
Windows API
Windows API 是 Microsoft Windows 操作系统中提供的 Microsoft 核心应用编程接口(API)集。 它以前称为 Winapi 或 Win32 API。
PyQt5 日期和时间
PyQt5 教程的这一部分显示了如何在 PyQt5 中使用日期和时间。
QDate,QTime,QDateTime
PyQt5 具有QDate,QDateTime和QTime类,可用于日期和时间。 QDate是用于使用公历中的日历日期的类。 它具有确定日期,比较或操纵日期的方法。 QTime类使用时钟时间。 它提供了比较时间,确定时间的方法以及其他各种时间操纵方法。 QDateTime是将QDate和QTime对象结合为一个对象的类。
当前日期和时间
PyQt5 具有currentDate(),currentTime()和currentDateTime()方法来确定当前日期和时间。
current_date_time.py
#!/usr/bin/python3
from PyQt5.QtCore import QDate, QTime, QDateTime, Qt
now = QDate.currentDate()
print(now.toString(Qt.ISODate))
print(now.toString(Qt.DefaultLocaleLongDate))
datetime = QDateTime.currentDateTime()
print(datetime.toString())
time = QTime.currentTime()
print(time.toString(Qt.DefaultLocaleLongDate))
该示例以各种格式打印当前日期,日期和时间以及时间。
now = QDate.currentDate()
currentDate()方法返回当前日期。
print(now.toString(Qt.ISODate))
print(now.toString(Qt.DefaultLocaleLongDate))
通过将Qt.ISODate和Qt.DefaultLocaleLongDate值传递给toString()方法,以两种不同的格式打印日期。
datetime = QDateTime.currentDateTime()
currentDateTime()返回当前日期和时间。
time = QTime.currentTime()
最后,currentTime()方法返回当前时间。
$ ./current_date_time.py
2017-09-11
Monday, September 11, 2017
Mon Sep 11 12:37:45 2017
12:37:45 PM CEST
这是输出。
UTC 时间
我们的星球是一个球体; 它绕其轴旋转。 地球向东旋转,因此太阳在不同位置的不同时间升起。 地球大约每 24 小时旋转一次。 因此,世界被划分为 24 个时区。 在每个时区,都有一个不同的本地时间。 夏令时通常会进一步修改此本地时间。
实际需要一个全球时间。 全球时间可以避免时区和夏令时的混淆。 UTC(世界标准时间)被选为主要时间标准。 UTC 用于航空,天气预报,飞行计划,空中交通管制通关和地图。 与当地时间不同,UTC 不会随季节变化而变化。
utc_local.py
#!/usr/bin/python3
from PyQt5.QtCore import QDateTime, Qt
now = QDateTime.currentDateTime()
print("Local datetime: ", now.toString(Qt.ISODate))
print("Universal datetime: ", now.toUTC().toString(Qt.ISODate))
print("The offset from UTC is: {0} seconds".format(now.offsetFromUtc()))
该示例确定当前的通用和本地日期和时间。
print("Local datetime: ", now.toString(Qt.ISODate))
currentDateTime()方法返回表示为本地时间的当前日期和时间。 我们可以使用toLocalTime()将世界时转换为当地时间。
print("Universal datetime: ", now.toUTC().toString(Qt.ISODate))
我们通过日期时间对象中的toUTC()方法获得了通用时间。
print("The offset from UTC is: {0} seconds".format(now.offsetFromUtc()))
offsetFromUtc()以秒为单位给出了通用时间和本地时间之间的时差。
$ ./utc_local.py
Local datetime: 2017-09-11T13:01:58
Universal datetime: 2017-09-11T11:01:58Z
The offset from UTC is: 7200 seconds
这是输出。
天数
特定月份的天数通过daysInMonth()方法返回,一年中的天数通过daysInYear()方法返回。
days.py
#!/usr/bin/python3
from PyQt5.QtCore import QDate, Qt
now = QDate.currentDate()
d = QDate(1945, 5, 7)
print("Days in month: {0}".format(d.daysInMonth()))
print("Days in year: {0}".format(d.daysInYear()))
该示例显示所选日期的月份和年份中的天数。
$ ./days.py
Days in month: 31
Days in year: 365
这是输出。
天数差异
daysTo()方法返回从一个日期到另一个日期的天数。
xmas.py
#!/usr/bin/python3
from PyQt5.QtCore import QDate
xmas1 = QDate(2016, 12, 24)
xmas2 = QDate(2017, 12, 24)
now = QDate.currentDate()
dayspassed = xmas1.daysTo(now)
print("{0} days have passed since last XMas".format(dayspassed))
nofdays = now.daysTo(xmas2)
print("There are {0} days until next XMas".format(nofdays))
该示例计算从上一个 XMas 经过的天数以及直到下一个 XMas 为止的天数。
$ ./xmas.py
261 days have passed since last XMas
There are 104 days until next XMas
这是输出。
日期时间算法
我们通常需要在日期时间值上加上或减去天,秒或年。
arithmetic.py
#!/usr/bin/python3
from PyQt5.QtCore import QDateTime, Qt
now = QDateTime.currentDateTime()
print("Today:", now.toString(Qt.ISODate))
print("Adding 12 days: {0}".format(now.addDays(12).toString(Qt.ISODate)))
print("Subtracting 22 days: {0}".format(now.addDays(-22).toString(Qt.ISODate)))
print("Adding 50 seconds: {0}".format(now.addSecs(50).toString(Qt.ISODate)))
print("Adding 3 months: {0}".format(now.addMonths(3).toString(Qt.ISODate)))
print("Adding 12 years: {0}".format(now.addYears(12).toString(Qt.ISODate)))
该示例确定当前日期时间,并添加或减去天,秒,月和年。
$ ./arithmetics.py
Today: 2017-09-11T13:15:24
Adding 12 days: 2017-09-23T13:15:24
Subtracting 22 days: 2017-08-20T13:15:24
Adding 50 seconds: 2017-09-11T13:16:14
Adding 3 months: 2017-12-11T13:15:24
Adding 12 years: 2029-09-11T13:15:24
这是示例输出。
夏令时
夏令时(DST)是在夏季的几个月中增加时钟的一种做法,因此晚上的夏时制持续时间更长。 在春季开始时将时间向前调整一小时,在秋季将时间向后调整为标准时间。
daylight_saving.py
#!/usr/bin/python3
from PyQt5.QtCore import QDateTime, QTimeZone, Qt
now = QDateTime.currentDateTime()
print("Time zone: {0}".format(now.timeZoneAbbreviation()))
if now.isDaylightTime():
print("The current date falls into DST time")
else:
print("The current date does not fall into DST time")
该示例检查日期时间是否在夏时制中。
print("Time zone: {0}".format(now.timeZoneAbbreviation()))
timeZoneAbbreviation()方法返回日期时间的时区缩写。
if now.isDaylightTime():
如果日期时间落在夏令时,则isDaylightTime()返回。
$ ./daylight_saving.py
Time zone: CEST
The current date falls into DST time
当前日期为夏令时(DST)时间。该程序于夏季在中欧的布拉迪斯拉发执行。 中欧夏令时间(CEST)比世界标准时间早 2 小时。 该时区是夏令时时区,在欧洲和南极洲使用。 冬季使用的标准时间是中欧时间(CET)。
Unix 纪元
纪元是选择作为特定纪元起源的时间瞬间。 例如,在西方基督教国家,时间从耶稣出生的第 0 天开始。 另一个例子是法国共和党日历,使用了十二年。 这个时期是 1792 年 9 月 22 日宣布的共和纪元的开始,即宣布成立第一共和国并废除君主制的那一天。
电脑也有自己的纪元。 最受欢迎的版本之一是 Unix 纪元。 Unix 纪元是 1970 年 1 月 1 日 UTC 时间 00:00:00(或1970-01-01T00:00:00Z ISO8601)。 计算机中的日期和时间是根据自该计算机或平台的定义时期以来经过的秒数或时钟滴答数确定的。
Unix 时间是自 Unix 纪元以来经过的秒数。
$ date +%s
1505128973
Unix date命令可用于获取 Unix 时间。 在这个特定时刻,自 Unix 纪元以来已经过去了 1505128973 秒。
unix_time.py
#!/usr/bin/python3
from PyQt5.QtCore import QDateTime, Qt
now = QDateTime.currentDateTime()
unix_time = now.toSecsSinceEpoch()
print(unix_time)
d = QDateTime.fromSecsSinceEpoch(unix_time)
print(d.toString(Qt.ISODate))
该示例显示 Unix 时间并将其转换回QDateTime。
now = QDateTime.currentDateTime()
首先,我们获取当前日期和时间。
unix_time = now.toSecsSinceEpoch()
toSecsSinceEpoch()返回 Unix 时间。
d = QDateTime.fromSecsSinceEpoch(unix_time)
使用fromSecsSinceEpoch()我们将 Unix 时间转换为QDateTime。
$ ./unix_time.py
1505129851
2017-09-11T13:37:31
这是输出。
朱利安日
朱利安日是指自朱利安纪元开始以来的连续天数。 它主要由天文学家使用。 不应将其与朱利安历法相混淆。 朱利安纪元开始于公元前 4713 年。 朱利安天数 0 被指定为从公元前 4713 年 1 月 1 日正午开始的那一天。
朱利安天数(JDN)是自此时间段开始以来经过的天数。 任意时刻的儒略日期(JD)是前一个正午的儒略日编号加上该时刻以来的那一天的分数。 (Qt 不会计算此分数。)除天文学外,军事和大型机程序经常使用儒略日期。
julian_day.py
#!/usr/bin/python3
from PyQt5.QtCore import QDate, Qt
now = QDate.currentDate()
print("Gregorian date for today: ", now.toString(Qt.ISODate))
print("Julian day for today: ", now.toJulianDay())
在示例中,我们计算了今天的公历日期和儒略日。
print("Julian day for today: ", now.toJulianDay())
使用toJulianDay()方法返回儒略日。
$ ./julianday.py
Gregorian date for today: 2017-09-11
Julian day for today: 2458008
这是输出。
历史战役
在朱利安纪元,可以进行跨越几个世纪的计算。
battles.py
#!/usr/bin/python3
from PyQt5.QtCore import QDate, Qt
borodino_battle = QDate(1812, 9, 7)
slavkov_battle = QDate(1805, 12, 2)
now = QDate.currentDate()
j_today = now.toJulianDay()
j_borodino = borodino_battle.toJulianDay()
j_slavkov = slavkov_battle.toJulianDay()
d1 = j_today - j_slavkov
d2 = j_today - j_borodino
print("Days since Slavkov battle: {0}".format(d1))
print("Days since Borodino battle: {0}".format(d2))
该示例计算自两次历史事件以来经过的天数。
borodino_battle = QDate(1812, 9, 7)
slavkov_battle = QDate(1805, 12, 2)
我们有拿破仑纪元的两次战斗。
j_today = now.toJulianDay()
j_borodino = borodino_battle.toJulianDay()
j_slavkov = slavkov_battle.toJulianDay()
我们计算了今天以及斯拉夫科夫和波罗底诺战役的儒略日。
d1 = j_today - j_slavkov
d2 = j_today - j_borodino
我们计算了两次战斗以来经过的天数。
$ ./battles.py
Days since Slavkov battle: 77350
Days since Borodino battle: 74879
运行此脚本时,自从斯拉夫科夫战役以来已经过去了 77350 天,从波罗底诺战役已经过去了 74878 天。
在 PyQt5 教程的这一部分中,我们处理了日期和时间。
Java Gnome 中的工具栏
在 Java Gnome 编程教程的这一部分中,我们将使用工具栏。
菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。
简单的工具栏
接下来,我们创建一个简单的工具栏。
toolbar.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Orientation;
import org.gnome.gtk.SeparatorToolItem;
import org.gnome.gtk.Stock;
import org.gnome.gtk.ToolButton;
import org.gnome.gtk.Toolbar;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a simple toolbar.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GToolbar extends Window {
public GToolbar() {
setTitle("Toolbar");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(300, 250);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Toolbar toolbar = new Toolbar();
ToolButton newtb = new ToolButton(Stock.NEW);
ToolButton opentb = new ToolButton(Stock.OPEN);
ToolButton savetb = new ToolButton(Stock.SAVE);
SeparatorToolItem sep = new SeparatorToolItem();
ToolButton quittb = new ToolButton(Stock.QUIT);
toolbar.insert(newtb, 0);
toolbar.insert(opentb, 1);
toolbar.insert(savetb, 2);
toolbar.insert(sep, 3);
toolbar.insert(quittb, 4);
quittb.connect(new ToolButton.Clicked() {
public void onClicked(ToolButton toolButton) {
Gtk.mainQuit();
}
});
VBox vbox = new VBox(false, 2);
vbox.packStart(toolbar, false, false, 0);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GToolbar();
Gtk.main();
}
}
该示例显示了一个工具栏和四个工具按钮。
Toolbar toolbar = new Toolbar();
Toolbar小部件已创建。
ToolButton newtb = new ToolButton(Stock.NEW);
创建带有库存图像的ToolButton。
SeparatorToolItem sep = new SeparatorToolItem();
这是一个分隔符。 它可以用于将工具栏按钮分成逻辑组。
toolbar.insert(newtb, 0);
toolbar.insert(opentb, 1);
...
工具栏按钮插入到工具栏小部件中。

图:工具栏
工具栏
在第二个示例中,我们显示了两个工具栏。 许多应用具有多个工具栏。 我们展示了如何在 Java Gnome 中做到这一点。
toolbars.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Stock;
import org.gnome.gtk.ToolButton;
import org.gnome.gtk.Toolbar;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates two toolbar
* instances.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GToolbars extends Window {
public GToolbars() {
setTitle("Toolbars");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(300, 250);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Toolbar upper = new Toolbar();
ToolButton newtb = new ToolButton(Stock.NEW);
ToolButton opentb = new ToolButton(Stock.OPEN);
ToolButton savetb = new ToolButton(Stock.SAVE);
upper.insert(newtb, 0);
upper.insert(opentb, 1);
upper.insert(savetb, 2);
Toolbar lower = new Toolbar();
ToolButton quittb = new ToolButton(Stock.QUIT);
quittb.connect(new ToolButton.Clicked() {
public void onClicked(ToolButton toolButton) {
Gtk.mainQuit();
}
});
lower.insert(quittb, 0);
VBox vbox = new VBox(false, 2);
vbox.packStart(upper, false, false, 0);
vbox.packStart(lower, false, false, 0);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GToolbars();
Gtk.main();
}
}
该应用显示两个工具栏。
Toolbar upper = new Toolbar();
...
Toolbar lower = new Toolbar();
我们创建两个Toolbar小部件。
upper.insert(newtb, 0);
...
lower.insert(quittb, 0);
它们每个都有自己的工具按钮。
VBox vbox = new VBox(false, 2);
vbox.packStart(upper, false, false, 0);
vbox.packStart(lower, false, false, 0);
工具栏一个接一个地包装在垂直盒中。

图:工具栏 s
撤销重做
以下示例演示了如何停用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法。 例如,保存按钮。 如果我们将文档的所有更改都保存到磁盘上,则在大多数文本编辑器中,“保存”按钮将被禁用。 这样,应用会向用户指示所有更改都已保存。
undoredo.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.SeparatorToolItem;
import org.gnome.gtk.Stock;
import org.gnome.gtk.ToolButton;
import org.gnome.gtk.Toolbar;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates an undo redo
* example. It shows how to enable/disable
* tool buttons if they cannot be used no
* more.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GUndoRedo extends Window {
private int count = 2;
private ToolButton undo;
private ToolButton redo;
public GUndoRedo() {
setTitle("Undo redo");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(300, 250);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Toolbar toolbar = new Toolbar();
undo = new ToolButton(Stock.UNDO);
redo = new ToolButton(Stock.REDO);
SeparatorToolItem sep = new SeparatorToolItem();
ToolButton quit = new ToolButton(Stock.QUIT);
toolbar.insert(undo, 0);
toolbar.insert(redo, 1);
toolbar.insert(sep, 2);
toolbar.insert(quit, 3);
undo.connect(new ToolButton.Clicked() {
public void onClicked(ToolButton toolButton) {
count -= 1;
if (count <= 0) {
undo.setSensitive(false);
redo.setSensitive(true);
}
}
});
redo.connect(new ToolButton.Clicked() {
public void onClicked(ToolButton toolButton) {
count += 1;
if (count >= 5) {
redo.setSensitive(false);
undo.setSensitive(true);
}
}
});
quit.connect(new ToolButton.Clicked() {
public void onClicked(ToolButton toolButton) {
Gtk.mainQuit();
}
});
VBox vbox = new VBox(false, 2);
vbox.packStart(toolbar, false, false, 0);
vbox.packStart(new Label(""), false, false, 0);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GUndoRedo();
Gtk.main();
}
}
我们的示例从 Java Gnome 库存资源创建撤消和重做按钮。 单击几下后,每个按钮均被禁用。 按钮显示为灰色。
private int count = 2;
count变量决定哪个按钮被激活和禁用。
undo = new ToolButton(Stock.UNDO);
redo = new ToolButton(Stock.REDO);
我们有两个工具按钮。 撤消和重做工具按钮。 图片来自库存资源。
undo.connect(new ToolButton.Clicked() {
public void onClicked(ToolButton toolButton) {
count -= 1;
if (count <= 0) {
undo.setSensitive(false);
redo.setSensitive(true);
}
}
});
我们为撤消工具按钮插入Clicked事件的方法。 要激活/禁用小部件,我们使用setSensitive()方法。

图:撤销和重做
在 Java Gnome 编程库的这一章中,我们提到了工具栏。
Java Gnome 中的事件
在 Java Gnome 编程教程的这一部分中,我们将讨论事件。
Java Gnome 库是事件驱动的系统。 所有 GUI 应用都是事件驱动的。 应用启动一个主循环,该循环不断检查新生成的事件。 如果没有事件,则应用将等待并且不执行任何操作。
简单事件示例
下一个示例显示了我们如何应对两个基本事件。
quitbutton.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Button;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* Java Gnome tutorial
*
* This program demonstrates two
* basic events.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GButton extends Window {
public GButton() {
setTitle("Button");
initUI();
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
Fixed fixed = new Fixed();
Button quit = new Button("Quit");
quit.connect(new Button.Clicked() {
public void onClicked(Button button) {
Gtk.mainQuit();
}
});
quit.setSizeRequest(80, 35);
fixed.put(quit, 50, 50);
add(fixed);
setSizeRequest(250, 200);
}
public static void main(String[] args) {
Gtk.init(args);
new GButton();
Gtk.main();
}
}
在我们的代码示例中,我们对两个事件做出反应。 删除事件和单击事件。 当我们关闭窗口时,将触发删除事件。 默认情况下,该窗口被销毁,但应用不退出。 我们必须明确退出该程序。
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
如果单击窗口的关闭按钮,则窗口将被破坏。 但是应用并未完全销毁。 我们必须调用Gtk.mainQuit()结束应用。 我们使用connect()方法将回调方法连接到特定事件类型。 在我们的例子中是DeleteEvent。
quit.connect(new Button.Clicked() {
public void onClicked(Button button) {
Gtk.mainQuit();
}
});
当我们单击按钮小部件时,将触发onClicked()方法。 我们对按钮单击做出反应,退出了应用。
移动窗口
下一个示例显示了我们如何对移动窗口事件做出反应。 我们在标题栏中显示窗口左上角的当前位置。
move.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventConfigure;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the
* configure event.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GMoveWindow extends Window
implements Window.ConfigureEvent {
public GMoveWindow() {
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
connect(this);
setPosition(WindowPosition.CENTER);
setTitle("");
resize(250, 200);
showAll();
}
public boolean onConfigureEvent(Widget widget,
EventConfigure eventConfigure) {
int x = eventConfigure.getX();
int y = eventConfigure.getY();
setTitle(x + ", " + y);
return false;
}
public static void main(String[] args) {
Gtk.init(args);
new GMoveWindow();
Gtk.main();
}
}
调整大小和移动窗口最终导致创建ConfigureEvent。
public boolean onConfigureEvent(Widget widget,
EventConfigure eventConfigure) {
int x = eventConfigure.getX();
int y = eventConfigure.getY();
setTitle(x + ", " + y);
return false;
}
我们将窗口的 x,y 坐标设置为窗口的标题栏。

图:移动事件
EnterNotifyEvent
当我们使用鼠标指针进入小部件的区域时,会发出EnterNotifyEvent。
enter.java
package com.zetcode;
import org.gnome.gdk.Color;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventCrossing;
import org.gnome.gtk.Button;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.StateType;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the
* EnterNotifyEvent.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GEnterNotifyEvent extends Window {
private Color lightGray = new Color(55000, 55000, 55000);
public GEnterNotifyEvent() {
setTitle("EnterNotifyEvent");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 150);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Button button = new Button("Button");
button.setSizeRequest(80, 30);
button.connect(new Button.EnterNotifyEvent() {
public boolean onEnterNotifyEvent(Widget widget,
EventCrossing eventCrossing) {
widget.modifyBackground(StateType.PRELIGHT, lightGray);
return false;
}
});
Fixed fix = new Fixed();
fix.put(button, 20, 20);
add(fix);
}
public static void main(String[] args) {
Gtk.init(args);
new GEnterNotifyEvent();
Gtk.main();
}
}
一旦将鼠标指针悬停在按钮小部件上,我们将更改其背景颜色。
button.connect(new Button.EnterNotifyEvent() {
public boolean onEnterNotifyEvent(Widget widget,
EventCrossing eventCrossing) {
widget.modifyBackground(StateType.PRELIGHT, lightGray);
return false;
}
});
在这里,我们对EnterNotifyEvent做出反应。
widget.modifyBackground(StateType.PRELIGHT, lightGray);
我们修改按钮背景的颜色。
本章介绍 Java Gnome 中的事件。
Java Gnome 中的小部件
在 Java Gnome 编程教程的这一部分中,我们将介绍一些小部件。
小部件是 GUI 应用的基本构建块。 多年来,几个小部件已成为所有 OS 平台上所有工具包中的标准。 例如,按钮,复选框或滚动条。 GTK+ 工具箱的理念是将小部件的数量保持在最低水平。 将创建更多专门的小部件作为自定义小部件。
Label
Label小部件显示文本。
label.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program uses Label widget
* to display text.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GLabel extends Window {
public GLabel() {
setTitle("Death song");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Label lyrics = new Label("Lets sing the death song kids\n\n" +
"We light a candle on an earth\n" +
"We made into hell\n" +
"And pretend that were in heaven\n" +
"Each time we do we get\n" +
"The blind mans ticket\n" +
"And we know that nothings true\n" +
"I saw priest kill a cop on the tv\n" +
"And I know now theyre our heroes too\n\n" +
"We sing the death song kids\n" +
"Because weve got no future\n" +
"And we want to be just like you\n" +
"And we want to be just like you\n");
add(lyrics);
setBorderWidth(8);
}
public static void main(String[] args) {
Gtk.init(args);
new GLabel();
Gtk.main();
}
}
该代码示例在窗口上显示了一些歌词。
Label lyrics = new Label("Lets sing the death song kids\n\n" +
"We light a candle on an earth\n" +
...
这是我们将在Label小部件中显示的文本。
setBorderWidth(8);
Label周围有一些空白。

图:Label小部件
HSeparator
HSeparator是一个装饰小部件,可用于分隔窗口上的项目。
separator.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HSeparator;
import org.gnome.gtk.Label;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* Java Gnome tutorial
*
* This program shows how to use
* a horizontal separator.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GHSeparator extends Window {
private final int VERTICAL_SPACE = 15;
private final int BORDER_AROUND = 25;
private final float Y_ALIGN = 0f;
private final float X_ALIGN = 0f;
public GHSeparator() {
setTitle("HSeparator");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, VERTICAL_SPACE);
Label zinc = new Label("Zinc is a moderately reactive, blue" +
"gray metal that tarnishes in moist air and burns in air of zinc " +
"oxide. It reacts with acids, alkalis and other non-metals. If not" +
"completely pure, zinc reacts with dilute acids to release hydrogen.");
zinc.setLineWrap(true);
zinc.setAlignment(X_ALIGN, Y_ALIGN);
vbox.packStart(zinc);
HSeparator hsep = new HSeparator();
vbox.packStart(hsep);
Label copper = new Label("Copper is an essential trace nutrient to" +
"all high plants and animals. In animals, including humans," +
"it is found primarily in the bloodstream, as a co-factor in various" +
"enzymes, and in copper-based pigments. However, in sufficient" +
"amounts, copper can be poisonous and even fatal to organisms.");
copper.setAlignment(X_ALIGN, Y_ALIGN);
copper.setLineWrap(true);
vbox.packStart(copper);
add(vbox);
setResizable(false);
setBorderWidth(BORDER_AROUND);
}
public static void main(String[] args) {
Gtk.init(args);
new GHSeparator();
Gtk.main();
}
}
在我们的代码示例中,我们描述了两个化学元素,它们由水平分隔符分隔。
zinc.setLineWrap(true);
该行换行。 对于较长的文本,这是必需的。
zinc.setAlignment(X_ALIGN, Y_ALIGN);
此代码行使文本左对齐。
HSeparator hsep = new HSeparator();
vbox.packStart(hsep);
HSeparator小部件已创建并放置在两个标签小部件之间。

图:HSeparator
CheckButton
CheckButton是具有两种状态的窗口小部件:打开和关闭。 接通状态通过复选标记显示。 它用来表示一些布尔属性。
checkbutton.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.CheckButton;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.ToggleButton;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program uses a CheckButton to
* toggle the visibility of a window title.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GCheckButton extends Window implements ToggleButton.Toggled {
CheckButton check;
Window window;
private String title = "Check Button";
public GCheckButton() {
setTitle(title);
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setSizeRequest(250, 200);
showAll();
}
public void initUI() {
setBorderWidth(10);
Fixed fixed = new Fixed();
check = new CheckButton("Show title");
check.setActive(true);
check.connect(this);
check.setCanFocus(false);
fixed.put(check, 50, 50);
add(fixed);
setPosition(WindowPosition.CENTER);
}
public void onToggled(ToggleButton toggleButton) {
if (check.getActive()) {
setTitle(title);
} else {
setTitle("");
}
}
public static void main(String[] args) {
Gtk.init(args);
new GCheckButton();
Gtk.main();
}
}
根据CheckButton的状态,我们将在窗口的标题栏中显示标题。
check = new CheckButton("Show title");
CheckButton小部件已创建。
check.setActive(true);
默认情况下标题是可见的,因此我们默认情况下选中复选按钮。
if (check.getActive()) {
setTitle(title);
} else {
setTitle("");
}
根据CheckButton的状态,我们显示或隐藏窗口的标题。

图:CheckButton
TextComboBox
TextComboBox是一个小部件,允许用户从文本选项列表中进行选择。
textcombobox.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.ComboBox;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.TextComboBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows how to use
* a TextComboBox.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GTextComboBox extends Window implements ComboBox.Changed {
TextComboBox cb;
Label label;
public GTextComboBox() {
setTitle("TextComboBox");
initUI();
setSizeRequest(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
Fixed fixed = new Fixed();
label = new Label("");
fixed.put(label, 55, 130);
cb = new TextComboBox();
cb.appendText("Ubuntu");
cb.appendText("Mandriva");
cb.appendText("Fedora");
cb.appendText("Mint");
cb.appendText("Debian");
cb.appendText("Gentoo");
cb.connect(this);
fixed.put(cb, 50, 50);
add(fixed);
}
public void onChanged(ComboBox comboBox) {
String text = cb.getActiveText();
label.setLabel(text);
}
public static void main(String[] args) {
Gtk.init(args);
new GTextComboBox();
Gtk.main();
}
}
该示例显示了一个文本组合框和一个标签。 文本组合框具有六个选项的列表。 这些是 Linux Distros 的名称。 标签窗口小部件显示了从文本组合框中选择的选项。
cb = new TextComboBox();
TextComboBox小部件已创建。
cb.appendText("Ubuntu");
调用appendText()方法来填充文本组合框。
public void onChanged(ComboBox comboBox) {
String text = cb.getActiveText();
label.setLabel(text);
}
当我们从文本组合框中选择一个选项时,将调用onChanged()方法。 我们获取选定的文本并将其设置为标签小部件。

图:TextComboBox
Image
下一个示例介绍Image小部件。 此小部件显示图片。
image.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Image;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows an image of a castle
* in the window.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GImage extends Window {
public GImage() {
setTitle("Red Rock");
initUI();
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
Image image = new Image("redrock.png");
int width = image.getRequisition().getWidth();
int height = image.getRequisition().getHeight();
setSizeRequest(width, height);
add(image);
setBorderWidth(2);
}
public static void main(String[] args) {
Gtk.init(args);
new GImage();
Gtk.main();
}
}
我们在窗口中显示红色岩石城堡。
Image image = new Image("redrock.png");
我们创建Image小部件的实例。 请注意,我们不会捕获任何异常。 图像小部件已经可以处理它们。 如果“图像”小部件找不到redrock.png图像,则默认情况下将显示丢失的图像。
Image image = new Image(castle);
Add(image);
Image小部件已创建并添加到窗口。

图:图像
在本章中,我们展示了 Java Gnome 编程库的第一组基本小部件。
Java Gnome 中的小部件 II
在 Java Gnome 编程教程的这一部分中,我们继续介绍小部件。
Entry
Entry是单行文本输入字段。 该小部件用于输入文本数据。
entry.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Editable;
import org.gnome.gtk.Entry;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows how to use
* an Entry widget.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GEntry extends Window {
private Label label;
private Entry entry;
public GEntry() {
setTitle("Entry");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
label = new Label("...");
entry = new Entry();
entry.connect(new Entry.Changed() {
public void onChanged(Editable editable) {
label.setLabel(entry.getText());
}
});
Fixed fix = new Fixed();
fix.put(entry, 60, 100);
fix.put(label, 60, 40);
add(fix);
}
public static void main(String[] args) {
Gtk.init(args);
new GEntry();
Gtk.main();
}
}
此示例显示了条目小部件和标签。 我们输入的文本将立即显示在标签控件中。
entry = new Entry();
Entry小部件已创建。
entry.connect(new Entry.Changed() {
public void onChanged(Editable editable) {
label.setLabel(entry.getText());
}
});
如果Entry小部件中的文本被更改,我们将调用onChanged()方法。 在这种方法中,我们从Entry小部件中获取文本并将其设置为标签。

图:Entry小部件
HScale
Scale是一个小部件,可让用户通过在有限间隔内滑动旋钮以图形方式选择一个值。 我们的示例将显示音量控制。
hscale.java
package com.zetcode;
import java.io.FileNotFoundException;
import org.gnome.gdk.Event;
import org.gnome.gdk.Pixbuf;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.HScale;
import org.gnome.gtk.Image;
import org.gnome.gtk.Range;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* Java Gnome tutorial
*
* This program shows how to use
* the HScale widget. It implements a
* volume control.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GHScale extends Window {
private HScale hscale;
private Image image;
private Pixbuf mute;
private Pixbuf min;
private Pixbuf med;
private Pixbuf max;
public GHScale() {
setTitle("HScale");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(260, 120);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
loadImages();
hscale = new HScale(0, 99, 1);
hscale.setSizeRequest(130, 45);
hscale.setCanFocus(false);
image = new Image("mute.png");
hscale.connect(new HScale.ValueChanged() {
public void onValueChanged(Range range) {
int pos = (int) hscale.getValue();
if (pos == 0) {
image.setImage(mute);
} else if (pos > 0 && pos <= 30) {
image.setImage(min);
} else if (pos > 30 && pos < 80) {
image.setImage(med);
} else {
image.setImage(max);
}
}
});
Fixed fixed = new Fixed();
fixed.put(hscale, 40, 20);
fixed.put(image, 220, 40);
add(fixed);
}
private void loadImages() {
try {
mute = new Pixbuf("mute.png");
min = new Pixbuf("min.png");
med = new Pixbuf("med.png");
max = new Pixbuf("max.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Gtk.init(args);
new GHScale();
Gtk.main();
}
}
在上面的示例中,我们有HScale和Image小部件。 通过拖动比例尺,我们可以在Image小部件上更改图像。
hscale = new HScale(0, 99, 1);
HScale小部件已创建。 参数是下边界,上边界和阶跃。
int pos = (int) hscale.getValue();
在onValueChanged()方法中,我们获得了比例小部件的值。
if (pos == 0) {
image.setImage(mute);
} else if (pos > 0 && pos <= 30) {
image.setImage(min);
} else if (pos > 30 && pos < 80) {
image.setImage(med);
} else {
image.setImage(max);
}
根据获得的值,我们在图像小部件中更改图片。

图:HScale小部件
ToggleButton
ToggleButton是具有两种状态的按钮。 已按下但未按下。 通过单击可以在这两种状态之间切换。 在某些情况下此功能非常合适。
togglebuttons.java
package com.zetcode;
import org.gnome.gdk.Color;
import org.gnome.gdk.Event;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.StateType;
import org.gnome.gtk.ToggleButton;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the ToggleButton
* widget. Three toggle buttons control the
* color of a drawing area.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GToggleButton extends Window
implements ToggleButton.Toggled {
private ToggleButton tb1;
private ToggleButton tb2;
private ToggleButton tb3;
private DrawingArea darea;
private Color color;
public GToggleButton() {
setTitle("ToggleButton");
initUI();
setPosition(WindowPosition.CENTER);
setSizeRequest(350, 220);
showAll();
}
public void initUI() {
color = new Color(0, 0, 0);
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
Fixed fixed = new Fixed();
tb1 = new ToggleButton("Red");
tb1.setSizeRequest(80, 35);
tb1.connect(this);
tb2 = new ToggleButton("Green");
tb2.setSizeRequest(80, 35);
tb2.connect(this);
tb3 = new ToggleButton("Blue");
tb3.setSizeRequest(80, 35);
tb3.connect(this);
darea = new DrawingArea();
darea.modifyBackground(StateType.NORMAL, Color.BLACK);
darea.setSizeRequest(150, 150);
fixed.put(tb1, 20, 20);
fixed.put(tb2, 20, 65);
fixed.put(tb3, 20, 110);
fixed.put(darea, 150, 20);
add(fixed);
}
public void onToggled(ToggleButton toggleButton) {
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
if ("Red".equals(toggleButton.getLabel())) {
if (toggleButton.getActive()) {
red = 65535;
} else {
red = 0;
}
}
if ("Green".equals(toggleButton.getLabel())) {
if (toggleButton.getActive()) {
green = 65535;
} else {
green = 0;
}
}
if ("Blue".equals(toggleButton.getLabel())) {
if (toggleButton.getActive()) {
blue = 65535;
} else {
blue = 0;
}
}
color = new Color(red, green, blue);
darea.modifyBackground(StateType.NORMAL, color);
}
public static void main(String[] args) {
Gtk.init(args);
new GToggleButton();
Gtk.main();
}
}
在我们的示例中,我们显示了三个切换按钮和一个DrawingArea。 我们将区域的背景色设置为黑色。 切换按钮将切换颜色值的红色,绿色和蓝色部分。 背景颜色取决于我们按下的切换按钮。
color = new Color(0, 0, 0);
这是将使用切换按钮更新的颜色值。
tb1 = new ToggleButton("Red");
tb1.setSizeRequest(80, 35);
tb1.connect(this);
ToggleButton小部件已创建。 我们将其大小设置为80x35像素。 它连接到onToggled()方法。
darea = new DrawingArea();
darea.modifyBackground(StateType.NORMAL, Color.BLACK);
darea.setSizeRequest(150, 150);
DrawingArea小部件是显示颜色的小部件,由切换按钮混合。 开始时,它显示为黑色。
if ("Red".equals(toggleButton.getLabel())) {
if (toggleButton.getActive()) {
red = 65535;
} else {
red = 0;
}
}
我们根据红色切换按钮的状态更新颜色的红色部分。
color = new Color(red, green, blue);
darea.modifyBackground(StateType.NORMAL, color);
颜色值将更新并设置为DrawingArea小部件。

图:ToggleButton widget
Calendar
我们最终的窗口小部件是Calendar小部件。 它用于处理日期。
calendar.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Calendar;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the Calendar
* widget. We show a selected date in a
* label widget.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GCalendar extends Window {
private Calendar calendar;
private Label label;
public GCalendar() {
setTitle("Calendar");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(260, 120);
setPosition(WindowPosition.CENTER);
showAll();
}
private void initUI() {
VBox vbox = new VBox(false, 1);
calendar = new Calendar();
label = new Label(getDate());
calendar.connect(new Calendar.DaySelected() {
public void onDaySelected(Calendar calendar) {
label.setLabel(getDate());
}
});
vbox.add(calendar);
label.setSizeRequest(-1, 50);
vbox.add(label);
add(vbox);
}
private String getDate() {
int year = calendar.getDateYear();
int month = calendar.getDateMonth();
int day = calendar.getDateDay();
String dateLabel = month + "/" + day + "/" + year;
return dateLabel;
}
public static void main(String[] args) {
Gtk.init(args);
new GCalendar();
Gtk.main();
}
}
我们有Calendar小部件和Label。 从日历中选择的日期显示在标签中。
calendar = new Calendar();
Calendar小部件已创建。
public void onDaySelected(Calendar calendar) {
label.setLabel(getDate());
};
在onDaySelected()方法中,我们将标签更新为当前选择的日期。

图:日历
在本章中,我们结束了 Java Gnome 中的小部件的讨论。
Java Gnome 中的高级小部件
在 Java Gnome 编程教程的这一部分中,我们将介绍一些更高级的小部件。
TexView
TexView小部件用于显示和编辑多行文本。 TextView小部件也具有 MVC 设计。 TextView 代表视图组件,TexBuffer代表模型组件。 TextBuffer用于处理文本数据。 TextTag是可以应用于文本的属性。 TextIter表示文本中两个字符之间的位置。 所有使用文本的操作都是使用文本迭代器完成的。
textview.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.TextBuffer;
import org.gnome.gtk.TextIter;
import org.gnome.gtk.TextTag;
import org.gnome.gtk.TextView;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
import org.gnome.gtk.WrapMode;
import org.gnome.pango.FontDescription;
import org.gnome.pango.Style;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the
* TextView widget.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GTextView extends Window {
public GTextView() {
setTitle("TextView");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
final TextView view;
final FontDescription desc;
final TextBuffer buffer;
final TextTag colorText, italics, background, mleft;
TextIter pointer;
view = new TextView();
desc = new FontDescription("Sans, 10");
view.modifyFont(desc);
buffer = new TextBuffer();
colorText = new TextTag();
colorText.setForeground("blue");
italics = new TextTag();
italics.setStyle(Style.ITALIC);
background = new TextTag();
background.setBackground("lightgray");
mleft = new TextTag();
mleft.setLeftMargin(8);
pointer = buffer.getIterStart();
buffer.insert(pointer, "Plain text \n");
buffer.insert(pointer, "Colored text\n", new TextTag[] {colorText, mleft});
buffer.insert(pointer, "Text with colored background\n", background);
buffer.insert(pointer, "Text in italics", italics);
view.setBuffer(buffer);
view.setPaddingAboveParagraph(5);
add(view);
}
public static void main(String[] args) {
Gtk.init(args);
new GTextView();
Gtk.main();
}
}
该示例显示了一些应用了不同TextTags的文本。
view = new TextView();
TextView 小部件已创建。
desc = new FontDescription("Sans, 10");
view.modifyFont(desc);
我们将TextView的字体设置为 Sans 10。
colorText = new TextTag();
colorText.setForeground("blue");
TextTag小部件已创建。 如果应用,此标签会将文本的颜色设置为蓝色。
buffer.insert(pointer, "Text in italics", italics);
在这里,我们为指定的文本应用斜体文本标签。

图:TextView
ListView
在下面的示例中,我们在列表视图模式下使用TreeView小部件。 ListStore用于存储数据。
listview.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.CellRendererText;
import org.gnome.gtk.DataColumn;
import org.gnome.gtk.DataColumnString;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.ListStore;
import org.gnome.gtk.Statusbar;
import org.gnome.gtk.TreeIter;
import org.gnome.gtk.TreePath;
import org.gnome.gtk.TreeView;
import org.gnome.gtk.TreeViewColumn;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the
* TreeView widget in listview mode.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GListView extends Window {
private Actress[] actresses =
{
new Actress("Jessica Alba", "Pomona", 1981),
new Actress("Sigourney Weaver", "New York", 1949),
new Actress("Angelina Jolie", "Los Angeles", 1975),
new Actress("Natalie Portman", "Jerusalem", 1981),
new Actress("Rachel Weissz", "London", 1971),
new Actress("Scarlett Johansson", "New York", 1984)
};
private Statusbar statusbar;
public GListView() {
setTitle("ListView");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(350, 300);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
final TreeView view;
final ListStore model;
TreeIter row;
CellRendererText renderer;
TreeViewColumn column;
final DataColumnString nameCol;
final DataColumnString placeCol;
final DataColumnString yearCol;
statusbar = new Statusbar();
model = new ListStore(new DataColumn[] {
nameCol = new DataColumnString(),
placeCol = new DataColumnString(),
yearCol = new DataColumnString(),
});
for (Actress act : actresses) {
row = model.appendRow();
model.setValue(row, nameCol, act.name);
model.setValue(row, placeCol, act.place);
model.setValue(row, yearCol, String.valueOf(act.year));
}
view = new TreeView(model);
column = view.appendColumn();
column.setTitle("Name");
renderer = new CellRendererText(column);
renderer.setText(nameCol);
column = view.appendColumn();
column.setTitle("Place");
renderer = new CellRendererText(column);
renderer.setText(placeCol);
column = view.appendColumn();
column.setTitle("Year");
renderer = new CellRendererText(column);
renderer.setText(yearCol);
view.connect(new TreeView.RowActivated() {
public void onRowActivated(TreeView treeView,
TreePath treePath,
TreeViewColumn treeViewColumn) {
final TreeIter row;
final String place;
final String name;
final String year;
final String text;
row = model.getIter(treePath);
place = model.getValue(row, placeCol);
name = model.getValue(row, nameCol);
year = model.getValue(row, yearCol);
text = name + ", " + place + ", " + year;
statusbar.setMessage(text);
}
});
VBox vbox = new VBox(false, 0);
vbox.packStart(view);
vbox.packStart(statusbar, false, false, 0);
add(vbox);
}
class Actress {
public String name;
public String place;
public int year;
Actress(String name, String place, int year) {
this.name = name;
this.place = place;
this.year = year;
}
}
public static void main(String[] args) {
Gtk.init(args);
new GListView();
Gtk.main();
}
}
在我们的示例中,我们在TreeView小部件中显示了六个女演员的列表。 每行分别显示名称,出生地和出生年份。
class Actress {
public String name;
public String place;
public int year;
...
}
Actress类用于存储有关女演员的数据。
model = new ListStore(new DataColumn[] {
nameCol = new DataColumnString(),
placeCol = new DataColumnString(),
yearCol = new DataColumnString(),
});
这些行声明模型具有三个字符串数据列。
for (Actress act : actresses) {
row = model.appendRow();
model.setValue(row, nameCol, act.name);
model.setValue(row, placeCol, act.place);
model.setValue(row, yearCol, String.valueOf(act.year));
}
该模型充满了女演员的数据。
column = view.appendColumn();
column.setTitle("Name");
renderer = new CellRendererText(column);
renderer.setText(nameCol);
将创建一个带有"Name"标题的新列。 每列都有一个单元格渲染器。 单元格渲染器负责显示数据。 在我们的情况下,我们有最常用的单元格渲染器CellRendererText。 顾名思义,它在TreeViewColumn中显示文本数据。
row = model.getIter(treePath);
place = model.getValue(row, placeCol);
name = model.getValue(row, nameCol);
year = model.getValue(row, yearCol);
text = name + ", " + place + ", " + year;
statusbar.setMessage(text);
如果双击某个项目,则会在状态栏中显示整行。

图:ListView
TreeView
在本章的最后一个示例中,我们使用TreeView小部件显示分层的数据树。
tree.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.CellRendererText;
import org.gnome.gtk.DataColumn;
import org.gnome.gtk.DataColumnString;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Statusbar;
import org.gnome.gtk.TreeIter;
import org.gnome.gtk.TreePath;
import org.gnome.gtk.TreeStore;
import org.gnome.gtk.TreeView;
import org.gnome.gtk.TreeViewColumn;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program demonstrates the
* TreeView widget in tree view mode.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GTree extends Window {
private DataColumnString dataCol;
private TreeStore model;
private TreeIter row;
private TreeView view;
private TreeIter rowChild;
private TreeViewColumn column;
private CellRendererText renderer;
private Statusbar statusbar;
public GTree() {
setTitle("GTree");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(350, 300);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
statusbar = new Statusbar();
model = new TreeStore(new DataColumn[] {
dataCol = new DataColumnString(),
});
row = model.appendRow();
model.setValue(row, dataCol, "Scripting languages");
rowChild = model.appendChild( row );
model.setValue(rowChild, dataCol, "PHP");
rowChild = model.appendChild( row );
model.setValue(rowChild, dataCol, "Python");
rowChild = model.appendChild( row );
model.setValue(rowChild, dataCol, "PERL");
rowChild = model.appendChild( row );
model.setValue(rowChild, dataCol, "Ruby");
row = model.appendRow();
model.setValue(row, dataCol, "Compiling languages");
rowChild = model.appendChild( row );
model.setValue( rowChild, dataCol, "Java");
rowChild = model.appendChild( row );
model.setValue( rowChild, dataCol, "C++");
rowChild = model.appendChild( row );
model.setValue( rowChild, dataCol, "C#");
rowChild = model.appendChild( row );
model.setValue( rowChild, dataCol, "C");
view = new TreeView(model);
column = view.appendColumn();
column.setTitle("Programming Languages");
renderer = new CellRendererText(column);
renderer.setText(dataCol);
view.connect(new TreeView.RowActivated() {
public void onRowActivated(TreeView treeView,
TreePath treePath,
TreeViewColumn treeViewColumn) {
final TreeIter row;
final String text;
row = model.getIter(treePath);
text = model.getValue(row, dataCol);
statusbar.setMessage(text);
}
});
VBox vbox = new VBox(false, 0);
vbox.packStart(view);
vbox.packStart(statusbar, false, false, 0);
add(vbox);
}
public static void main(String[] args) {
Gtk.init(args);
new GTree();
Gtk.main();
}
}
这次我们使用TreeView小部件显示分层数据。
model = new TreeStore(new DataColumn[] {
dataCol = new DataColumnString(),
});
在这里,我们声明TreeStore具有一个字符串数据列。
model.setValue(row, dataCol, "Scripting languages");
我们附加一个顶级节点。
rowChild = model.appendChild( row );
model.setValue(rowChild, dataCol, "PHP");
我们为顶层节点附加一个子行。
view = new TreeView(model);
我们创建TreeView小部件的实例。
column = view.appendColumn();
column.setTitle("Programming Languages");
renderer = new CellRendererText(column);
renderer.setText(dataCol);
我们创建列和单元格渲染器。
row = model.getIter(treePath);
text = model.getValue(row, dataCol);
statusbar.setMessage(text);
双击后,我们将在状态栏中显示选定的节点。

图:Tree
在本章中,我们讨论的是高级 Java Gnome 小部件。
Java Gnome 中的对话框
在 Java Gnome 编程教程的这一部分中,我们将介绍对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
MessageDialog
消息对话框是方便的对话框,可向应用的用户提供消息。 该消息包含文本和图像数据。
messages.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Button;
import org.gnome.gtk.ButtonsType;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.MessageDialog;
import org.gnome.gtk.MessageType;
import org.gnome.gtk.Table;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* Java Gnome tutorial
*
* This program shows four
* message dialogs.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GMessages extends Window {
Window parent;
public GMessages() {
setTitle("GMessages");
initUI();
parent = this;
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 100);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
Table table = new Table(2, 2, true);
Button info = new Button("Information");
Button warn = new Button("Warning");
Button ques = new Button("Question");
Button erro = new Button("Error");
info.connect(new Button.Clicked() {
public void onClicked(Button button) {
MessageDialog md = new MessageDialog(null, true,
MessageType.INFO,
ButtonsType.CLOSE, "Download completed");
md.setPosition(WindowPosition.CENTER);
md.run();
md.hide();
}
});
warn.connect(new Button.Clicked() {
public void onClicked(Button button) {
MessageDialog md = new MessageDialog(parent, true,
MessageType.WARNING,
ButtonsType.CLOSE, "Unallowed operation");
md.setPosition(WindowPosition.CENTER);
md.run();
md.hide();
}
});
ques.connect(new Button.Clicked() {
public void onClicked(Button button) {
MessageDialog md = new MessageDialog(null, true,
MessageType.QUESTION,
ButtonsType.CLOSE, "Are you sure to quit?");
md.setPosition(WindowPosition.CENTER);
md.run();
md.hide();
}
});
erro.connect(new Button.Clicked() {
public void onClicked(Button button) {
MessageDialog md = new MessageDialog (null, true,
MessageType.ERROR,
ButtonsType.CLOSE, "Error loading file");
md.setPosition(WindowPosition.CENTER);
md.run();
md.hide();
}
});
table.attach(info, 0, 1, 0, 1);
table.attach(warn, 1, 2, 0, 1);
table.attach(ques, 0, 1, 1, 2);
table.attach(erro, 1, 2, 1, 2);
add(table);
}
public static void main(String[] args) {
Gtk.init(args);
new GMessages();
Gtk.main();
}
}
在我们的示例中,我们将显示四种消息对话框。 信息,警告,问题和错误消息对话框。
Button info = new Button("Information");
Button warn = new Button("Warning");
Button ques = new Button("Question");
Button erro = new Button("Error");
我们有四个按钮。 这些按钮中的每个按钮都会显示不同类型的消息对话框。
info.connect(new Button.Clicked() {
public void onClicked(Button button) {
MessageDialog md = new MessageDialog(null, true,
MessageType.INFO,
ButtonsType.CLOSE, "Download completed");
md.setPosition(WindowPosition.CENTER);
md.run();
md.hide();
}
});
如果单击信息按钮,将显示“信息”对话框。 MessageType.INFO指定对话框的类型。 ButtonsType.CLOSE指定要在对话框中显示的按钮。 最后一个参数是已分发的消息。 该对话框使用run()方法显示。 hide()方法隐藏对话框。




AboutDialog
AboutDialog显示有关应用的信息。 AboutDialog可以显示徽标,应用名称,版本,版权或许可信息。 也可以给作者或翻译者以荣誉。
aboutdialog.java
package com.zetcode;
import java.io.FileNotFoundException;
import org.gnome.gdk.Event;
import org.gnome.gdk.Pixbuf;
import org.gnome.gtk.AboutDialog;
import org.gnome.gtk.Button;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
public class GAboutDialog extends Window implements Button.Clicked {
Pixbuf logo;
public GAboutDialog() {
setTitle("AboutDialog");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
try {
logo = new Pixbuf("battery.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Button button = new Button("About");
button.connect(this);
Fixed fix = new Fixed();
fix.put(button, 20, 20);
add(fix);
}
public void onClicked(Button button) {
AboutDialog about = new AboutDialog();
about.setProgramName("Battery");
about.setVersion("0.1");
about.setCopyright("(c) Jan Bodnar");
about.setComments("Battery is a simple tool for battery checking");
about.setLogo(logo);
about.setPosition(WindowPosition.CENTER);
about.run();
about.hide();
}
public static void main(String[] args) {
Gtk.init(args);
new GAboutDialog();
Gtk.main();
}
}
该代码示例使用具有某些功能的AboutDialog。
AboutDialog about = new AboutDialog();
我们创建一个AboutDialog。
about.setProgramName("Battery");
about.setVersion("0.1");
about.setCopyright("(c) Jan Bodnar");
我们设置名称,版本和版权。
about.setLogo(logo);
此行创建徽标。

图:AboutDialog
在 Java Gnome 教程的这一部分中,我们介绍了对话框。
Java Gnome 中的 Pango
在 Java Gnome 编程教程的这一部分中,我们将探索 Pango 库。
Pango 是一个免费的开源计算库,可高质量呈现国际化文本。 可以使用不同的字体后端,从而允许跨平台支持。 (维基百科)
Pango 提供了用于Gdk和Gtk的高级字体和文本处理。
引用
在我们的第一个示例中,我们展示了如何更改Label小部件的字体。
quotes.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
import org.gnome.pango.FontDescription;
/**
* ZetCode Java Gnome tutorial
*
* This program uses the pango library to
* display text.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GQuotes extends Window {
public GQuotes() {
setTitle("Quotes");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
String text = "Excess of joy is harder to bear than any amount\n" +
"of sorrow. The more one judges, the less one loves.\n" +
"There is no such thing as a great talent without great will power.";
Label label = new Label(text);
FontDescription fontdesc = new FontDescription("Purisa 9");
label.modifyFont(fontdesc);
Fixed fix = new Fixed();
fix.put(label, 5, 5);
add(fix);
}
public static void main(String[] args) {
Gtk.init(args);
new GQuotes();
Gtk.main();
}
}
在上面的代码示例中,我们有一个带有三个引号的标签小部件。 我们将其字体更改为 Purisa 9。
String text = "Excess of joy is harder to bear than any amount\n" +
"of sorrow. The more one judges, the less one loves.\n" +
"There is no such thing as a great talent without great will power.";
这是要在标签中显示的文本。
FontDescription fontdesc = new FontDescription("Purisa 9");
FontDescription用于指定要加载的字体的特征。
label.modifyFont(fontdesc);
我们将标签小部件的字体更改为 Purisa 9。

图:Quotations
Pango 标记
在下面的示例中,我们将使用 Pango 标记语言来修改ExposeEvent事件中的字体。
markup.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.pango.FontDescription;
import org.gnome.pango.Layout;
/**
* ZetCode Java Gnome tutorial
*
* This program uses Pango markup language
* to modify the text.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GPangoMarkup extends Window implements Widget.ExposeEvent {
String quote = "<span foreground='blue' size='19000'>The only " +
"victory over love is flight</span>";
public GPangoMarkup() {
setTitle("Pango Markup");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
move(150, 150);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
darea.queueDraw();
add(darea);
setBorderWidth(5);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
final Layout layout;
final FontDescription desc;
cr = new Context(widget.getWindow());
layout = new Layout(cr);
desc = new FontDescription("Sans 12");
layout.setFontDescription(desc);
layout.setMarkup(quote);
widget.setSizeRequest(layout.getPixelWidth(), layout.getPixelHeight());
cr.showLayout(layout);
return false;
}
public static void main(String[] args) {
Gtk.init(args);
new GPangoMarkup();
Gtk.main();
}
}
使用 Cario 库进行绘制。 我们通过传递 Cario 绘图上下文来获得布局。
cr = new Context(widget.getWindow());
此代码行创建一个Context对象。
layout = new Layout(cr);
Layout代表文本的一段及其属性。
layout.setMarkup(quote);
这行设置布局的标记。
widget.setSizeRequest(layout.getPixelWidth(), layout.getPixelHeight());
这是为了显示窗口中的所有文本。
cr.showLayout(layout);
最后,绘制文本。

图:Pango 标记
Unicode
Pango 用于处理国际化文本。
unicode.java
package com.zetcode;
import org.gnome.gdk.Event;
import org.gnome.gtk.Fixed;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
import org.gnome.pango.FontDescription;
/**
* ZetCode Java Gnome tutorial
*
* This program shows text in
* azbuka.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GUnicode extends Window {
public GUnicode() {
setTitle("Unicode");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
String text =
"Фёдор Михайлович Достоевский родился 30 октября (11 ноября)\n" +
"1821 года в Москве.Был вторым из 7 детей. Отец, Михаил Андреевич,\n" +
"работал в госпитале для бедных. Мать, Мария Фёдоровна\n" +
"(в девичестве Нечаева), происходила из купеческого рода.";
Label label = new Label(text);
FontDescription fontdesc = new FontDescription("Purisa 9");
label.modifyFont(fontdesc);
Fixed fix = new Fixed();
fix.put(label, 5, 5);
add(fix);
setBorderWidth(5);
}
public static void main(String[] args) {
Gtk.init(args);
new GUnicode();
Gtk.main();
}
}
我们在西里尔字母中显示一些文本。
String text =
"Фёдор Михайлович Достоевский родился 30 октября (11 ноября)\n" ...
我们可以直接使用 unicode 文本。 但是,文本永远不会放在源代码中。 在实际的文字应用中,我们将文本放在外部文件中。 在 Java 中,这些通常是属性文件。
Label label = new Label(text);
我们通常在标签小部件中使用它。

图:Unicode
在 Java Gnome 编程教程的这一章中,我们使用了 Pango 库。
在 Java Gnome 中用 Cairo 绘图
在 Java 编程教程的这一部分中,我们将使用 Cairo 库进行一些绘制。
Cairo 是用于创建 2D 矢量图形的库。 我们可以使用它来绘制自己的小部件,图表或各种效果或动画。
简单绘图
描边操作绘制形状的轮廓,填充操作填充形状的内部。 接下来,我们将演示这两个操作。
simpledrawing.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.Allocation;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* Java Gnome tutorial
*
* This program draws a simple
* drawing with the Cairo library.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GSimpleDrawing extends Window implements Widget.ExposeEvent {
public GSimpleDrawing() {
setTitle("Simple drawing");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(250, 200);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
add(darea);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
cr = new Context(widget.getWindow());
drawShape(cr);
return false;
}
public void drawShape(Context cr) {
cr.setLineWidth(9);
cr.setSource(0.7, 0.2, 0.0);
int width, height;
width = getAllocation().getWidth();
height = getAllocation().getHeight();
cr.translate(width/2, height/2);
cr.arc(0, 0, (width < height ? width : height) / 2 - 10, 0, 2*Math.PI);
cr.strokePreserve();
cr.setSource(0.3, 0.4, 0.6);
cr.fill();
}
public static void main(String[] args) {
Gtk.init(args);
new GSimpleDrawing();
Gtk.main();
}
}
在我们的示例中,我们将绘制一个圆并用纯色填充它。
DrawingArea darea = new DrawingArea();
我们将在DrawingArea小部件上进行绘制操作。
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
cr = new Context(widget.getWindow());
drawShape(cr);
return false;
}
当需要重绘窗口时,将创建ExposeEvent。 实际图形委托给drawShape()方法。
cr = new Context(widget.getWindow());
我们从绘图区域的 gdk 窗口创建Context对象。 上下文是我们绘制所有图纸的对象。
cr.setLineWidth(9);
我们将线条的宽度设置为 9 像素。
cr.setSource(0.7, 0.2, 0.0);
我们将颜色设置为深红色。
int width, height;
width = getAllocation().getWidth();
height = getAllocation().getHeight();
cr.translate(width/2, height/2);
我们得到绘图区域的宽度和高度。 我们将原点移动到窗口的中间。
cr.arc(0, 0, (width < height ? width : height) / 2 - 10, 0, 2*Math.PI);
cr.strokePreserve();
我们绘制一个圆形的外部形状。 strokePreserve()根据当前的线宽,线连接,线帽和笔划线设置描边当前路径。 与stroke()不同,它在 cairo 上下文中保留路径。
cr.setSource(0.3, 0.4, 0.6);
cr.fill();
这会用一些蓝色填充圆圈的内部。

图:简单 drawing
基本形状
下一个示例将一些基本形状绘制到窗口上。
basicshapes.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.freedesktop.cairo.Matrix;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program draws basic shapes
* with the cairo library.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GBasicShapes extends Window implements Window.ExposeEvent {
public GBasicShapes() {
setTitle("Basic Shapes");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(390, 240);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
add(darea);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
cr = new Context(widget.getWindow());
drawShapes(cr);
return false;
}
public void drawShapes(Context cr) {
cr.setSource(0.6, 0.6, 0.6);
cr.rectangle(20, 20, 120, 80);
cr.rectangle(180, 20, 80, 80);
cr.fill();
cr.arc(330, 60, 40, 0, 2*Math.PI);
cr.fill();
cr.arc(90, 160, 40, Math.PI/4, Math.PI);
cr.fill();
Matrix mat = new Matrix();
mat.translate(220, 180);
mat.scale(1, 0.7);
cr.transform(mat);
cr.arc(0, 0, 50, 0, 2*Math.PI);
cr.fill();
}
public static void main(String[] args) {
Gtk.init(args);
new GBasicShapes();
Gtk.main();
}
}
在此示例中,我们将创建一个矩形,正方形,圆形,弧形和椭圆形。
cr.rectangle(20, 20, 120, 80);
cr.rectangle(180, 20, 80, 80);
cr.fill();
这些线绘制一个矩形和一个正方形。
cr.arc(330, 60, 40, 0, 2*Math.PI);
cr.fill();
此处arc()方法绘制一个完整的圆。
Matrix mat = new Matrix();
mat.translate(220, 180);
mat.scale(1, 0.7);
cr.transform(mat);
cr.arc(0, 0, 50, 0, 2*Math.PI);
cr.fill();
如果要绘制椭圆形,请先进行一些缩放。 scale()方法缩小 y 轴。

图:基本形状
色彩
颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 Cario 有效 RGB 值在 0 到 1 的范围内。
colors.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program draws nine rectangles
* on the drawing area. Each of them
* has different color.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GColors extends Window
implements Widget.ExposeEvent {
public GColors() {
setTitle("Colors");
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
initUI();
setDefaultSize(350, 280);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
add(darea);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
cr = new Context(widget.getWindow());
drawRectangles(cr);
return false;
}
public void drawRectangles(Context cr) {
cr.setSource(0.5, 0.65, 0.45);
cr.rectangle(10, 15, 90, 60);
cr.fill();
cr.setSource(0.16, 0.7, 0.9);
cr.rectangle(130, 15, 90, 60);
cr.fill();
cr.setSource(0.274, 0.262, 0.48);
cr.rectangle(250, 15, 90, 60);
cr.fill();
cr.setSource(0.5, 0.39, 0.33);
cr.rectangle(10, 105, 90, 60);
cr.fill();
cr.setSource(0.99, 0.83, 0.24);
cr.rectangle(130, 105, 90, 60);
cr.fill();
cr.setSource(0.95, 0.38, 0.27);
cr.rectangle(250, 105, 90, 60);
cr.fill();
cr.setSource(0.85, 0.57, 0.21);
cr.rectangle(10, 195, 90, 60);
cr.fill();
cr.setSource(0.25, 0.04, 0.73);
cr.rectangle(130, 195, 90, 60);
cr.fill();
cr.setSource(0.12, 0.08, 0.03);
cr.rectangle(250, 195, 90, 60);
cr.fill();
}
public static void main(String[] args) {
Gtk.init(args);
new GColors();
Gtk.main();
}
}
我们用 9 种不同的颜色绘制 9 个矩形。
cr.setSource(0.5, 0.65, 0.45);
setSource()方法为 Cario 上下文设置颜色。 该方法的三个参数是颜色强度值。
cr.rectangle(10, 15, 90, 60);
cr.fill();
我们创建一个矩形形状,并用先前指定的颜色填充它。

图:颜色
透明矩形
透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)
transparentrectangles.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program draws ten rectangles
* with different levels of transparency.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GTransparentRectangles extends Window
implements Widget.ExposeEvent {
public GTransparentRectangles() {
setTitle("Transparent Rectangles");
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
initUI();
setSizeRequest(590, 90);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
add(darea);
darea.connect(this);
}
public void doDrawing(Context cr) {
for (int i = 1; i<=10; i++) {
cr.setSource(0, 0, 1, 0.1*i);
cr.rectangle(50*i, 20, 40, 40);
cr.fill();
}
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
Context cr = new Context(widget.getWindow());
doDrawing(cr);
return false;
}
public static void main(String[] args) {
Gtk.init(args);
new GTransparentRectangles();
Gtk.main();
}
}
在示例中,我们将绘制十个具有不同透明度级别的矩形。
cr.setSource(0, 0, 1, 0.1*i);
setSource()方法的最后一个参数是 alpha 透明度。

图:透明矩形
在 Java Gnome 编程库的这一章中,我们使用 Cairo 库进行绘图。
Cario 绘图 II
在 Java Gnome 编程教程的这一部分中,我们将继续使用 Cairo 库进行绘制。
甜甜圈
在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。
donut.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.freedesktop.cairo.Matrix;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program draws a Donut
* shape on the drawing area.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GDonut extends Window
implements Widget.ExposeEvent {
public GDonut() {
setTitle("Donut");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(300, 260);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
add(darea);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
cr = new Context(widget.getWindow());
drawDonut(cr);
return false;
}
public void drawDonut(Context cr) {
int width = this.getWindow().getWidth();
int height = this.getWindow().getHeight();
cr.setLineWidth(0.5);
cr.translate(width/2, height/2);
cr.arc( 0, 0, 120, 0, 2 * Math.PI);
cr.stroke();
cr.save();
for ( int i = 0; i < 36; i++) {
Matrix mat = new Matrix();
mat.rotate(i*Math.PI/36);
mat.scale(0.3, 1);
cr.transform(mat);
cr.arc(0, 0, 120, 0, 2 * Math.PI);
cr.restore();
cr.stroke();
cr.save();
}
}
public static void main(String[] args) {
Gtk.init(args);
new GDonut();
Gtk.main();
}
}
在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。
cr.translate(width/2, height/2);
cr.arc( 0, 0, 120, 0, 2 * Math.PI);
cr.stroke();
刚开始时有一个椭圆。
Matrix mat = new Matrix();
mat.rotate(i*Math.PI/36);
mat.scale(0.3, 1);
cr.transform(mat);
cr.arc(0, 0, 120, 0, 2 * Math.PI);
cr.restore();
cr.stroke();
cr.save();
旋转几圈后,有一个甜甜圈。

图:多纳圈
渐变
在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers.com)
gradients.java
package com.zetcode;
import org.freedesktop.cairo.Context;
import org.freedesktop.cairo.LinearPattern;
import org.gnome.gdk.Color;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program draws gradients.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GGradients extends Window implements Widget.ExposeEvent {
public GGradients() {
setTitle("Gradients");
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Gtk.mainQuit();
return false;
}
});
setDefaultSize(340, 390);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
add(darea);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
final Context cr;
cr = new Context(widget.getWindow());
drawGradients(cr);
return false;
}
public void drawGradients(Context cr) {
LinearPattern lg1 = new LinearPattern(0.0, 0.0, 350.0, 350.0);
int count = 1;
for (double j=0.1; j<1.0; j+= 0.1) {
if (count % 2 != 0) {
lg1.addColorStopRGB(j, 0, 0, 0);
} else {
lg1.addColorStopRGB(j, 1, 0, 0);
}
count++;
}
cr.rectangle(20, 20, 300, 100);
cr.setSource(lg1);
cr.fill();
LinearPattern lg2 = new LinearPattern(0.0, 0.0, 350.0, 0);
count = 1;
for (double i=0.05; i<0.95; i+= 0.025) {
if (count % 2 != 0) {
lg2.addColorStopRGB(i, 0, 0, 0);
} else {
lg2.addColorStopRGB(i, 0, 0, 1);
}
count++;
}
cr.rectangle(20, 140, 300, 100);
cr.setSource(lg2);
cr.fill();
LinearPattern lg3 = new LinearPattern(20.0, 260.0, 20.0, 360.0);
lg3.addColorStopRGB(0.1, 0, 0, 0 );
lg3.addColorStopRGB(0.5, 1, 1, 0);
lg3.addColorStopRGB(0.9, 0, 0, 0 );
cr.rectangle(20, 260, 300, 100);
cr.setSource(lg3);
cr.fill();
}
public static void main(String[] args) {
Gtk.init(args);
new GGradients();
Gtk.main();
}
}
在我们的示例中,我们绘制了三个具有三个不同渐变的矩形。
LinearPattern lg1 = new LinearPattern(0.0, 0.0, 350.0, 350.0);
在这里,我们创建一个线性渐变图案。 参数指定直线,沿着该直线绘制渐变。 在我们的情况下,它是一条对角线。
LinearPattern lg3 = new LinearPattern(20.0, 260.0, 20.0, 360.0);
lg3.addColorStopRGB(0.1, 0, 0, 0 );
lg3.addColorStopRGB(0.5, 1, 1, 0);
lg3.addColorStopRGB(0.9, 0, 0, 0 );
我们定义色标以产生渐变图案。 在这种情况下,渐变是黑色和黄色的混合。 通过添加两个黑色和一个黄色色标,我们创建了一个水平渐变图案。 这些停止实际上是什么意思? 在我们的情况下,我们从黑色开始,该颜色将以大小的 1/10 停止。 然后,我们开始逐渐涂成黄色,最终达到形状的中心。 黄色停在大小的 9/10,我们再次开始用黑色绘图,直到结束。

图:渐变
星形
在下面的示例中,我们创建了一颗移动的星星。星星移动,旋转并成长/收缩。
star.java
package com.zetcode;
import java.util.Timer;
import java.util.TimerTask;
import org.freedesktop.cairo.Context;
import org.freedesktop.cairo.Matrix;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program shows an animated star. Rotate,
* translate and scale operations are
* applied on the star.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GStar extends Window
implements Widget.ExposeEvent {
private static Timer timer;
private int count;
private double angle = 0;
private double scale = 1;
private double delta = 0.01;
double points[][] = {
{ 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 },
{ 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 },
{ 40, 190 }, { 50, 125 }, { 0, 85 }
};
public GStar() {
setTitle("Star");
timer = new Timer();
timer.scheduleAtFixedRate(new ScheduleTask(), 100, 20);
count = 0;
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
timer.cancel();
Gtk.mainQuit();
return false;
}
});
setSizeRequest(350, 250);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
darea.connect(this);
add(darea);
}
public void drawStar(Context cr) {
int width = this.getWindow().getWidth();
int height = this.getWindow().getHeight();
cr.setSource(0, 0.44, 0.7);
cr.setLineWidth(1);
Matrix mat = new Matrix();
mat.translate(width/2, height/2);
mat.rotate(angle);
mat.scale(scale, scale);
cr.transform(mat);
for ( int i = 0; i < 10; i++ ) {
cr.lineTo(points[i][0], points[i][1]);
}
cr.fill();
cr.stroke();
if ( scale < 0.01 ) {
delta = -delta;
} else if (scale > 0.99) {
delta = -delta;
}
scale += delta;
angle += 0.01;
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
Context cr = new Context(widget.getWindow());
drawStar(cr);
return false;
}
class ScheduleTask extends TimerTask {
public void run() {
count++;
queueDraw();
}
}
public static void main(String[] args) {
Gtk.init(args);
new GStar();
Gtk.main();
}
}
我们对星形应用平移,缩放和旋转操作。
public boolean onDeleteEvent(Widget source, Event event) {
timer.cancel();
Gtk.mainQuit();
return false;
}
为了干净出口,我们一定不要忘记停止计时器。 Timer是 Java util库的对象,并且不被Gtk.mainQuit()方法停止。
double points[][] = {
{ 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 },
{ 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 },
{ 40, 190 }, { 50, 125 }, { 0, 85 }
};
这些点用于构建星形。
Matrix mat = new Matrix();
mat.translate(width/2, height/2);
mat.rotate(angle);
mat.scale(scale, scale);
cr.transform(mat);
在这里,我们对星形应用平移,旋转和缩放操作。
for ( int i = 0; i < 10; i++ ) {
cr.lineTo(points[i][0], points[i][1]);
}
在这里,我们画星星。
等待
在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一条线在移动的错觉。 这种效果通常用于通知用户,一项艰巨的任务正在幕后进行。 一个示例是通过互联网流式传输视频。
waiting.java
package com.zetcode;
import java.util.Timer;
import java.util.TimerTask;
import org.freedesktop.cairo.Context;
import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a waiting
* effect.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GWaiting extends Window
implements Widget.ExposeEvent {
private static Timer timer;
private int count;
private final double[][] trs = {
{ 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
{ 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
{ 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 },
{ 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 },
{ 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 },
{ 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 },
{ 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 },
{ 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, }
};
public GWaiting() {
setPosition(WindowPosition.CENTER);
timer = new Timer();
timer.scheduleAtFixedRate(new ScheduleTask(), 100, 80);
count = 0;
initUI();
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
timer.cancel();
Gtk.mainQuit();
return false;
}
});
resize(250, 150);
setTitle("Waiting");
showAll();
}
public void initUI() {
DrawingArea darea = new DrawingArea();
add(darea);
darea.connect(this);
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
Context cr = new Context(widget.getWindow());
drawWaiting(cr);
return false;
}
private void drawWaiting(Context cr) {
int w = this.getWidth();
int h = this.getHeight();
cr.translate(w/2, h/2);
for (int i = 0; i < 8; i++) {
cr.setLineWidth(3);
cr.setSource(0, 0, 0, trs[count%8][i]);
cr.moveTo(0, -10);
cr.lineTo(0, -40);
cr.rotate(Math.PI/4f);
cr.stroke();
}
}
class ScheduleTask extends TimerTask {
public void run() {
count++;
queueDraw();
}
}
public static void main(String[] args) {
Gtk.init(args);
new GWaiting();
Gtk.main();
}
}
我们用八个不同的 alpha 值绘制八条线。
private final double[][] trs = {
{ 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
....
};
这是此演示中使用的透明度值的二维数组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。
cr.setLineWidth(3);
我们使线条更粗一些,以便更好地显示它们。
cr.setSource(0, 0, 0, trs[count%8][i]);
在这里,我们定义了一条线的透明度值。
cr.moveTo(0, -10);
cr.lineTo(0, -40);
cr.rotate(Math.PI/4f);
cr.stroke();
这些代码行将绘制八行中的每行。

图:等待
在 Java Gnome 编程库的这一章中,我们使用 Cairo 库进行了一些更高级的绘制。
Java Gnome 中的贪食蛇
在 Java Gnome 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在状态栏小部件中显示"Game Over"消息。
board.java
package com.zetcode;
import java.io.FileNotFoundException;
import java.util.Timer;
import java.util.TimerTask;
import org.freedesktop.cairo.Context;
import org.gnome.gdk.Color;
import org.gnome.gdk.EventExpose;
import org.gnome.gdk.EventKey;
import org.gnome.gdk.Keyval;
import org.gnome.gdk.ModifierType;
import org.gnome.gdk.Pixbuf;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Justification;
import org.gnome.gtk.Label;
import org.gnome.gtk.StateType;
import org.gnome.gtk.Widget;
public class Board extends DrawingArea implements Widget.ExposeEvent {
private final int WIDTH = 300;
private final int HEIGHT = 300;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 900;
private final int RAND_POS = 29;
private final int DELAY = 140;
private final int PERIOD = 80;
private int x[] = new int[ALL_DOTS];
private int y[] = new int[ALL_DOTS];
private int dots;
private int apple_x;
private int apple_y;
private boolean left = false;
private boolean right = true;
private boolean up = false;
private boolean down = false;
private boolean inGame = true;
private Timer timer;
private Pixbuf dot;
private Pixbuf apple;
private Pixbuf head;
private Label statusbar;
public Board(Label statusbar) {
this.statusbar = statusbar;
connect(new SnakeKeyListener());
modifyBackground(StateType.NORMAL, Color.BLACK);
try {
dot = new Pixbuf("dot.png");
apple = new Pixbuf("apple.png");
head = new Pixbuf("head.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
connect(this);
setCanFocus(true);
initGame();
}
public Timer getTimer() { return timer; }
public void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z * 10;
y[z] = 50;
}
locateApple();
timer = new Timer();
timer.scheduleAtFixedRate(new ScheduleTask(), DELAY, PERIOD);
}
public void drawObjects(Context cr) {
if (inGame) {
cr.setSource(apple, apple_x, apple_y);
cr.paint();
for (int z = 0; z < dots; z++) {
if (z == 0) {
cr.setSource(head, x[z], y[z]);
cr.paint();
} else {
cr.setSource(dot, x[z], y[z]);
cr.paint();
}
}
} else {
gameOver();
}
}
public void gameOver() {
timer.cancel();
statusbar.setJustify(Justification.LEFT);
statusbar.setAlignment(0f, 0.5f);
statusbar.setLabel("Game Over");
}
public void checkApple() {
if ((x[0] == apple_x) && (y[0] == apple_y)) {
dots++;
locateApple();
}
}
public void move() {
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
if (left) {
x[0] -= DOT_SIZE;
}
if (right) {
x[0] += DOT_SIZE;
}
if (up) {
y[0] -= DOT_SIZE;
}
if (down) {
y[0] += DOT_SIZE;
}
}
public void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
if (y[0] > HEIGHT) {
inGame = false;
}
if (y[0] < 0) {
inGame = false;
}
if (x[0] > WIDTH) {
inGame = false;
}
if (x[0] < 0) {
inGame = false;
}
}
public void locateApple() {
int r = (int) (Math.random() * RAND_POS);
apple_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
apple_y = ((r * DOT_SIZE));
}
public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
Context cr = new Context(widget.getWindow());
drawObjects(cr);
return false;
}
class ScheduleTask extends TimerTask {
public void run() {
if (inGame) {
checkApple();
checkCollision();
move();
}
queueDraw();
}
}
class SnakeKeyListener implements Widget.KeyPressEvent {
public boolean onKeyPressEvent(Widget widget, EventKey eventKey) {
final Keyval key;
final ModifierType mod;
key = eventKey.getKeyval();
mod = eventKey.getState();
if ((key == key.Left) && (!right)) {
left = true;
up = false;
down = false;
}
if ((key == key.Right) && (!left)) {
right = true;
up = false;
down = false;
}
if ((key == key.Up) && (!down)) {
up = true;
right = false;
left = false;
}
if ((key == key.Down) && (!up)) {
down = true;
right = false;
left = false;
}
return false;
}
}
}
首先,我们将定义一些在游戏中使用的全局变量。
WIDTH和HEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。
private int x[] = new int[ALL_DOTS];
private int y[] = new int[ALL_DOTS];
这两个数组存储蛇的所有可能关节的 x,y 坐标。
initGame()方法初始化变量,加载图像并启动超时功能。
在move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
该代码将关节向上移动。
if (left) {
x[0] -= DOT_SIZE;
}
将头向左移动。
在checkCollision()方法中,我们确定蛇是否击中了自己或撞墙之一。
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
inGame = false;
}
}
如果蛇用头撞到关节之一,我们就结束游戏。
if (y[0] > HEIGHT) {
inGame = false;
}
如果蛇击中了棋盘的底部,我们就结束了游戏。
locateApple()方法在表格上随机定位一个苹果。
int r = (int) (Math.random() * RAND_POS);
我们得到一个从 0 到RAND_POS-1的随机数。
apple_x = ((r * DOT_SIZE));
...
apple_y = ((r * DOT_SIZE));
这些行设置了apple对象的 x,y 坐标。
在Board类的onKeyPressEvent()方法中,我们确定玩家按下了哪些键。
if ((key == key.Left) && (!right)) {
left = true;
up = false;
down = false;
}
如果单击左光标键,则将left变量设置为true。 在move()方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
nibbles.java
package com.zetcode;
import java.util.Timer;
import org.gnome.gdk.Event;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Label;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;
/**
* ZetCode Java Gnome tutorial
*
* This program creates a Nibbles game clone.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class GNibbles extends Window {
Board board;
Label statusbar;
public GNibbles() {
setTitle("Nibbles");
initUI();
setDefaultSize(320, 320);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI() {
VBox vbox = new VBox(false, 0);
statusbar = new Label("");
board = new Board(statusbar);
vbox.packStart(board);
vbox.packStart(statusbar, false, false, 0);
add(vbox);
connect(new Window.DeleteEvent() {
public boolean onDeleteEvent(Widget source, Event event) {
Timer timer = board.getTimer();
timer.cancel();
Gtk.mainQuit();
return false;
}
});
}
public static void main(String[] args) {
Gtk.init(args);
new GNibbles();
Gtk.main();
}
}
在这个类中,我们设置了贪食蛇游戏。 注意,我们从板上获得了计时器对象。 这是执行干净的退出。

图:贪食蛇
这是使用 Java Gnome 编程库编程的贪食蛇电脑游戏。
PyQt5 中的第一个程序
在 PyQt5 教程的这一部分中,我们学习一些基本功能。 这些示例显示了一个工具提示和一个图标,关闭了一个窗口,显示了一个消息框,并在桌面上将窗口居中。
简单的例子
这是一个显示小窗口的简单示例。 然而,我们可以利用这个窗口做很多事情。 我们可以调整大小,最大化或最小化它。 这需要大量的编码。 已经有人对该功能进行了编码。 由于它在大多数应用中都重复执行,因此无需再次编码。 PyQt5 是高级工具包。 如果我们使用较低级的工具箱进行编码,则下面的代码示例可能很容易包含数百行。
simple.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
In this example, we create a simple
window in PyQt5.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
import sys
from PyQt5.QtWidgets import QApplication, QWidget
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
上面的代码示例在屏幕上显示了一个小窗口。
import sys
from PyQt5.QtWidgets import QApplication, QWidget
在这里,我们提供必要的导入。 基本窗口小部件位于PyQt5.QtWidgets模块中。
app = QApplication(sys.argv)
每个 PyQt5 应用都必须创建一个应用对象。 sys.argv参数是命令行中的参数列表。 可以从外壳运行 Python 脚本。 这是我们可以控制脚本启动的方式。
w = QWidget()
QWidget小部件是 PyQt5 中所有用户界面对象的基类。 我们为QWidget提供了默认的构造器。 默认构造器没有父代。 没有父级的窗口小部件称为窗口。
w.resize(250, 150)
resize()方法调整窗口小部件的大小。 宽 250 像素,高 150 像素。
w.move(300, 300)
move()方法将窗口小部件移动到屏幕上x = 300,y = 300坐标的位置。
w.setWindowTitle('Simple')
我们用setWindowTitle()设置窗口的标题。 标题显示在标题栏中。
w.show()
show()方法在屏幕上显示小部件。 首先在内存中创建一个小部件,然后将其显示在屏幕上。
sys.exit(app.exec_())
最后,我们进入应用的主循环。 事件处理从这一点开始。 mainloop 从窗口系统接收事件,并将其分配给应用小部件。 如果调用exit()方法或主窗口小部件被销毁,则主循环结束。 sys.exit()方法可确保干净退出。 将告知环境应用如何结束。
exec_()方法带有下划线。 这是因为exec是 Python 关键字。 因此,使用了exec_()。

图:简单
应用图标
应用图标是一个小图像,通常显示在标题栏的左上角。 在下面的示例中,我们将展示如何在 PyQt5 中做到这一点。 我们还将介绍一些新方法。
某些环境在标题栏中不显示图标。 我们需要启用它们。 如果没有看到图标,请参阅我在 Stackoverflow 上的答案以获取解决方案。
icon.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This example shows an icon
in the titlebar of the window.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
前面的示例以过程样式编码。 Python 编程语言支持过程和面向对象的编程风格。 在 PyQt5 中编程意味着在 OOP 中编程。
class Example(QWidget):
def __init__(self):
super().__init__()
...
面向对象编程中的三个重要方面是类,数据和方法。 在这里,我们创建了一个名为Example的新类。 Example类继承自QWidget类。 这意味着我们调用了两个构造器:第一个构造器用于Example类,第二个构造器用于继承的类。 super()方法返回Example类的父对象,我们将其称为构造器。 __init__()方法是 Python 语言的构造方法。
self.initUI()
GUI 的创建委托给initUI()方法。
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
所有这三种方法都从QWidget类继承。 setGeometry()做两件事:在屏幕上找到窗口并设置其大小。 前两个参数是窗口的 x 和 y 位置。 第三个是窗口的宽度,第四个是窗口的高度。 实际上,它将resize()和move()方法结合在一起。 最后一种方法设置应用图标。 为此,我们创建了一个QIcon对象。 QIcon接收到要显示的图标的路径。
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
将创建应用和示例对象。 主循环开始。

图:图标
显示工具提示
我们可以为我们的任何小部件提供气球帮助。
tooltip.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This example shows a tooltip on
a window and a button.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,
QPushButton, QApplication)
from PyQt5.QtGui import QFont
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
QToolTip.setFont(QFont('SansSerif', 10))
self.setToolTip('This is a <b>QWidget</b> widget')
btn = QPushButton('Button', self)
btn.setToolTip('This is a <b>QPushButton</b> widget')
btn.resize(btn.sizeHint())
btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Tooltips')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
在此示例中,我们显示了两个 PyQt5 小部件的工具提示。
QToolTip.setFont(QFont('SansSerif', 10))
此静态方法设置用于呈现工具提示的字体。 我们使用 10pt SansSerif 字体。
self.setToolTip('This is a <b>QWidget</b> widget')
要创建工具提示,我们调用setTooltip()方法。 我们可以使用富文本格式。
btn = QPushButton('Button', self)
btn.setToolTip('This is a <b>QPushButton</b> widget')
我们创建一个按钮小部件并为其设置工具提示。
btn.resize(btn.sizeHint())
btn.move(50, 50)
调整按钮的大小并在窗口上移动。 sizeHint()方法为按钮提供了建议的大小。

图:工具提示 s
关闭窗口
关闭窗口的明显方法是单击标题栏上的 X 标记。 在下一个示例中,我们展示了如何以编程方式关闭窗口。 我们将简要介绍信号和槽。
以下是我们在示例中使用的QPushButton小部件的构造器。
QPushButton(string text, QWidget parent = None)
text参数是将在按钮上显示的文本。 parent是一个小部件,我们在其上放置了按钮。 在我们的情况下,它将是QWidget。 应用的小部件形成层次结构。 在此层次结构中,大多数小部件都有其父级。 没有父级的小部件是顶层窗口。
quitbutton.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This program creates a quit
button. When we press the button,
the application terminates.
Author: Jan Bodnar
Website: zetcode.com
Last edited: January 2018
"""
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
qbtn = QPushButton('Quit', self)
qbtn.clicked.connect(QApplication.instance().quit)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Quit button')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
在此示例中,我们创建一个退出按钮。 单击按钮后,应用终止。
qbtn = QPushButton('Quit', self)
我们创建一个按钮。 该按钮是QPushButton类的实例。 构造器的第一个参数是按钮的标签。 第二个参数是父窗口小部件。 父窗口小部件是Example小部件,通过继承它是QWidget。
qbtn.clicked.connect(QApplication.instance().quit)
PyQt5 中的事件处理系统是通过信号和槽机制构建的。 如果单击按钮,将发出信号clicked。 该槽可以是 Qt 槽或任何可调用的 Python。
用QApplication.instance()检索的QCoreApplication包含主事件循环-它处理并调度所有事件。 单击的信号连接到quit()方法,该方法终止应用。 通信是在两个对象之间进行的:发送者和接收者。 发送者是按钮,接收者是应用对象。

图:退出按钮
MessageDialog
默认情况下,如果单击标题栏上的 X 按钮,则QWidget将关闭。 有时我们想要修改此默认行为。 例如,如果我们在编辑器中打开了一个文件,对此我们做了一些更改。 我们显示一个消息框以确认操作。
messagebox.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This program shows a confirmation
message box when we click on the close
button of the application window.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
import sys
from PyQt5.QtWidgets import QWidget, QMessageBox, QApplication
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Message box')
self.show()
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
如果我们关闭QWidget,则会生成QCloseEvent。 要修改小部件的行为,我们需要重新实现closeEvent()事件处理器。
reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
我们显示一个带有两个按钮的消息框:是和否。第一个字符串出现在标题栏上。 第二个字符串是对话框显示的消息文本。 第三个参数指定出现在对话框中的按钮的组合。 最后一个参数是默认按钮。 该按钮最初具有键盘焦点。 返回值存储在reply变量中。
if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
event.ignore()
在这里,我们测试返回值。 如果单击“是”按钮,我们将接受导致小部件关闭和应用终止的事件。 否则,我们将忽略关闭事件。

图:消息框
屏幕上的居中窗口
以下脚本显示了如何在桌面屏幕上居中放置窗口。
center.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This program centers a window
on the screen.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
import sys
from PyQt5.QtWidgets import QWidget, QDesktopWidget, QApplication
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(250, 150)
self.center()
self.setWindowTitle('Center')
self.show()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
QDesktopWidget类提供有关用户桌面的信息,包括屏幕大小。
self.center()
将使窗口居中的代码位于自定义center()方法中。
qr = self.frameGeometry()
我们得到一个指定主窗口几何形状的矩形。 这包括任何窗框。
cp = QDesktopWidget().availableGeometry().center()
我们计算出显示器的屏幕分辨率。 从这个分辨率,我们得到了中心点。
qr.moveCenter(cp)
我们的矩形已经具有宽度和高度。 现在,我们将矩形的中心设置为屏幕的中心。 矩形的大小不变。
self.move(qr.topLeft())
我们将应用窗口的左上角移动到qr矩形的左上角,从而将窗口居中放置在屏幕上。
在 PyQt5 教程的这一部分中,我们在 PyQt5 中创建了简单的代码示例。
QtJambi 教程
这是 QtJambi 编程教程。 在本教程中,我们将学习使用 QtJambi 进行 GUI 编程的基础。 QtJambi 教程适合初学者和中级程序员。
目录
QtJambi
QtJambi 是用于创建图形用户界面的跨平台工具包。 它基于两种成功的技术。 Qt 库和 Java 编程语言。 Qt 是功能强大的跨平台应用开发框架。 它的母语是 C++ 。
相关教程
您可能也对 Java Swing 教程, Java SWT 教程或 Qt4 教程感兴趣。
QtJambi 简介
在 QtJambi 编程教程的这一部分中,我们将介绍 QtJambi 工具包并创建我们的第一个 QtJambi 程序。
本教程的目的是帮助您开始使用 QtJambi 工具包。 可以在此处下载本教程中使用的图像。 我使用了 Gnome 项目的探戈图标包中的一些图标。
关于
QtJambi是用于创建图形用户界面的跨平台工具包。 它基于两种非常成功的技术。 Qt 库和 Java 编程语言。 Qt 是功能强大的跨平台应用开发框架。 它的母语是 C++ 。 Java 是非常成功的编程语言。 QtJambi 是 Qt 库的 Java 绑定。 与 Python,C# 和 Ruby 绑定不同,诺基亚公司正式支持 Java 绑定。
创建工具提示
第一个示例将显示一个工具提示。 工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program displays a
* tooltip
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("Tooltip");
setToolTip("This is QWidget");
resize(250, 150);
move(300, 300);
show();
}
public static void main(String args[])
{
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
该示例创建一个窗口。 如果将鼠标指针悬停在窗口区域上方,则会弹出一个工具提示。
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QWidget;
这些是我们在代码示例中使用的组件的必要导入。
public class JambiApp extends QWidget {
该示例继承自QWidget。 QWidget类是所有用户界面对象的基类。 小部件是用户界面的原子。 它从窗口系统接收鼠标,键盘和其他事件。
setWindowTitle("Tooltip");
此方法调用为窗口创建标题。
setToolTip("This is QWidget");
setToolTip方法为QWidget对象创建一个工具提示。
resize(250, 150);
在这里,我们设置窗口的宽度和高度。
move(300, 300);
move()方法在屏幕上移动窗口。
show();
一切准备就绪后,我们在屏幕上显示窗口。
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
这三行设置了应用。

图:工具提示
使窗口居中
在第二个示例中,我们将窗口置于屏幕中央。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QDesktopWidget;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program centers a window
* on the screen
*
* @author jan bodnar
* website zetcode.com
* last modified April 2009
*/
public class JambiApp extends QWidget {
private final int WIDTH = 250;
private final int HEIGHT = 150;
public JambiApp() {
QDesktopWidget qdw = new QDesktopWidget();
int screenWidth = qdw.width();
int screenHeight = qdw.height();
int x = (screenWidth - WIDTH) / 2;
int y = (screenHeight - HEIGHT) / 2;
resize(WIDTH, HEIGHT);
move(x, y);
setWindowTitle("Center");
show();
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
QtJambi 没有使窗口居中的单一方法。
private final int WIDTH = 250;
private final int HEIGHT = 150;
这两个常数定义了应用窗口的宽度和高度。
QDesktopWidget qdw = new QDesktopWidget();
QDesktopWidget类提供有关屏幕的信息。
int screenWidth = qdw.width();
int screenHeight = qdw.height();
在这里,我们确定屏幕的宽度和高度。
int x = (screenWidth - WIDTH) / 2;
int y = (screenHeight - HEIGHT) / 2;
在这里,我们计算居中窗口的 x,y 坐标。 为了使窗口在屏幕上居中,我们需要知道屏幕的大小和窗口的大小。
move(x, y);
我们将窗口移至计算出的 x,y 坐标。
退出按钮
在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program creates a quit
* button. When we press the button,
* the application terminates
*
* @author jan bodnar
* website zetcode.com
* last modified April 2009
*/
public class JambiApp extends QWidget
{
public JambiApp() {
setWindowTitle("Quit button");
initUI();
resize(250, 150);
move(300, 300);
show();
}
private void initUI() {
QPushButton quit = new QPushButton("Quit", this);
quit.setGeometry(30, 30, 75, 30);
quit.clicked.connect(QApplication.instance(), "quit()");
}
public static void main(String args[])
{
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
我们使用QPushButton。这是一个非常常见的小部件。 它是一个矩形,通常显示一个文本标签。
initUI();
我们将用户界面的创建委托给initUI()方法。
QPushButton quit = new QPushButton("Quit", this);
我们创建按钮小部件。 构造器的第一个参数是标签,按钮将显示该标签。 第二个参数是按钮的父窗口小部件。
quit.setGeometry(30, 30, 75, 30);
我们定位和调整按钮小部件的大小。 前两个参数是按钮的 x,y 坐标。 最后两个参数是按钮的宽度和高度。
quit.clicked.connect(QApplication.instance(), "quit()");
当我们点击按钮时,会发出clicked信号。 connect()方法将信号连接到对象的特定槽。 该方法的第一个参数是接收信号的对象。 在我们的例子中,它是应用对象。 第二个参数是方法,称为。 在我们的情况下,它是应用对象的quit()方法。

图:退出按钮
本节是 QtJambi 工具包的介绍。
QtJambi 中的布局管理
在 QtJambi 编程教程的这一部分中,我们将介绍布局管理器。
在设计应用的 GUI 时,我们决定要使用哪些组件以及如何在应用中组织这些组件。 为了组织我们的组件,我们使用专门的不可见对象,称为布局管理器。 QtJambi 中有多个选项。 我们可以使用绝对定位,内置布局管理器或创建自定义布局管理器。 我们还可以使用 Qt Designer 直观地构建布局。
QtJambi 具有一些重要的内置布局管理器。 QVBoxLayout类垂直排列小部件。 QHBoxLayout水平排列小部件。 QGridLayout类将小部件布置在网格中。 网格布局是最灵活的布局管理器。 盒子布局相互嵌套以创建复杂的布局。
绝对定位
在大多数情况下,程序员应使用布局管理器。 在某些情况下,我们可以使用绝对定位。 在绝对定位中,程序员以像素为单位指定每个小部件的位置和大小。 如果我们调整窗口大小,则小部件的大小和位置不会改变。 在各种平台上,应用看起来都不同,在 Linux 上看起来不错,在 Mac OS 上看起来不太正常。 在我们的应用中更改字体可能会破坏布局。 如果我们将应用翻译成另一种语言,则必须重做布局。 对于所有这些问题,仅在有理由时才使用绝对定位。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QPixmap;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* In this program, we lay out widgets
* using absolute positioning
*
* @author jan bodnar
* website zetcode.com
* last modified April 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("Absolute");
initUI();
resize(300, 280);
move(300, 300);
show();
}
private void initUI() {
setStyleSheet("QWidget { background-color: #414141 }");
QPixmap bardejov = new QPixmap("bardejov.jpg");
QPixmap rotunda = new QPixmap("rotunda.jpg");
QPixmap mincol = new QPixmap("mincol.jpg");
QLabel barLabel = new QLabel(this);
barLabel.setPixmap(bardejov);
barLabel.move(20, 20);
QLabel rotLabel = new QLabel(this);
rotLabel.setPixmap(rotunda);
rotLabel.move(40, 160);
QLabel minLabel = new QLabel(this);
minLabel.setPixmap(mincol);
minLabel.move(170, 50);
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在此示例中,我们使用绝对定位显示了三幅图像。
QLabel barLabel = new QLabel(this);
barLabel.setPixmap(bardejov);
QLabel小部件用于保存图像。
barLabel.move(20, 20);
我们使用move()方法将标签放置在窗口上的x = 20,y = 20处。
调整窗口大小时,标签将保留其初始大小。

图:绝对定位
按钮示例
在下面的示例中,我们将在窗口的右下角放置两个按钮。
package com.zetcode;
import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QHBoxLayout;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* In this program, use box layouts
* to position two buttons in the
* bottom right corner of the window
*
* @author jan bodnar
* website zetcode.com
* last modified April 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("Buttons");
initUI();
resize(300, 150);
move(300, 300);
show();
}
private void initUI() {
QVBoxLayout vbox = new QVBoxLayout(this);
QHBoxLayout hbox = new QHBoxLayout();
QPushButton ok = new QPushButton("OK", this);
QPushButton apply = new QPushButton("Apply", this);
hbox.addWidget(ok, 1, Qt.AlignmentFlag.AlignRight);
hbox.addWidget(apply);
vbox.addStretch(1);
vbox.addLayout(hbox);
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
我们使用嵌套框布局来获得我们想要的布局。
QVBoxLayout vbox = new QVBoxLayout(this);
QHBoxLayout hbox = new QHBoxLayout();
我们使用一个垂直框和一个水平框。
QPushButton ok = new QPushButton("OK", this);
QPushButton apply = new QPushButton("Apply", this);
这是两个将进入窗口右下角的按钮。
hbox.addWidget(ok, 1, Qt.AlignmentFlag.AlignRight);
我们将确定按钮放入水平框中。 第二个参数是stretch因子。 它将扩大分配给“确定”按钮的区域。 它会占用所有可用空间。 在此区域内,按钮向右对齐。
vbox.addStretch(1);
这条线创建了一个垂直扩展的白色空间,它将带有按钮的水平框推到底部。
vbox.addLayout(hbox);
水平框嵌套在垂直框中。

图:按钮示例
Windows 示例
以下是嵌套框布局更复杂的示例。
package com.zetcode;
import com.trolltech.qt.core.Qt.AlignmentFlag;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QHBoxLayout;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QTextEdit;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* In this program, use box layouts
* to create a windows example
*
* @author jan bodnar
* website zetcode.com
* last modified April 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("Windows");
initUI();
resize(350, 300);
move(300, 300);
show();
}
private void initUI() {
QVBoxLayout vbox = new QVBoxLayout(this);
QVBoxLayout vbox1 = new QVBoxLayout();
QHBoxLayout hbox1 = new QHBoxLayout();
QHBoxLayout hbox2 = new QHBoxLayout();
QLabel windLabel = new QLabel("Windows", this);
QTextEdit edit = new QTextEdit(this);
edit.setEnabled(false);
QPushButton activate = new QPushButton("Activate", this);
QPushButton close = new QPushButton("Close", this);
QPushButton help = new QPushButton("Help", this);
QPushButton ok = new QPushButton("OK", this);
vbox.addWidget(windLabel);
vbox1.addWidget(activate);
vbox1.addWidget(close, 0, AlignmentFlag.AlignTop);
hbox1.addWidget(edit);
hbox1.addLayout(vbox1);
vbox.addLayout(hbox1);
hbox2.addWidget(help);
hbox2.addStretch(1);
hbox2.addWidget(ok);
vbox.addLayout(hbox2, 1);
setLayout(vbox);
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在此布局中,我们使用两个垂直和水平框。
QVBoxLayout vbox = new QVBoxLayout(this);
这是示例的基本布局。
vbox.addWidget(windLabel);
首先是标签小部件。 它只是转到垂直框的顶部。
vbox1.addWidget(activate);
vbox1.addWidget(close, 0, AlignmentFlag.AlignTop);
hbox1.addWidget(edit);
hbox1.addLayout(vbox1);
vbox.addLayout(hbox1);
在窗口的中心部分,我们有一个文本编辑小部件和两个垂直排列的按钮。 这些按钮进入垂直框。 在此垂直框中,按钮与顶部对齐。 垂直框和文本编辑进入水平框。 该水平框转到标签窗口小部件正下方的基本垂直框。
hbox2.addWidget(help);
hbox2.addStretch(1);
hbox2.addWidget(ok);
vbox.addLayout(hbox2, 1);
帮助和确定按钮进入另一个水平框。 这两个按钮之间有一个扩大的空白区域。 同样,水平框转到基本垂直框。
setLayout(vbox);
基本的垂直框设置为窗口的主要布局。

图:窗口示例
新文件夹示例
在最后一个示例中,我们使用QGridLayout管理器创建“新文件夹”布局示例。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QGridLayout;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QLineEdit;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QTextEdit;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* In this program, use the QGridLayout
* to create a New Folder example
*
* @author jan bodnar
* website zetcode.com
* last modified April 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("New Folder");
initUI();
move(300, 300);
show();
}
private void initUI() {
QGridLayout grid = new QGridLayout(this);
QLabel nameLabel = new QLabel("Name", this);
QLineEdit nameEdit = new QLineEdit(this);
QTextEdit text = new QTextEdit(this);
QPushButton okButton = new QPushButton("OK", this);
QPushButton closeButton = new QPushButton("Close", this);
grid.addWidget(nameLabel, 0, 0);
grid.addWidget(nameEdit, 0, 1, 1, 3);
grid.addWidget(text, 1, 0, 2, 4);
grid.setColumnStretch(1, 1);
grid.addWidget(okButton, 4, 2);
grid.addWidget(closeButton, 4, 3);
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在我们的示例中,我们有一个标签,一行编辑,一个文本编辑和两个按钮。
QGridLayout grid = new QGridLayout(this);
我们创建QGridLayout管理器的实例。
grid.addWidget(nameLabel, 0, 0);
我们将标签小部件放置在网格的第一个单元格中。 单元格从 0 开始计数。最后两个参数是行号和列号。
grid.addWidget(nameEdit, 0, 1, 1, 3);
线编辑窗口小部件位于第一行第二列。 最后两个参数是行跨度和列跨度。 在水平方向上,小部件将跨越三列。
grid.setColumnStretch(1, 1);
该方法的参数是列号和拉伸因子。 在这里,我们将拉伸因子 1 设置到第二列。 这意味着此列将占用所有剩余空间。 之所以这样设置,是因为我们希望按钮保持其初始大小。

图:新文件夹示例
在 QtJambi 教程的这一部分中,我们提到了小部件的布局管理。
QtJambi 中的小部件
在 QtJambi 编程教程的这一部分中,我们将介绍 QtJambi 小部件。
小部件是 GUI 应用的基本构建块。 多年来,几个小部件已成为所有 OS 平台上所有工具包中的标准。 例如,按钮,复选框或滚动条。 QtJambi 有一组丰富的小部件,可以满足大多数编程需求。 可以将更多专门的窗口小部件创建为自定义窗口小部件。
QCheckBox
QCheckBox是具有两种状态的窗口小部件:开和关。 接通状态通过复选标记显示。 它用来表示一些布尔属性。 QCheckBox小部件提供一个带有文本标签的复选框。
package com.zetcode;
import com.trolltech.qt.core.Qt.FocusPolicy;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QCheckBox;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program uses QCheckBox
* widget to show/hide the title
* of the window
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("QCheckBox");
initUI();
resize(250, 150);
move(300, 300);
show();
}
public void initUI() {
QCheckBox cb = new QCheckBox("Show Title", this);
cb.setFocusPolicy(FocusPolicy.NoFocus);
cb.setChecked(true);
cb.toggled.connect(this, "onChanged(boolean)");
cb.move(50, 50);
}
public void onChanged(boolean state) {
if (state) {
setWindowTitle("QCheckBox");
} else {
setWindowTitle("");
}
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在我们的示例中,我们在窗口上放置了一个复选框。 复选框显示/隐藏窗口的标题。
setWindowTitle("QCheckBox");
在构建窗口期间,我们为窗口设置标题。
QCheckBox cb = new QCheckBox("Show Title", this);
QCheckBox小部件已创建。 构造器的第一个参数是其文本标签。 第二个参数是父窗口小部件。
cb.setFocusPolicy(FocusPolicy.NoFocus);
我不喜欢聚焦复选框的视觉表示。 此行禁用焦点。
cb.setChecked(true);
标题在应用的开始处可见。 因此,也必须选中该复选框。
cb.toggled.connect(this, "onChanged(boolean)");
复选框的状态更改时,会发出toggled()信号。 发出信号时,我们触发onChanged()方法。
if (state) {
setWindowTitle("QCheckBox");
} else {
setWindowTitle("");
}
根据复选框的状态,我们显示或隐藏窗口的标题。

图:QCheckBox
QLabel
QLabel小部件用于显示文本或图像。 没有用户交互。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program uses QLabel to
* show lyrics of a song
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class JambiApp extends QWidget {
public JambiApp() {
setWindowTitle("You know I'm no Good");
initUI();
move(300, 300);
show();
}
private void initUI() {
String text =
"Meet you downstairs in the bar and heard\n" +
"your rolled up sleeves and your skull t-shirt\n" +
"You say why did you do it with him today?\n" +
"and sniff me out like I was Tanqueray\n\n" +
"cause you're my fella, my guy\n" +
"hand me your stella and fly\n" +
"by the time I'm out the door\n" +
"you tear men down like Roger Moore\n\n" +
"I cheated myself\n" +
"like I knew I would\n" +
"I told ya, I was trouble\n" +
"you know that I'm no good";
QLabel label = new QLabel(text, this);
label.setFont(new QFont("Purisa", 9));
QVBoxLayout vbox = new QVBoxLayout();
vbox.addWidget(label);
setLayout(vbox);
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
我们的示例在窗口中显示了歌曲的歌词。
String text =
"Meet you downstairs in the bar and heard\n" +
...
我们定义了多行文字。
QLabel label = new QLabel(text, this);
label.setFont(new QFont("Purisa", 9));
我们创建标签小部件并更改其字体。
QVBoxLayout vbox = new QVBoxLayout();
vbox.addWidget(label);
setLayout(vbox);
代替手动编码标签的位置和大小,我们将标签放入盒子布局中。

图:QLabel
QLineEdit
QLineEdit是一个小部件,允许输入和编辑单行纯文本。 QLineEdit小部件具有撤消/重做,剪切/粘贴和拖放功能。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QLineEdit;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program shows text
* which is entered in a QLineEdit
* widget in a QLabel widget
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class JambiApp extends QWidget {
QLabel label;
public JambiApp() {
setWindowTitle("QLineEdit");
initUI();
resize(250, 200);
move(300, 300);
show();
}
private void initUI() {
label = new QLabel(this);
QLineEdit edit = new QLineEdit(this);
edit.textChanged.connect(this, "onChanged(String)");
edit.move(60, 100);
label.move(60, 40);
}
private void onChanged(String text) {
label.setText(text);
label.adjustSize();
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在我们的示例中,我们显示了两个小部件。 行编辑和标签小部件。 输入到行编辑中的文本显示在标签窗口小部件中。
QLineEdit edit = new QLineEdit(this);
QLineEdit小部件已创建。
edit.textChanged.connect(this, "onChanged(String)");
当我们在行编辑中键入或删除某些文本时,将触发onChanged()方法。
private void onChanged(String text) {
label.setText(text);
label.adjustSize();
}
在onChanged()方法中,我们将行编辑的内容设置为标签窗口小部件。 adjustSize()方法确保所有文本都是可见的。

图:QLineEdit小部件
ToggleButton
切换按钮是设置了可检查标志的按钮。 切换按钮是具有两种状态的按钮。 已按下但未按下。 通过单击可以在这两种状态之间切换。 在某些情况下此功能非常合适。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QWidget;
import java.util.Formatter;
/**
* ZetCode QtJambi tutorial
*
* This program uses toggle buttons to
* change the background color of
* a widget
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class JambiApp extends QWidget {
private QWidget square;
private QColor color;
private QPushButton redb;
private QPushButton greenb;
private QPushButton blueb;
public JambiApp() {
setWindowTitle("Toggle Buttons");
initUI();
resize(350, 240);
move(300, 300);
show();
}
private void initUI() {
color = new QColor();
redb = new QPushButton("Red", this);
redb.setCheckable(true);
greenb = new QPushButton("Green", this);
greenb.setCheckable(true);
blueb = new QPushButton("Blue", this);
blueb.setCheckable(true);
redb.toggled.connect(this, "onToggled()");
greenb.toggled.connect(this, "onToggled()");
blueb.toggled.connect(this, "onToggled()");
square = new QWidget(this);
square.setStyleSheet("QWidget { background-color: black }");
redb.move(30, 30);
greenb.move(30, 80);
blueb.move(30, 130);
square.setGeometry(150, 25, 150, 150);
}
public void onToggled() {
int red = color.red();
int green = color.green();
int blue = color.blue();
if (redb.isChecked()) {
red = 255;
} else {
red = 0;
}
if (greenb.isChecked()) {
green = 255;
} else {
green = 0;
}
if (blueb.isChecked()) {
blue = 255;
} else {
blue = 0;
}
color = new QColor(red, green, blue);
Formatter fmt = new Formatter();
fmt.format("QWidget { background-color: %s }", color.name());
square.setStyleSheet(fmt.toString());
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在代码示例中,我们使用三个切换按钮来更改矩形小部件的颜色。
private QWidget square;
private QColor color;
private QPushButton redb;
private QPushButton greenb;
private QPushButton blueb;
我们定义了五个对象。 正方形小部件是QWidget,它显示颜色。 color变量用于保存颜色值。 这三个按钮是切换按钮,用于混合颜色值。
redb = new QPushButton("Red", this);
redb.setCheckable(true);
我们创建一个QPushButton小部件。 setCheckable()方法将按钮更改为切换按钮。
redb.toggled.connect(this, "onToggled()");
greenb.toggled.connect(this, "onToggled()");
blueb.toggled.connect(this, "onToggled()");
所有三个按钮都插入到一个方法调用中,即onToggled()方法。
square = new QWidget(this);
square.setStyleSheet("QWidget { background-color: black }");
我们创建方形小部件。 一开始是黑色的。 在 QtJambi 中,我们使用样式表来自定义小部件的外观。
在onToggled()方法内部,我们确定颜色值并将正方形小部件更新为新颜色。
int red = color.red();
int green = color.green();
int blue = color.blue();
在这里,我们确定方形小部件的当前颜色。
if (redb.isChecked()) {
red = 255;
} else {
red = 0;
}
根据红色切换按钮的状态,更改颜色的红色部分。
color = new QColor(red, green, blue);
我们创建一个新的颜色值。
Formatter fmt = new Formatter();
fmt.format("QWidget { background-color: %s }", color.name());
这两行创建样式表的文本。 我们使用 Java Formatter对象。
square.setStyleSheet(fmt.toString());
正方形的颜色已更新。

图:开关按钮
QComboBox
QComboBox是一个小部件,允许用户从选项列表中进行选择。 这是一个显示当前项目的选择小部件,可以弹出可选择项目的列表。 组合框可能是可编辑的。 它以占用最少屏幕空间的方式向用户显示选项列表。
package com.zetcode;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QComboBox;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QWidget;
/**
* ZetCode QtJambi tutorial
*
* This program uses the QComboBox widget.
* The option selected from the combo box is
* displayed in the label widget.
*
* @author jan bodnar
* website zetcode.com
* last modified March 2009
*/
public class JambiApp extends QWidget {
QLabel label;
public JambiApp() {
setWindowTitle("QComboBox");
initUI();
resize(250, 200);
move(300, 300);
show();
}
private void initUI() {
label = new QLabel("Ubuntu", this);
QComboBox combo = new QComboBox(this);
combo.addItem("Ubuntu");
combo.addItem("Fedora");
combo.addItem("Mandriva");
combo.addItem("Red Hat");
combo.addItem("Mint");
combo.currentStringChanged.connect(this, "OnActivated(String)");
combo.move(50, 30);
label.move(50, 100);
}
private void OnActivated(String text) {
label.setText(text);
label.adjustSize();
}
public static void main(String[] args) {
QApplication.initialize(args);
new JambiApp();
QApplication.exec();
}
}
在我们的代码示例中,我们有两个小部件。 组合框和标签小部件。 从组合框中选择的选项显示在标签中。
label = new QLabel("Ubuntu", this);
这是一个标签,它将显示组合框中当前选择的选项。
QComboBox combo = new QComboBox(this);
我们创建QComboBox小部件的实例。
combo.addItem("Ubuntu");
combo.addItem("Fedora");
combo.addItem("Mandriva");
combo.addItem("Red Hat");
combo.addItem("Mint");
组合框将填充值。
combo.currentStringChanged.connect(this, "OnActivated(String)");
当我们从组合框中选择一个选项时,将触发OnActivated()方法。
private void OnActivated(String text) {
label.setText(text);
label.adjustSize();
}
在OnActivated()方法中,我们将标签小部件更新为从组合框中选择的当前字符串。

图:QComboBox小部件
在 QtJambi 教程的这一部分中,我们介绍了几个 QtJambi 小部件。


浙公网安备 33010602011771号