ZetCode-GUI-教程-六-
ZetCode GUI 教程(六)
原文:ZetCode
贪食蛇
在 Visual Basic Qyoto 编程教程的这一部分中,我们将创建贪食蛇游戏克隆。
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"
消息。
board.vb
Imports Qyoto
NameSpace BoardSpace
public class Board
Inherits QFrame
Const WIDTH As Integer = 300
Const HEIGHT As Integer = 300
Const DOT_SIZE As Integer = 10
Const ALL_DOTS As Integer = 900
Const RAND_POS As Integer = 30
Const DELAY As Integer = 140
Dim x(ALL_DOTS) As Integer
Dim y(ALL_DOTS) As Integer
Dim dots As Integer
Dim apple_x As Integer
Dim apple_y As Integer
Dim left As Boolean = False
Dim right As Boolean = True
Dim up As Boolean = False
Dim down As Boolean = False
Dim inGame As Boolean = True
Dim timer As QBasicTimer
Dim ball As QImage
Dim apple As QImage
Dim head As QImage
Public Sub New()
Me.SetStyleSheet("QWidget { background-color: black }")
Me.FocusPolicy = Qt.FocusPolicy.StrongFocus
Try
ball = New QImage("dot.png")
apple = New QImage("apple.png")
head = New QImage("head.png")
Catch e As Exception
Console.WriteLine("Cannot read images")
Console.WriteLine(e.Message)
Environment.Exit(1)
End Try
Me.InitGame()
End Sub
Private Sub InitGame()
dots = 3
For z As Integer = 0 To dots-1
x(z) = 50 - z*10
y(z) = 50
Next
Me.LocateApple()
timer = New QBasicTimer()
timer.Start(DELAY, Me)
End Sub
Protected Overrides Sub PaintEvent(ByVal e As QPaintEvent)
Dim painter As New QPainter()
painter.Begin(Me)
If inGame
Me.DrawObjects(painter)
Else
Me.GameOver(painter)
End If
painter.End()
End Sub
Private Sub DrawObjects(ByVal painter As QPainter)
painter.DrawImage(apple_x, apple_y, apple)
For z As Integer = 0 to dots - 1
If z = 0
painter.DrawImage(x(z), y(z), head)
Else
painter.DrawImage(x(z), y(z), ball)
End If
Next
End Sub
Private Sub GameOver(ByVal painter As QPainter)
Dim msg As String = "Game Over"
Dim small As New QFont("Helvetica", 12)
small.SetBold(True)
Dim metr As New QFontMetrics(small)
Dim textWidth As Integer = metr.Width(msg)
Dim h As Integer = Me.Height
Dim w As Integer = Me.Width
painter.SetPen(New QPen(New QBrush(Qt.GlobalColor.white), 1))
painter.SetFont(small)
painter.Translate(New QPoint(w/2, h/2))
Dim text_x As Integer = -textWidth / 2
Dim text_y As Integer = 0
painter.DrawText(text_x, text_y, msg)
End Sub
Private Sub CheckApple()
If x(0) = apple_x And y(0) = apple_y
dots += 1
Me.LocateApple()
End If
End Sub
Private Sub Move()
For z As Integer = dots To 1 Step -1
x(z) = x(z - 1)
y(z) = y(z - 1)
Next
If left
x(0) -= DOT_SIZE
End If
If right
x(0) += DOT_SIZE
End If
If up
y(0) -= DOT_SIZE
End If
If down
y(0) += DOT_SIZE
End If
End Sub
Private Sub CheckCollision()
For z As Integer = dots To 1 Step -1
If z > 4 And x(0) = x(z) And y(0) = y(z)
inGame = False
End If
Next
If y(0) > HEIGHT
inGame = False
End If
If y(0) < 0
inGame = False
End If
If x(0) > WIDTH
inGame = False
End If
If x(0) < 0
inGame = False
End If
End Sub
Private Sub LocateApple()
Dim rand As New Random()
Dim r As Integer = rand.Next(RAND_POS)
apple_x = r * DOT_SIZE
r = rand.Next(RAND_POS)
apple_y = r * DOT_SIZE
End Sub
Protected Overrides Sub TimerEvent(ByVal e As QTimerEvent)
If inGame
Me.CheckApple()
Me.CheckCollision()
Me.Move()
Else
timer.Stop()
End If
Me.Repaint()
End Sub
Protected Overrides Sub KeyPressEvent(ByVal e As QKeyEvent)
Dim key As Integer = e.Key()
If key = Qt.Key.Key_Left And Not right
left = True
up = False
down = False
End If
If key = Qt.Key.Key_Right And Not left
right = True
up = False
down = False
End If
If key = Qt.Key.Key_Up And Not down
up = True
right = False
left = False
End If
If key = Qt.Key.Key_Down And Not up
down = True
right = False
left = False
End If
End Sub
End Class
End Namespace
首先,我们将定义一些在游戏中使用的全局变量。
WIDTH
和HEIGHT
常数确定电路板的大小。 DOT_SIZE
是苹果的大小和蛇的点。 ALL_DOTS
常数定义了板上可能的最大点数。 RAND_POS
常数用于计算苹果的随机位置。 DELAY
常数确定游戏的速度。
Dim x(ALL_DOTS) As Integer
Dim y(ALL_DOTS) As Integer
这两个数组存储蛇的所有可能关节的 x,y 坐标。
InitGame()
方法初始化变量,加载图像并启动超时功能。
If inGame
Me.DrawObjects(painter)
Else
Me.GameOver(painter)
End If
在PaintEvent()
方法内部,我们检查inGame
变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"
文本。
Private Sub DrawObjects(ByVal painter As QPainter)
painter.DrawImage(apple_x, apple_y, apple)
For z As Integer = 0 to dots - 1
If z = 0
painter.DrawImage(x(z), y(z), head)
Else
painter.DrawImage(x(z), y(z), ball)
End If
Next
End Sub
DrawObjects()
方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。
Private Sub CheckApple()
If x(0) = apple_x And y(0) = apple_y
dots += 1
Me.LocateApple()
End If
End Sub
CheckApple()
方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用LocateApple()
方法,该方法将随机放置一个新的Apple
对象。
在Move()
方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
For z As Integer = dots To 1 Step -1
x(z) = x(z - 1)
y(z) = y(z - 1)
Next
该代码将关节向上移动。
If left
x(0) -= DOT_SIZE
End If
将头向左移动。
在CheckCollision()
方法中,我们确定蛇是否击中了自己或撞墙之一。
For z As Integer = dots To 1 Step -1
If z > 4 And x(0) = x(z) And y(0) = y(z)
inGame = False
End If
Next
如果蛇用头撞到关节之一,我们就结束游戏。
If y(0) > HEIGHT
inGame = False
End If
如果蛇击中了棋盘的底部,我们就结束了游戏。
LocateApple()
方法在板上随机放置一个苹果。
Dim rand As New Random()
Dim r As Integer = rand.Next(RAND_POS)
我们得到一个从 0 到RAND_POS-1
的随机数。
apple_x = r * DOT_SIZE
...
apple_y = r * DOT_SIZE
这些行设置了apple
对象的 x,y 坐标。
If inGame
Me.CheckApple()
Me.CheckCollision()
Me.Move()
Else
timer.Stop()
End If
每 140 毫秒,将调用TimerEvent()
方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 否则,我们将停止计时器。
在Board
类的KeyPressEvent()
方法中,我们确定按下的键。
If key = Qt.Key.Key_Left And Not right
left = True
up = False
down = False
End If
如果单击左光标键,则将left
变量设置为true
。 在Move()
方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
nibbles.vb
' ZetCode Mono Visual Basic Qt tutorial
'
' In this program, we create
' a Nibbles game clone
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports Qyoto
Imports BoardSpace
Public Class VBQApp
Inherits QMainWindow
Dim WIDTH As Integer = 250
Dim HEIGHT As Integer = 150
Public Sub New()
Me.SetWindowTitle("Nibbles")
Dim board As New BoardSpace.Board()
Me.SetCentralWidget(board)
Me.Resize(310, 310)
Me.Move(300, 300)
Me.Show()
End Sub
Public Shared Sub Main(ByVal args() As String)
Dim qapp As New QApplication(args)
Dim app As New VBQApp
QApplication.Exec()
End Sub
End Class
在这个类中,我们设置了贪食蛇游戏。
图:贪食蛇
这是用 Qyoto 库和 Visual Basic 编程语言编写的贪食蛇电脑游戏。
Mono IronPython Winforms 教程
这是 Mono IronPython Winforms 教程。 在本教程中,我们将学习使用 IronPython 在 Winforms 中进行 GUI 编程的基础。 Mono IronPython Winforms 教程适用于初学者。
目录
Winforms
Windows Forms 是图形用户界面应用编程接口(API),包含在 Microsoft .NET Framework 中。 截至 2008 年 5 月 13 日,Mono 的System.Windows.Forms
2.0 已完成 API。 简而言之,Winforms 是一个用于创建 GUI 应用的库。
相关教程
在 ZetCode 上有有关 Winforms 库的其他绑定的教程。 Mono C# Winforms 教程和 Visual Basic Winforms 教程。
介绍
原文: http://zetcode.com/tutorials/ironpythontutorial/introduction/
IronPython Mono Winforms 教程的第一部分介绍了 Mono 平台和 Winforms 库。
关于本教程
这是 Mono IronPython Winforms 教程。 Mono IronPython Winforms 教程适用于初学者。 本教程的目的是向读者介绍用于 IronPython 编程语言的 Mono Winforms 中 GUI 编程的基础知识。 本教程是在 Linux 上创建并测试的。 但是,它也可以在其他操作系统上使用。 大多数示例都应运行而无需修改。 可以在此处下载本教程中使用的图像。
Mono
Mono 是由 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 应用的库。
IronPython
IronPython 是 .NET Framework 和 Mono 的 Python 编程语言的实现。 IronPython 完全用 C# 编写。 Python 和 IronPython 之间有一些明显的区别。
运行代码示例
我们的教程使用 IronPython 语言。
simple.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Application, Form
class IForm(Form):
def __init__(self):
self.Text = 'Simple'
self.Width = 250
self.Height = 200
self.CenterToScreen()
Application.Run(IForm())
这个小代码示例在屏幕上显示了一个简单的窗口。 它使用 Winforms 库。 它是用 IronPython 编码的。
#!/usr/bin/ipy
这是 IronPython 解释器的路径。
$ chmod +x simple.py
$ ./simple.py
我们使脚本可执行并运行它。
参考
这是 Mono IronPython Winforms 的简介。
IronPython Mono Winforms 中的第一步
原文: http://zetcode.com/tutorials/ironpythontutorial/firststeps/
在 IronPython Mono Winforms 教程的这一部分中,我们介绍 Winforms 编程库中的一些基本程序。
简单
这是一个简单的 Winforms 应用。
simple.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Application, Form
class IForm(Form):
def __init__(self):
self.Text = 'Simple'
self.Width = 250
self.Height = 200
self.CenterToScreen()
Application.Run(IForm())
此代码示例在屏幕上显示一个小窗口。
clr.AddReference("System.Windows.Forms")
我们添加了 Winforms 库的引用。
class IForm(Form):
在 Winforms 中,任何窗口或对话框都是Form
。 该控件是一个基本容器,其目的是显示其他子控件。 我们的类IForm
继承自表单。 这样,它本身就成为一种形式。
self.Text = 'Simple'
self.Width = 250
self.Height = 200
Text
,Width
和Height
是表单的属性。 更改这些属性,我们将修改表单控件。 第一行在表单控件的标题栏中显示文本"Simple"
。 其他两行将表单的大小设置为250x200
像素。
self.CenterToScreen()
这种方法将我们的应用集中在屏幕上。
Application.Run(IForm())
此行运行示例。
图:简单
图标
Mono 在西班牙语中意为猴子。 如果我们不为应用提供图标,则默认情况下,我们的头是猴子。 下一个示例显示如何更改此设置。
icon.py
#!/usr/bin/ipy
import clr
import sys
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Icon
class IForm(Form):
def __init__(self):
self.Text = 'Icon'
self.Width = 250
self.Height = 200
try:
self.Icon = Icon("web.ico")
except Exception, e:
print e.msg
sys.exit(1)
self.CenterToScreen()
Application.Run(IForm())
该代码示例在窗体的左上角显示一个图标。 表单的图标是代表任务栏中表单的图片以及为表单的控制框显示的图标。
clr.AddReference("System.Drawing")
Icon
对象来自System.Drawing
模块。 因此,我们必须添加引用。
try:
self.Icon = Icon("web.ico")
except Exception, e:
print e.msg
sys.exit(1)
最好将所有输入输出工作放在try
和except
关键字之间。 web.ico
文件必须在当前工作目录中可用。 这是我们执行应用的目录(ipy icon.py
)。
图:图标
工具提示
工具提示是一个小的矩形弹出窗口,当用户将指针放在控件上时,它会显示控件目的的简短说明。
tooltips.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import Button, ToolTip
from System.Drawing import Point, Size
class IForm(Form):
def __init__(self):
self.Text = 'Tooltips'
self.CenterToScreen()
self.Size = Size(200, 150)
tooltip = ToolTip()
tooltip.SetToolTip(self, "This is a Form")
button = Button()
button.Parent = self
button.Text = "Button"
button.Location = Point(50, 70)
tooltip.SetToolTip(button, "This is a Button")
Application.Run(IForm())
我们的代码示例为两个控件创建一个工具提示。 Button
控件和Form
控件。
tooltip = ToolTip()
在这里,我们创建ToolTip
控件。 此实例用于为两个控件提供工具提示。
tooltip.SetToolTip(self, "This is a Form")
在这里,我们为表单设置工具提示。
tooltip.SetToolTip(button, "This is a Button")
这里是按钮。
button = Button()
button.Parent = self
button.Text = "Button"
button.Location = Point(50, 70)
注意Button
控件的创建。 Parent
属性确定按钮所在的容器。 Text
属性是按钮的标签。 Location
属性将按钮放在表单上的x = 30
,y = 70px
坐标处。
图:工具提示 s
按钮
我们的最后一个代码示例显示了一个有效的按钮控件。
button.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, Button
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'Button'
self.CenterToScreen()
self.Size = Size(200, 150)
btn = Button()
btn.Parent = self
btn.Text = "Quit"
btn.Location = Point(50, 50)
btn.Click += self.OnClick
btn.MouseEnter += self.OnEnter
def OnClick(self, sender, args):
self.Close()
def OnEnter(self, sender, args):
print "button entered"
Application.Run(IForm())
所有 GUI 编程都是事件驱动的编程。 在我们的示例中,我们在表单容器上显示了一个按钮控件。 该按钮将收听两个事件:Click
和MouseEnter
事件。
btn.Click += self.OnClick
此代码行将事件处理器插入Click
事件。 当我们单击按钮时,将调用OnClick()
方法。
btn.MouseEnter += self.OnEnter
当我们使用鼠标指针进入按钮区域时,将触发MouseEnter
事件。 在这种情况下,我们的代码将调用OnEnter()
方法。
def OnClick(self, sender, args):
self.Close()
该方法关闭应用。
def OnEnter(self, sender, args):
print "button entered"
当我们使用鼠标指针进入按钮控制区域时,"button entered"
文本将显示在终端中。
IronPython Mono Winforms 教程的这一部分显示了一些入门代码示例。
布局管理
IronPython Mono Winforms 教程继续进行控件的布局管理。 在将控件放置在其父容器上之后,我们必须确保其布局正确。
Anchor
控件的Anchor
属性确定如何使用其父控件调整其大小。 锚是海洋世界中的一个术语。 当船锚掉入水中时,船就固定在某个地方。 Winforms 控件也是如此。
Winforms 中的每个控件都可以具有以下AnchorStyles
值之一:
TOP
LEFT
RIGHT
BOTTOM
注意,控件不限于一个值。 他们可以使用|
运算符将这些值进行任意组合。
基本Anchor
示例
下面的示例显示一个非常基本的示例,演示Anchor
属性。
anchor.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import Button, AnchorStyles
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'Anchor'
self.Size = Size(210, 210)
btn1 = Button()
btn1.Text = "Button"
btn1.Parent = self
btn1.Location = Point(30, 30)
btn2 = Button()
btn2.Text = "Button"
btn2.Parent = self
btn2.Location = Point(30, 80)
btn2.Anchor = AnchorStyles.Right
self.CenterToScreen()
Application.Run(IForm())
这是一个非常基本的代码示例,清楚地显示了Anchor
属性的含义。 我们在表单上有两个按钮。 第一个按钮具有默认的AnchorStyles
值,即AnchorStyles.Top | AnchorStyles.Left
。 第二个按钮已显式设置AnchorStyles.Right
。
btn2.Anchor = AnchorStyles.Right
我们将第二个按钮的Anchor
属性明确设置为AnchorStyles
。 正确的价值。
现在看看以下两个图像。 左边的是开始时显示的应用。 调整大小后,右侧显示相同的应用。 第一个按钮与表单的左边界和上边界保持距离。 第二个按钮与表单的右边框保持距离。 但是它在垂直方向上没有保持任何距离。
图:调整大小前后
Dock
Dock
属性允许我们将控件粘贴到父窗体或控件的特定边缘。
以下是可能的DockStyle
值。
TOP
LEFT
RIGHT
BOTTOM
FILL
NONE
编辑器骨架
以下代码示例演示了正在使用的Dock
属性。
editor.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, MainMenu, StatusBar
from System.Windows.Forms import Shortcut, MenuItem, TextBox, DockStyle
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'Editor'
self.Size = Size(210, 180)
mainMenu = MainMenu()
filem = mainMenu.MenuItems.Add('&File')
filem.MenuItems.Add(MenuItem('E&xit',
self.OnExit, Shortcut.CtrlX))
self.Menu = mainMenu
tb = TextBox()
tb.Parent = self
tb.Dock = DockStyle.Fill
tb.Multiline = True
sb = StatusBar()
sb.Parent = self
sb.Text = 'Ready'
self.CenterToScreen()
def OnExit(self, sender, event):
self.Close()
Application.Run(IForm())
我们显示一个菜单栏和一个状态栏。 其余区域由TextBox
控件占用。
tb = TextBox()
tb.Parent = self
在这里,我们创建TextBox
控件。 Form
容器被设置为文本框的父级。
tb.Dock = DockStyle.Fill
此代码行使TextBox
控件占用了表单容器内的剩余空间。
图:编辑器骨架
固定按钮
下一个示例显示了位于窗体右下角的两个按钮。
anchoredbuttons.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
clr.AddReference("System")
from System.Windows.Forms import Application, Form, Button, Panel
from System.Windows.Forms import DockStyle, AnchorStyles
from System.Drawing import Size, Point
WIDTH = 250
HEIGHT = 150
BUTTONS_SPACE = 15
PANEL_SPACE = 8
CLOSE_SPACE = 10
class IForm(Form):
def __init__(self):
self.Text = 'Buttons'
self.Size = Size(WIDTH, HEIGHT)
ok = Button()
PANEL_HEIGHT = ok.Height + PANEL_SPACE
panel = Panel()
panel.Height = PANEL_HEIGHT
panel.Dock = DockStyle.Bottom
panel.Parent = self
x = ok.Width * 2 + BUTTONS_SPACE
y = (PANEL_HEIGHT - ok.Height) / 2
ok.Text = "Ok"
ok.Parent = panel
ok.Location = Point(WIDTH-x, y)
ok.Anchor = AnchorStyles.Right
close = Button()
x = close.Width
close.Text = "Close"
close.Parent = panel
close.Location = Point(WIDTH-x-CLOSE_SPACE, y)
close.Anchor = AnchorStyles.Right
self.CenterToScreen()
Application.Run(IForm())
该示例在对话框的右下角显示“确定”,“关闭”按钮,这在对话框窗口中很常见。
WIDTH = 250
HEIGHT = 150
WIDTH
和HEIGHT
变量确定应用窗口的宽度和高度。
BUTTONS_SPACE = 15
PANEL_SPACE = 8
CLOSE_SPACE = 10
BUTTONS_SPACE
是“确定”和“关闭”按钮之间的空间。 PANEL_SPACE
是面板和表单底部之间的空间。 最后,CLOSE_SPACE
变量设置“关闭”按钮和表单右边框之间的间隔。
PANEL_HEIGHT = ok.Height + PANEL_SPACE
在这里,我们计算面板的高度。 面板的高度基于“确定”按钮的高度。 并且我们添加了一些额外的空间,以使按钮不会太靠近边框。
panel = Panel()
panel.Height = PANEL_HEIGHT
panel.Dock = DockStyle.Bottom
panel.Parent = self
在这里,我们创建和管理Panel
控件。 在此示例中,它用作按钮的容器。 它被粘贴到表单的底部边框。 然后将按钮放置在面板内。
ok.Text = "Ok"
ok.Parent = panel
ok.Location = Point(WIDTH-x, y)
ok.Anchor = AnchorStyles.Right
“确定”按钮的父级设置为面板小部件。 计算位置。 并且Anchor
属性设置为右侧。 另一个按钮的创建类似。
图:固定按钮
播放器骨架
IronPython Mono Winforms 教程这一部分的最后一个示例显示了一个更复杂的示例。 它是音乐播放器的骨架。
player.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, Button, Panel
from System.Windows.Forms import DockStyle, AnchorStyles, StatusBar
from System.Windows.Forms import TrackBar, MainMenu, MenuItem
from System.Windows.Forms import FlatStyle, TickStyle, Shortcut
from System.Drawing import Size, Point, Bitmap, Color
class IForm(Form):
def __init__(self):
self.Text = 'Player'
self.Size = Size(350, 280)
mainMenu = MainMenu()
filem = mainMenu.MenuItems.Add("&File")
playm = mainMenu.MenuItems.Add("&Play")
view = mainMenu.MenuItems.Add("&View")
tools = mainMenu.MenuItems.Add("&Tools")
favourites = mainMenu.MenuItems.Add("&Favourites")
help = mainMenu.MenuItems.Add("&Help")
filem.MenuItems.Add(MenuItem("E&xit",
self.OnExit, Shortcut.CtrlX))
self.Menu = mainMenu
panel = Panel()
panel.Parent = self
panel.BackColor = Color.Black
panel.Dock = DockStyle.Fill
buttonPanel = Panel()
buttonPanel.Parent = self
buttonPanel.Height = 40
buttonPanel.Dock = DockStyle.Bottom
pause = Button()
pause.FlatStyle = FlatStyle.Popup
pause.Parent = buttonPanel
pause.Location = Point(5, 10)
pause.Size = Size(25, 25)
pause.Image = Bitmap("pause.png")
play = Button()
play.FlatStyle = FlatStyle.Popup
play.Parent = buttonPanel
play.Location = Point(35, 10)
play.Size = Size(25, 25)
play.Image = Bitmap("play.png")
forward = Button()
forward.FlatStyle = FlatStyle.Popup
forward.Parent = buttonPanel
forward.Location = Point(80, 10)
forward.Size = Size(25, 25)
forward.Image = Bitmap("forward.png")
backward = Button()
backward.FlatStyle = FlatStyle.Popup
backward.Parent = buttonPanel
backward.Location = Point(110, 10)
backward.Size = Size(25, 25)
backward.Image = Bitmap("backward.png")
tb = TrackBar()
tb.Parent = buttonPanel
tb.TickStyle = TickStyle.None
tb.Size = Size(150, 25)
tb.Location = Point(200, 10)
tb.Anchor = AnchorStyles.Right
audio = Button()
audio.FlatStyle = FlatStyle.Popup
audio.Parent = buttonPanel
audio.Size = Size(25, 25)
audio.Image = Bitmap("audio.png")
audio.Location = Point(170, 10)
audio.Anchor = AnchorStyles.Right
sb = StatusBar()
sb.Parent = self
sb.Text = "Ready"
self.CenterToScreen()
def OnExit(self, sender, event):
self.Close()
Application.Run(IForm())
这是一个更复杂的示例,它同时显示了Dock
和Anchor
属性。
mainMenu = MainMenu()
filem = mainMenu.MenuItems.Add("&File")
...
self.Menu = mainMenu
在这里,我们创建菜单栏。
panel = Panel()
panel.Parent = self
panel.BackColor = Color.Black
panel.Dock = DockStyle.Fill
这是黑色的面板,占据了菜单栏,状态栏和控制面板剩余的所有剩余空间。
buttonPanel = Panel()
buttonPanel.Parent = self
buttonPanel.Height = 40
buttonPanel.Dock = DockStyle.Bottom
这是控制面板。 它的父级是表单容器。 它被粘贴到表格的底部。 高度为 40 像素。 在此控制面板内部,我们放置了所有按钮和轨迹仪。
pause = Button()
pause.FlatStyle = FlatStyle.Popup
pause.Parent = buttonPanel
pause.Location = Point(5, 10)
pause.Size = Size(25, 25)
pause.Image = Bitmap("pause.png")
暂停按钮是具有默认Anchor
属性值的四个按钮之一。 该按钮的样式设置为平面,因为它看起来更好。 我们在按钮上放置一个位图。
tb.Anchor = AnchorStyles.Right
...
audio.Anchor = AnchorStyles.Right
最后两个控件固定在右侧。
图:播放器骨架
IronPython Mono Winforms 教程的这一部分是关于控件的布局管理的。 我们实践了 Winforms 库提供的各种可能性。
PyQt5 中的俄罗斯方块
在本章中,我们将创建一个俄罗斯方块游戏克隆。
俄罗斯方块
俄罗斯方块游戏是有史以来最受欢迎的计算机游戏之一。 原始游戏是由俄罗斯程序员 Alexey Pajitnov 于 1985 年设计和编程的。此后,几乎所有版本的几乎所有计算机平台上都可以使用俄罗斯方块。
俄罗斯方块被称为下降块益智游戏。 在这个游戏中,我们有七个不同的形状,称为 tetrominoes:S 形,Z 形,T 形,L 形,线形,MirroredL 形和正方形。 这些形状中的每一个都形成有四个正方形。 形状从板上掉下来。 俄罗斯方块游戏的目的是移动和旋转形状,以使其尽可能地适合。 如果我们设法形成一行,则该行将被破坏并得分。 我们玩俄罗斯方块游戏,直到达到顶峰。
图:Tetrominoes
PyQt5 是旨在创建应用的工具包。 还有其他一些旨在创建计算机游戏的库。 尽管如此,PyQt5 和其他应用工具包仍可用于创建简单的游戏。
创建计算机游戏是增强编程技能的好方法。
开发
我们的俄罗斯方块游戏没有图像,我们使用 PyQt5 编程工具包中提供的绘图 API 绘制四面体。 每个计算机游戏的背后都有一个数学模型。 俄罗斯方块也是如此。
游戏背后的一些想法:
- 我们使用
QtCore.QBasicTimer()
创建游戏周期。 - 绘制四方块。
- 形状以正方形为单位移动(而不是逐个像素移动)。
- 从数学上讲,棋盘是一个简单的数字列表。
该代码包括四个类别:Tetris
,Board
,Tetrominoe
和Shape
。 Tetris
类设置游戏。 Board
是编写游戏逻辑的地方。 Tetrominoe
类包含所有俄罗斯方块的名称,Shape
类包含俄罗斯方块的代码。
tetris.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This is a Tetris game clone.
Author: Jan Bodnar
Website: zetcode.com
Last edited: August 2017
"""
from PyQt5.QtWidgets import QMainWindow, QFrame, QDesktopWidget, QApplication
from PyQt5.QtCore import Qt, QBasicTimer, pyqtSignal
from PyQt5.QtGui import QPainter, QColor
import sys, random
class Tetris(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
'''initiates application UI'''
self.tboard = Board(self)
self.setCentralWidget(self.tboard)
self.statusbar = self.statusBar()
self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)
self.tboard.start()
self.resize(180, 380)
self.center()
self.setWindowTitle('Tetris')
self.show()
def center(self):
'''centers the window on the screen'''
screen = QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2,
(screen.height()-size.height())/2)
class Board(QFrame):
msg2Statusbar = pyqtSignal(str)
BoardWidth = 10
BoardHeight = 22
Speed = 300
def __init__(self, parent):
super().__init__(parent)
self.initBoard()
def initBoard(self):
'''initiates board'''
self.timer = QBasicTimer()
self.isWaitingAfterLine = False
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
self.setFocusPolicy(Qt.StrongFocus)
self.isStarted = False
self.isPaused = False
self.clearBoard()
def shapeAt(self, x, y):
'''determines shape at the board position'''
return self.board[(y * Board.BoardWidth) + x]
def setShapeAt(self, x, y, shape):
'''sets a shape at the board'''
self.board[(y * Board.BoardWidth) + x] = shape
def squareWidth(self):
'''returns the width of one square'''
return self.contentsRect().width() // Board.BoardWidth
def squareHeight(self):
'''returns the height of one square'''
return self.contentsRect().height() // Board.BoardHeight
def start(self):
'''starts game'''
if self.isPaused:
return
self.isStarted = True
self.isWaitingAfterLine = False
self.numLinesRemoved = 0
self.clearBoard()
self.msg2Statusbar.emit(str(self.numLinesRemoved))
self.newPiece()
self.timer.start(Board.Speed, self)
def pause(self):
'''pauses game'''
if not self.isStarted:
return
self.isPaused = not self.isPaused
if self.isPaused:
self.timer.stop()
self.msg2Statusbar.emit("paused")
else:
self.timer.start(Board.Speed, self)
self.msg2Statusbar.emit(str(self.numLinesRemoved))
self.update()
def paintEvent(self, event):
'''paints all shapes of the game'''
painter = QPainter(self)
rect = self.contentsRect()
boardTop = rect.bottom() - Board.BoardHeight * self.squareHeight()
for i in range(Board.BoardHeight):
for j in range(Board.BoardWidth):
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
if shape != Tetrominoe.NoShape:
self.drawSquare(painter,
rect.left() + j * self.squareWidth(),
boardTop + i * self.squareHeight(), shape)
if self.curPiece.shape() != Tetrominoe.NoShape:
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.drawSquare(painter, rect.left() + x * self.squareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
self.curPiece.shape())
def keyPressEvent(self, event):
'''processes key press events'''
if not self.isStarted or self.curPiece.shape() == Tetrominoe.NoShape:
super(Board, self).keyPressEvent(event)
return
key = event.key()
if key == Qt.Key_P:
self.pause()
return
if self.isPaused:
return
elif key == Qt.Key_Left:
self.tryMove(self.curPiece, self.curX - 1, self.curY)
elif key == Qt.Key_Right:
self.tryMove(self.curPiece, self.curX + 1, self.curY)
elif key == Qt.Key_Down:
self.tryMove(self.curPiece.rotateRight(), self.curX, self.curY)
elif key == Qt.Key_Up:
self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY)
elif key == Qt.Key_Space:
self.dropDown()
elif key == Qt.Key_D:
self.oneLineDown()
else:
super(Board, self).keyPressEvent(event)
def timerEvent(self, event):
'''handles timer event'''
if event.timerId() == self.timer.timerId():
if self.isWaitingAfterLine:
self.isWaitingAfterLine = False
self.newPiece()
else:
self.oneLineDown()
else:
super(Board, self).timerEvent(event)
def clearBoard(self):
'''clears shapes from the board'''
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoe.NoShape)
def dropDown(self):
'''drops down a shape'''
newY = self.curY
while newY > 0:
if not self.tryMove(self.curPiece, self.curX, newY - 1):
break
newY -= 1
self.pieceDropped()
def oneLineDown(self):
'''goes one line down with a shape'''
if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
self.pieceDropped()
def pieceDropped(self):
'''after dropping shape, remove full lines and create new shape'''
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.setShapeAt(x, y, self.curPiece.shape())
self.removeFullLines()
if not self.isWaitingAfterLine:
self.newPiece()
def removeFullLines(self):
'''removes all full lines from the board'''
numFullLines = 0
rowsToRemove = []
for i in range(Board.BoardHeight):
n = 0
for j in range(Board.BoardWidth):
if not self.shapeAt(j, i) == Tetrominoe.NoShape:
n = n + 1
if n == 10:
rowsToRemove.append(i)
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.setShapeAt(l, k, self.shapeAt(l, k + 1))
numFullLines = numFullLines + len(rowsToRemove)
if numFullLines > 0:
self.numLinesRemoved = self.numLinesRemoved + numFullLines
self.msg2Statusbar.emit(str(self.numLinesRemoved))
self.isWaitingAfterLine = True
self.curPiece.setShape(Tetrominoe.NoShape)
self.update()
def newPiece(self):
'''creates a new shape'''
self.curPiece = Shape()
self.curPiece.setRandomShape()
self.curX = Board.BoardWidth // 2 + 1
self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
if not self.tryMove(self.curPiece, self.curX, self.curY):
self.curPiece.setShape(Tetrominoe.NoShape)
self.timer.stop()
self.isStarted = False
self.msg2Statusbar.emit("Game over")
def tryMove(self, newPiece, newX, newY):
'''tries to move a shape'''
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
return False
if self.shapeAt(x, y) != Tetrominoe.NoShape:
return False
self.curPiece = newPiece
self.curX = newX
self.curY = newY
self.update()
return True
def drawSquare(self, painter, x, y, shape):
'''draws a square of a shape'''
colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
color = QColor(colorTable[shape])
painter.fillRect(x + 1, y + 1, self.squareWidth() - 2,
self.squareHeight() - 2, color)
painter.setPen(color.lighter())
painter.drawLine(x, y + self.squareHeight() - 1, x, y)
painter.drawLine(x, y, x + self.squareWidth() - 1, y)
painter.setPen(color.darker())
painter.drawLine(x + 1, y + self.squareHeight() - 1,
x + self.squareWidth() - 1, y + self.squareHeight() - 1)
painter.drawLine(x + self.squareWidth() - 1,
y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
class Tetrominoe(object):
NoShape = 0
ZShape = 1
SShape = 2
LineShape = 3
TShape = 4
SquareShape = 5
LShape = 6
MirroredLShape = 7
class Shape(object):
coordsTable = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
((0, -1), (0, 0), (1, 0), (1, 1)),
((0, -1), (0, 0), (0, 1), (0, 2)),
((-1, 0), (0, 0), (1, 0), (0, 1)),
((0, 0), (1, 0), (0, 1), (1, 1)),
((-1, -1), (0, -1), (0, 0), (0, 1)),
((1, -1), (0, -1), (0, 0), (0, 1))
)
def __init__(self):
self.coords = [[0,0] for i in range(4)]
self.pieceShape = Tetrominoe.NoShape
self.setShape(Tetrominoe.NoShape)
def shape(self):
'''returns shape'''
return self.pieceShape
def setShape(self, shape):
'''sets a shape'''
table = Shape.coordsTable[shape]
for i in range(4):
for j in range(2):
self.coords[i][j] = table[i][j]
self.pieceShape = shape
def setRandomShape(self):
'''chooses a random shape'''
self.setShape(random.randint(1, 7))
def x(self, index):
'''returns x coordinate'''
return self.coords[index][0]
def y(self, index):
'''returns y coordinate'''
return self.coords[index][1]
def setX(self, index, x):
'''sets x coordinate'''
self.coords[index][0] = x
def setY(self, index, y):
'''sets y coordinate'''
self.coords[index][1] = y
def minX(self):
'''returns min x value'''
m = self.coords[0][0]
for i in range(4):
m = min(m, self.coords[i][0])
return m
def maxX(self):
'''returns max x value'''
m = self.coords[0][0]
for i in range(4):
m = max(m, self.coords[i][0])
return m
def minY(self):
'''returns min y value'''
m = self.coords[0][1]
for i in range(4):
m = min(m, self.coords[i][1])
return m
def maxY(self):
'''returns max y value'''
m = self.coords[0][1]
for i in range(4):
m = max(m, self.coords[i][1])
return m
def rotateLeft(self):
'''rotates shape to the left'''
if self.pieceShape == Tetrominoe.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.setX(i, self.y(i))
result.setY(i, -self.x(i))
return result
def rotateRight(self):
'''rotates shape to the right'''
if self.pieceShape == Tetrominoe.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.setX(i, -self.y(i))
result.setY(i, self.x(i))
return result
if __name__ == '__main__':
app = QApplication([])
tetris = Tetris()
sys.exit(app.exec_())
游戏进行了简化,以便于理解。 游戏启动后立即开始。 我们可以通过按 p
键暂停游戏。 Space
键将使俄罗斯方块立即下降到底部。 游戏以恒定速度进行,没有实现加速。 分数是我们已删除的行数。
self.tboard = Board(self)
self.setCentralWidget(self.tboard)
创建Board
类的实例,并将其设置为应用的中央窗口小部件。
self.statusbar = self.statusBar()
self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)
我们创建一个状态栏,在其中显示消息。 我们将显示三种可能的消息:已删除的行数,暂停的消息或游戏结束消息。 msg2Statusbar
是在Board
类中实现的自定义信号。 showMessage()
是一种内置方法,可在状态栏上显示一条消息。
self.tboard.start()
这条线启动了游戏。
class Board(QFrame):
msg2Statusbar = pyqtSignal(str)
...
使用pyqtSignal
创建自定义信号。 msg2Statusbar
是当我们要向状态栏写消息或乐谱时发出的信号。
BoardWidth = 10
BoardHeight = 22
Speed = 300
这些是Board's
类变量。 BoardWidth
和BoardHeight
以块为单位定义电路板的大小。 Speed
定义游戏的速度。 每 300 毫秒将开始一个新的游戏周期。
...
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
...
在initBoard()
方法中,我们初始化了一些重要的变量。 变量self.board
是从 0 到 7 的数字的列表。它表示各种形状的位置以及板上形状的其余部分。
def shapeAt(self, x, y):
'''determines shape at the board position'''
return self.board[(y * Board.BoardWidth) + x]
shapeAt()
方法确定给定块上的形状类型。
def squareWidth(self):
'''returns the width of one square'''
return self.contentsRect().width() // Board.BoardWidth
电路板可以动态调整大小。 结果,块的大小可能改变。 squareWidth()
计算单个正方形的宽度(以像素为单位)并将其返回。 Board.BoardWidth
是板的大小,以块为单位。
def pause(self):
'''pauses game'''
if not self.isStarted:
return
self.isPaused = not self.isPaused
if self.isPaused:
self.timer.stop()
self.msg2Statusbar.emit("paused")
else:
self.timer.start(Board.Speed, self)
self.msg2Statusbar.emit(str(self.numLinesRemoved))
self.update()
pause()
方法暂停游戏。 它停止计时器并在状态栏上显示一条消息。
def paintEvent(self, event):
'''paints all shapes of the game'''
painter = QPainter(self)
rect = self.contentsRect()
...
绘图以paintEvent()
方法进行。 QPainter
负责 PyQt5 中的所有低级绘图。
for i in range(Board.BoardHeight):
for j in range(Board.BoardWidth):
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
if shape != Tetrominoe.NoShape:
self.drawSquare(painter,
rect.left() + j * self.squareWidth(),
boardTop + i * self.squareHeight(), shape)
游戏的绘图分为两个步骤。 在第一步中,我们绘制所有形状或已放置到板底部的形状的其余部分。 所有正方形都记在self.board
列表变量中。 使用shapeAt()
方法访问该变量。
if self.curPiece.shape() != Tetrominoe.NoShape:
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.drawSquare(painter, rect.left() + x * self.squareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
self.curPiece.shape())
下一步是下落的实际零件的图纸。
elif key == Qt.Key_Right:
self.tryMove(self.curPiece, self.curX + 1, self.curY)
在keyPressEvent()
方法中,我们检查按键是否按下。 如果按向右箭头键,我们将尝试将棋子向右移动。 我们说尝试,因为那条可能无法移动。
elif key == Qt.Key_Up:
self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY)
向上
方向键将使下降片向左旋转。
elif key == Qt.Key_Space:
self.dropDown()
空格键
将立即将下降的片段降到底部。
elif key == Qt.Key_D:
self.oneLineDown()
按下 d
键,乐曲将向下移动一个格。 它可以用来加速一块的下落。
def timerEvent(self, event):
'''handles timer event'''
if event.timerId() == self.timer.timerId():
if self.isWaitingAfterLine:
self.isWaitingAfterLine = False
self.newPiece()
else:
self.oneLineDown()
else:
super(Board, self).timerEvent(event)
在计时器事件中,我们要么在上一个下降到底部之后创建一个新作品,要么将下降的一块向下移动一行。
def clearBoard(self):
'''clears shapes from the board'''
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoe.NoShape)
clearBoard()
方法通过在板的每个块上设置Tetrominoe.NoShape
来清除板。
def removeFullLines(self):
'''removes all full lines from the board'''
numFullLines = 0
rowsToRemove = []
for i in range(Board.BoardHeight):
n = 0
for j in range(Board.BoardWidth):
if not self.shapeAt(j, i) == Tetrominoe.NoShape:
n = n + 1
if n == 10:
rowsToRemove.append(i)
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.setShapeAt(l, k, self.shapeAt(l, k + 1))
numFullLines = numFullLines + len(rowsToRemove)
...
如果片段触底,我们将调用removeFullLines()
方法。 我们找出所有实线并将其删除。 通过将所有行移动到当前全行上方来将其向下移动一行来实现。 请注意,我们颠倒了要删除的行的顺序。 否则,它将无法正常工作。 在我们的情况下,我们使用朴素重力。 这意味着碎片可能会漂浮在空的间隙上方。
def newPiece(self):
'''creates a new shape'''
self.curPiece = Shape()
self.curPiece.setRandomShape()
self.curX = Board.BoardWidth // 2 + 1
self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
if not self.tryMove(self.curPiece, self.curX, self.curY):
self.curPiece.setShape(Tetrominoe.NoShape)
self.timer.stop()
self.isStarted = False
self.msg2Statusbar.emit("Game over")
newPiece()
方法随机创建一个新的俄罗斯方块。 如果棋子无法进入其初始位置,则游戏结束。
def tryMove(self, newPiece, newX, newY):
'''tries to move a shape'''
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
return False
if self.shapeAt(x, y) != Tetrominoe.NoShape:
return False
self.curPiece = newPiece
self.curX = newX
self.curY = newY
self.update()
return True
在tryMove()
方法中,我们尝试移动形状。 如果形状在板的边缘或与其他零件相邻,则返回False
。 否则,我们将当前的下降片放到新位置。
class Tetrominoe(object):
NoShape = 0
ZShape = 1
SShape = 2
LineShape = 3
TShape = 4
SquareShape = 5
LShape = 6
MirroredLShape = 7
Tetrominoe
类包含所有可能形状的名称。 我们还有一个NoShape
用于空白。
Shape
类保存有关俄罗斯方块的信息。
class Shape(object):
coordsTable = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
...
)
...
coordsTable
元组保存了俄罗斯方块的所有可能的坐标值。 这是一个模板,所有零件均从该模板获取其坐标值。
self.coords = [[0,0] for i in range(4)]
创建后,我们将创建一个空坐标列表。 该列表将保存俄罗斯方块的坐标。
图:坐标
上面的图片将帮助您更多地了解坐标值。 例如,元组(0,-1),(0,0),(-1,0),(-1,-1)表示 Z 形。 该图说明了形状。
def rotateLeft(self):
'''rotates shape to the left'''
if self.pieceShape == Tetrominoe.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.setX(i, self.y(i))
result.setY(i, -self.x(i))
return result
rotateLeft()
方法将一块向左旋转。 正方形不必旋转。 这就是为什么我们只是将引用返回到当前对象。 将创建一个新零件,并将其坐标设置为旋转零件的坐标。
图:俄罗斯方块
这是 PyQt5 中的俄罗斯方块游戏。
菜单和工具栏
http://zetcode.com/tutorials/ironpythontutorial/menustoolbars/
在 IronPython Mono Winforms 教程的这一部分中,我们将讨论菜单和工具栏。
菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。
简单菜单
在第一个示例中,我们创建一个简单的菜单。
simplemenu.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import Keys, MenuStrip, ToolStripMenuItem
from System.Drawing import Size
class IForm(Form):
def __init__(self):
self.Text = 'Simple Menu'
self.Size = Size(250, 200)
ms = MenuStrip()
ms.Parent = self
filem = ToolStripMenuItem("&File")
exit = ToolStripMenuItem("&Exit", None,
self.OnExit)
exit.ShortcutKeys = Keys.Control | Keys.X
filem.DropDownItems.Add(exit)
ms.Items.Add(filem)
self.MainMenuStrip = ms
self.CenterToScreen()
def OnExit(self, sender, event):
self.Close()
Application.Run(IForm())
在我们的示例中,我们有一个菜单栏和一个菜单。 菜单内有一个菜单项。 如果选择菜单项,则应用关闭。
注意如何关闭应用。 我们可以使用 Ctrl + X
快捷方式或按 Alt + F + E
键关闭它 。
ms = MenuStrip()
MenuStrip
为我们的表单创建一个菜单系统。 我们将ToolStripMenuItem
对象添加到MenuStrip
,它们表示菜单结构中的各个菜单命令。 每个ToolStripMenuItem
可以是您的应用的命令,也可以是其他子菜单项的父菜单。
filem = ToolStripMenuItem("&File")
在这里,我们创建一个菜单。
exit = ToolStripMenuItem("&Exit", None,
self.OnExit)
此行创建退出菜单项。
exit.ShortcutKeys = Keys.Control | Keys.X
我们提供了退出菜单项的快捷方式。
filem.DropDownItems.Add(exit)
退出菜单项将添加到菜单对象的下拉项中。
ms.Items.Add(filem)
在这里,我们将菜单对象添加到菜单栏中。
self.MainMenuStrip = ms
MenuStrip
已插入表格。
图:简单菜单
子菜单
每个菜单项也可以有一个子菜单。 这样,我们可以将类似的命令分组。 例如,我们可以将隐藏或显示各种工具栏(例如个人栏,地址栏,状态栏或导航栏)的命令放入称为工具栏的子菜单中。
submenu.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import MenuStrip, ToolStripMenuItem
from System.Drawing import Size
class IForm(Form):
def __init__(self):
self.Text = 'Simple Menu'
self.Size = Size(380, 200)
ms = MenuStrip()
ms.Parent = self
filem = ToolStripMenuItem("&File")
exit = ToolStripMenuItem("&Exit", None,
self.OnExit)
importm = ToolStripMenuItem()
importm.Text = "Import"
filem.DropDownItems.Add(importm)
temp = ToolStripMenuItem()
temp.Text = "Import newsfeed list..."
importm.DropDownItems.Add(temp)
temp = ToolStripMenuItem()
temp.Text = "Import bookmarks..."
importm.DropDownItems.Add(temp)
temp = ToolStripMenuItem()
temp.Text = "Import mail..."
importm.DropDownItems.Add(temp)
filem.DropDownItems.Add(exit)
ms.Items.Add(filem)
self.MainMenuStrip = ms
self.CenterToScreen()
def OnExit(self, sender, event):
self.Close()
Application.Run(IForm())
在此示例中,我们创建一个子菜单。 子菜单导入具有三个菜单项。
importm = ToolStripMenuItem()
importm.Text = "Import"
ToolStripMenuItem
可以是菜单或菜单项。 在这里它将作为子菜单。
temp = ToolStripMenuItem()
temp.Text = "Import newsfeed list..."
importm.DropDownItems.Add(temp)
在这里,我们创建一个菜单项并将其添加到“导入”子菜单。
图:子菜单
检查菜单项
下一个代码示例演示如何创建选中的菜单项。
checkmenuitem.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import Shortcut, MainMenu, MenuItem
from System.Drawing import Size
class IForm(Form):
def __init__(self):
self.Text = 'Simple Menu'
self.sb = StatusBar()
self.sb.Parent = self
self.sb.Text = "Ready"
mainMenu = MainMenu()
filem = mainMenu.MenuItems.Add("&File")
filem.MenuItems.Add(MenuItem("E&xit",
self.OnExit, Shortcut.CtrlX))
view = mainMenu.MenuItems.Add("&View")
self.viewStatusBar = MenuItem("View StatusBar")
self.viewStatusBar.Checked = True
self.viewStatusBar.Click += self.ToggleStatusBar
view.MenuItems.Add(self.viewStatusBar)
self.Menu = mainMenu
self.Size = Size(250, 200)
self.CenterToScreen()
def OnExit(self, sender, event):
self.Close()
def ToggleStatusBar(self, sender, event):
check = self.viewStatusBar.Checked
if (check):
self.sb.Visible = False
self.viewStatusBar.Checked = False
else:
self.sb.Visible = True
self.viewStatusBar.Checked = True
Application.Run(IForm())
我们有两个菜单:“文件”和“查看”。 “查看”菜单具有一个菜单项,用于切换状态栏的可见性。
mainMenu = MainMenu()
在此示例中,我们使用MainMenu
控件。 要创建菜单栏,我们可以使用MainMenu
或MenuStrip
控件。 后者具有一些附加功能。
self.viewStatusBar.Checked = True
默认情况下会选中此菜单项,因为状态栏从应用的开始就可见。
check = self.viewStatusBar.Checked
if (check):
self.sb.Visible = False
self.viewStatusBar.Checked = False
else:
self.sb.Visible = True
self.viewStatusBar.Checked = True
我们确定菜单项是否被选中。 我们根据check
值显示和隐藏状态栏和复选标记。
图:选中菜单项
图像,分隔符
我们将进一步增强对MenuStrip
控件的了解。 我们将创建一个带有图像的菜单项,并显示如何使用分隔符将其分开。
menustrip.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, MenuStrip
from System.Windows.Forms import ToolStripMenuItem, ToolStripSeparator
from System.Drawing import Size, Image
class IForm(Form):
def __init__(self):
self.Text = 'MenuStrip'
self.Size = Size(250, 200)
menuStrip = MenuStrip()
titem1 = ToolStripMenuItem("File")
menuStrip.Items.Add(titem1)
titem2 = ToolStripMenuItem("Tools")
menuStrip.Items.Add(titem2)
subm1 = ToolStripMenuItem("New")
subm1.Image = Image.FromFile("new.png")
titem1.DropDownItems.Add(subm1)
subm2 = ToolStripMenuItem("Open")
subm2.Image = Image.FromFile("open.png")
titem1.DropDownItems.Add(subm2)
titem1.DropDownItems.Add(ToolStripSeparator())
subm3 = ToolStripMenuItem("Exit")
subm3.Image = Image.FromFile("exit.png")
titem1.DropDownItems.Add(subm3)
subm3.Click += self.OnExit
self.Controls.Add(menuStrip)
self.MainMenuStrip = menuStrip
self.CenterToScreen()
def OnExit(self, sender, event):
self.Close()
Application.Run(IForm())
在代码示例中,我们有两个菜单:“文件”和“工具”。 在文件中,我们有三个带有图像的菜单项。 我们还有一个分隔符。 在此示例中,png 图像必须位于当前工作目录中。
subm1 = ToolStripMenuItem("New")
subm1.Image = Image.FromFile("new.png")
titem1.DropDownItems.Add(subm1)
在这里,我们创建第一个菜单项。 要将图像添加到项目,我们将Image
属性设置为图像。 我们使用FromFile()
方法从指定的文件创建一个Image
。
titem1.DropDownItems.Add(ToolStripSeparator())
在这里,我们向“文件”菜单添加分隔符。
图:图像 s and separator
工具栏
菜单将我们可以在应用中使用的所有命令分组。 使用工具栏可以快速访问最常用的命令。 ToolBar
控件用于显示ToolBarButton
控件。 我们可以通过创建ImageList
将图像分配给按钮。 然后,我们将图像列表分配给工具栏的ImageList
属性,并为每个ToolBarButton
将图像索引值分配给ImageIndex
属性。
toolbar.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
clr.AddReference("System")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import ToolBar, ToolBarButton, ImageList
from System.Drawing import Size, Icon
class IForm(Form):
def __init__(self):
self.Text = 'Simple ToolBar'
self.Size = Size(250, 200)
toolBar = ToolBar()
toolBarIcons = ImageList()
save = ToolBarButton()
exit = ToolBarButton()
save.ImageIndex = 0
save.Tag = "Save"
exit.ImageIndex = 1
exit.Tag = "Exit"
toolBar.ImageList = toolBarIcons
toolBar.ShowToolTips = True
toolBar.Buttons.AddRange((save, exit))
toolBar.ButtonClick += self.OnClicked
toolBarIcons.ImageSize = Size(16, 16)
toolBarIcons.Images.Add(Icon("new.ico"))
toolBarIcons.Images.Add(Icon("exit.ico"))
self.Controls.Add(toolBar)
self.CenterToScreen()
def OnClicked(self, sender, event):
if event.Button.Tag == "Exit":
self.Close()
Application.Run(IForm())
在我们的示例中,我们在工具栏上显示了两个按钮。
toolBar = ToolBar()
在这里,我们创建ToolBar
控件。
toolBarIcons = ImageList()
创建图像列表。
save = ToolBarButton()
exit = ToolBarButton()
这是两个工具栏按钮。
save.ImageIndex = 0
我们确定图像列表中的哪个图标将用于保存工具栏按钮。
toolBar.Buttons.AddRange((save, exit))
ToolBarButton
控件已添加到工具栏。
toolBarIcons.Images.Add(Icon("new.ico"))
toolBarIcons.Images.Add(Icon("exit.ico"))
图标将添加到图像列表。
if event.Button.Tag == "Exit":
self.Close()
如果按钮的标签等于"Exit"
,我们将关闭该应用。
图:工具栏
IronPython Winforms 教程的这一部分是关于菜单和工具栏的。
Mono Winforms 中的基本控件
原文: http://zetcode.com/tutorials/ironpythontutorial/controls/
IronPython Mono Winforms 编程教程的这一部分将介绍基本控件。
Winforms 控件是应用的基本构建块。 Winforms 具有广泛的各种控件。 按钮,复选框,轨迹栏,标签等。程序员完成工作所需的一切。 在本教程的这一部分中,我们将描述几个有用的控件。
Label
Label
是用于显示文本或图像的简单控件。 它没有得到关注。
label.py
#!/usr/bin/ipy
import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, Label
from System.Drawing import Size, Point, Font
text = """Meet you downstairs in the bar and heard
Your rolled up sleeves and your skull t-shirt
You say why did you do it with him today?
And sniffed me out like I was tanqueray
Cause you're my fella, my guy
Hand me your stella and fly
By the time I'm out the door
You tear me down like roger moore
I cheated myself
Like I knew I would
I told ya, I was trouble
You know that I'm no good
Upstairs in bed, with my ex boy
He's in a place, but I cant get joy
Thinking of you in the final throws, this is when my buzzer goes"""
class IForm(Form):
def __init__(self):
self.Text = "You know I'm No Good"
font = Font("Serif", 10)
lyrics = Label()
lyrics.Parent = self
lyrics.Text = text
lyrics.Font = font
lyrics.Location = Point(10, 10)
lyrics.Size = Size(290, 290)
self.CenterToScreen()
Application.Run(IForm())
在我们的示例中,我们在表单上显示了一些歌词。
lyrics = Label()
Label
控件已创建。
text = """Meet you downstairs in the bar and heard
... """
这是我们的文字。
font = Font("Serif", 10)
...
lyrics.Font = font
标签文本的字体设置为 10px Serif。
图:Label
CheckBox
CheckBox
是具有两个状态的控件:开和关。 它是带有标签或图像的盒子。 如果选中CheckBox
,则在方框中用勾号表示。 CheckBox
可用于在启动时显示或隐藏启动画面,切换工具栏的可见性等。
checkbox.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, CheckBox
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = "CheckBox"
self.Size = Size(220, 170)
cb = CheckBox()
cb.Parent = self
cb.Location = Point(30, 30)
cb.Text = "Show Title"
cb.Checked = True
cb.CheckedChanged += self.OnChanged
self.CenterToScreen()
def OnChanged(self, sender, event):
if sender.Checked:
self.Text = "CheckBox"
else:
self.Text = ""
Application.Run(IForm())
我们的代码示例根据窗口的状态显示或隐藏窗口的标题。
cb = CheckBox()
CheckBox
控件已创建。
cb.Text = "Show Title"
cb.Checked = True
当应用启动时,我们显示标题。 然后将CheckBox
控件设置为选中状态。
cb.CheckedChanged += self.OnChanged
当我们单击CheckBox
控件时,将触发CheckedChanged
事件。
if sender.Checked:
self.Text = "CheckBox"
else:
self.Text = ""
在这里,我们切换窗口的标题。
图:CheckBox
TrackBar
TrackBar
是一个组件,使用户可以通过在有限的间隔内滑动旋钮来以图形方式选择一个值。 我们的示例将显示音量控制。
trackbar.py
#!/usr/bin/ipy
import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, PictureBox
from System.Windows.Forms import TrackBar, TickStyle
from System.Drawing import Size, Point, Bitmap
class IForm(Form):
def __init__(self):
self.Text = 'TrackBar'
self.Size = Size(260, 190)
tb = TrackBar()
tb.Parent = self
tb.Size = Size(150, 30)
tb.Location = Point(30, 50)
tb.TickStyle = TickStyle.None
tb.SetRange(0, 100)
tb.ValueChanged += self.OnChanged
self.LoadImages()
self.pb = PictureBox()
self.pb.Parent = self
self.pb.Location = Point(210, 50)
self.pb.Image = self.mutep
self.CenterToScreen()
def LoadImages(self):
try:
self.mutep = Bitmap("mute.png")
self.minp = Bitmap("min.png")
self.medp = Bitmap("med.png")
self.maxp = Bitmap("max.png")
except Exception, e:
print "Error reading images"
print e.msg
sys.exit(1)
def OnChanged(self, sender, event):
val = sender.Value
if val == 0:
self.pb.Image = self.mutep
elif val > 0 and val <= 30:
self.pb.Image = self.minp
elif val > 30 and val < 80:
self.pb.Image = self.medp
else: self.pb.Image = self.maxp
Application.Run(IForm())
在代码示例中,我们显示了TrackBar
和PictureBox
。 通过拖动轨迹栏,我们可以在PictureBox
控件上更改图像。
tb = TrackBar()
TrackBar
控件已创建。
tb.TickStyle = TickStyle.None
我们对此TrackBar
没有显示任何报价。
self.pb = PictureBox()
...
self.pb.Image = self.mutep
PictureBox
控件已创建。 它用于显示图像。 开始时,它会显示静音图像。
self.mutep = Bitmap("mute.png")
self.minp = Bitmap("min.png")
self.medp = Bitmap("med.png")
self.maxp = Bitmap("max.png")
在这里,我们将创建四个将要使用的图像。
val = sender.Value
if val == 0:
self.pb.Image = self.mutep
elif val > 0 and val <= 30:
self.pb.Image = self.minp
elif val > 30 and val < 80:
self.pb.Image = self.medp
else: self.pb.Image = self.maxp
我们确定TrackBar
的值。 根据其值,我们更新PictureBox
控件。
图:TrackBar
ComboBox
ComboBox
是一个组合了按钮或可编辑字段和下拉列表的控件。 用户可以从下拉列表中选择一个值,该列表应用户的要求出现。 如果使组合框可编辑,则组合框将包含一个可编辑字段,用户可以在其中输入值。
combobox.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import ComboBox, Label
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = "ComboBox"
self.Size = Size(240, 240)
cb = ComboBox()
cb.Parent = self
cb.Location = Point(50, 30)
cb.Items.AddRange(("Ubuntu",
"Mandriva",
"Red Hat",
"Fedora",
"Gentoo"))
cb.SelectionChangeCommitted += self.OnChanged
self.label = Label()
self.label.Location = Point(50, 140)
self.label.Parent = self
self.label.Text = "..."
self.CenterToScreen()
def OnChanged(self, sender, event):
self.label.Text = sender.Text
Application.Run(IForm())
我们的代码编程示例显示了一个包含五个项目的组合框。 所选项目显示在标签控件中。
cb = ComboBox()
ComboBox
控件已创建。
cb.Items.AddRange(("Ubuntu",
"Mandriva",
"Red Hat",
"Fedora",
"Gentoo"))
ComboBox
控件中充满了项目。
cb.SelectionChangeCommitted += self.OnChanged
如果我们从组合框中选择一个项目,则会触发SelectionChangeCommitted
事件。
def OnChanged(self, sender, event):
self.label.Text = sender.Text
在这里,将从组合框中选择的文本复制到标签。
图:ComboBox
我们已经完成了 IronPython Mono Winforms 教程的这一章,专门讨论基本控件。
Mono Winforms 中的基本控件 II
原文: http://zetcode.com/tutorials/ironpythontutorial/controlsII/
在 IronPython Mono Winforms 教程的这一部分中,我们将继续介绍基本的 Mono Winforms 控件。
RadioButton
与其他RadioButton
控件配对时,RadioButton
控件使用户能够从一组选项中选择一个选项。 GroupBox
控件用于将单选按钮配对在一起。
radiobutton.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import RadioButton, GroupBox
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = "RadioButton"
self.Size = Size(240, 240)
gb = GroupBox()
gb.Text = "Sex"
gb.Size = Size(120, 110)
gb.Location = Point(20, 20)
gb.Parent = self
male = RadioButton()
male.Text = "Male"
male.Parent = gb
male.Location = Point(10, 30)
male.CheckedChanged += self.OnChanged
female = RadioButton()
female.Text = "Female"
female.Parent = gb
female.Location = Point(10, 60)
female.CheckedChanged += self.OnChanged
self.statusbar = StatusBar()
self.statusbar.Parent = self
self.CenterToScreen()
def OnChanged(self, sender, event):
if sender.Checked:
self.statusbar.Text = sender.Text
Application.Run(IForm())
在我们的示例中,我们在一个组框中显示了两个单选按钮。 一次只能选择一个选项。 选项值显示在状态栏中。
gb = GroupBox()
gb.Text = "Sex"
GroupBox
控件用于将单选按钮组合在一起。 这样,我们一次只能选择一个单选按钮控件。
male = RadioButton()
male.Text = "Male"
male.Parent = gb
创建带有文本"Male"
的RadioButton
控件。 它的父级是组框控件。
def OnChanged(self, sender, event):
if sender.Checked:
self.statusbar.Text = sender.Text
OnChanged()
方法将当前所选单选按钮的文本设置为状态栏控件。
图:RadioButton
MonthCalendar
在下一个示例中,我们将显示MonthCalendar
控件。 MonthCalendar
控件允许用户使用视觉显示选择日期。
monthcalendar.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import Label, MonthCalendar
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'MonthCalendar'
self.Size = Size(240, 240)
calendar = MonthCalendar()
calendar.Parent = self
calendar.Location = Point(20, 20)
calendar.DateSelected += self.OnSelected
self.date = Label()
self.date.Location = Point(30, 180)
self.date.Parent = self
dt = calendar.SelectionStart
self.date.Text = str(dt.Month) + "/" + str(dt.Day) + "/" + str(dt.Year)
self.CenterToScreen()
def OnSelected(self, sender, event):
dt = sender.SelectionStart
self.date.Text = str(dt.Month) + "/" + str(dt.Day) + "/" + str(dt.Year)
Application.Run(IForm())
在示例中,我们显示了MonthCalendar
和Label
。
calendar = MonthCalendar()
...
self.date = Label()
我们有两个控件。 一个MonthCalendar
和一个Label
。 后者显示当前选择的日期。
def OnSelected(self, sender, event):
dt = sender.SelectionStart
self.date.Text = str(dt.Month) + "/" + str(dt.Day) + "/" + str(dt.Year)
当我们从MonthCalendar
中选择一个日期时,就会调用OnSelected()
方法。 SelectionStart
属性获取所选日期范围的开始日期。
图:MonthCalendar
TextBox
TextBox
控件用于显示或接受某些文本。 文本可以是单行或多行。 此控件还可以进行密码屏蔽。
textbox.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Windows.Forms import Label, TextBox
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'TextBox'
self.text = Label()
self.text.Parent = self
self.text.Text = "..."
self.text.AutoSize = True
self.text.Location = Point(60, 40)
tbox = TextBox()
tbox.Parent = self
tbox.Location = Point(60, 100)
tbox.KeyUp += self.OnKeyUp
self.Size = Size(250, 200)
self.CenterToScreen()
def OnKeyUp(self, sender, event):
self.text.Text = sender.Text
Application.Run(IForm())
本示例显示一个文本框和一个标签。 我们在文本框中键入的文本将立即显示在标签控件中。
self.text = Label()
...
self.text.AutoSize = True
Label
控件已创建。 AutoSize
属性确保Label
增长以显示文本。
tbox = TextBox()
...
tbox.KeyUp += self.OnKeyUp
我们将KeyUp
事件插入到TextBox
控件中。 释放按键时,将调用OnKeyUp()
方法。
def OnKeyUp(self, sender, event):
self.text.Text = sender.Text
在OnKeyUp()
方法中,我们使用文本框控件中的文本更新了标签控件。
图:TextBox
PictureBox
PictureBox
控件用于在表单上显示图片。
picturebox.py
#!/usr/bin/ipy
import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, PictureBox
from System.Drawing import Size, Point, Bitmap
class IForm(Form):
def __init__(self):
self.Text = 'PictureBox'
try:
castle = Bitmap('redrock.png')
except Exception, e:
print 'Cannot read image file'
print e.msg
sys.exit(1)
pb = PictureBox()
pb.Parent = self
pb.Size = Size(castle.Width, castle.Height)
pb.Location = Point(2, 2)
pb.Image = castle
self.Size = Size(castle.Width, castle.Height)
self.CenterToScreen()
Application.Run(IForm())
该示例显示了表单上的 png 图像。
try:
castle = Bitmap('redrock.png')
except Exception, e:
print 'Cannot read image file'
print e.msg
sys.exit(1)
我们从当前工作目录中获得一个位图。
pb = PictureBox()
PictureBox
控件已创建。
pb.Image = castle
Image
属性指向我们创建的位图。
self.Size = Size(castle.Width, castle.Height)
窗体的大小等于位图的大小。
图:PictureBox
我们已经完成了 Mono Winforms 编程教程的这一章,专门讨论基本控件。
Mono Winforms 中的高级控件
原文: http://zetcode.com/tutorials/ironpythontutorial/advancedcontrols/
在 IronPython Mono Winforms 教程的这一部分中,我们介绍一些更高级的控件。 即ListBox
,ListView
和TreeView
控件。
ListBox
ListBox
控件用于显示项目列表。 用户可以通过单击选择一个或多个项目。
listbox.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import ListBox, DockStyle
from System.Drawing import Size
class IForm(Form):
def __init__(self):
self.Text = "ListBox"
lb = ListBox()
lb.Parent = self
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 += self.OnChanged
self.sb = StatusBar()
self.sb.Parent = self
self.Size = Size(220, 220)
self.CenterToScreen()
def OnChanged(self, sender, event):
self.sb.Text = sender.SelectedItem
Application.Run(IForm())
我们的示例显示了一个具有六个名称的列表框。 所选项目显示在状态栏中。
lb = ListBox()
lb.Parent = self
ListBox
控件已创建。
lb.Items.Add("Jessica")
这就是我们向ListBox
控件添加新项目的方式。 该控件具有Items
属性。 该属性是对列表框中项目列表的引用。 使用此引用,我们可以添加,删除或获取列表框中的项目数。
lb.SelectedIndexChanged += self.OnChanged
当我们选择一个项目时,会触发SelectedIndexChanged
事件。
def OnChanged(self, sender, event):
self.sb.Text = sender.SelectedItem
在OnChange()
方法内,我们获得列表框的选定值并将其设置为状态栏。
图:ListBox
ListView
ListView
控件用于显示项目集合。 它是比ListBox
控件更复杂的控件。 它可以在各种视图中显示数据。 它主要用于在多列视图中显示数据。
listview.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import ListView, View, ColumnHeader
from System.Windows.Forms import ListViewItem, DockStyle, SortOrder
from System.Drawing import Size
class IForm(Form):
def __init__(self):
self.Text = 'ListBox'
actresses = { 'Jessica Alba' : '1981', 'Angelina Jolie' : '1975',
'Natalie Portman' : '1981', 'Rachel Weiss' : '1971',
'Scarlett Johansson' : 1984 }
name = ColumnHeader()
name.Text = 'Name'
name.Width = -1
year = ColumnHeader()
year.Text = 'Year'
self.SuspendLayout()
lv = ListView()
lv.Parent = self
lv.FullRowSelect = True
lv.GridLines = True
lv.AllowColumnReorder = True
lv.Sorting = SortOrder.Ascending
lv.Columns.AddRange((name, year))
lv.ColumnClick += self.OnColumnClick
for act in actresses.keys():
item = ListViewItem()
item.Text = act
item.SubItems.Add(str(actresses[act]))
lv.Items.Add(item)
lv.Dock = DockStyle.Fill
lv.Click += self.OnChanged
self.sb = StatusBar()
self.sb.Parent = self
lv.View = View.Details
self.ResumeLayout()
self.Size = Size(350, 300)
self.CenterToScreen()
def OnChanged(self, sender, event):
name = sender.SelectedItems[0].SubItems[0].Text
born = sender.SelectedItems[0].SubItems[1].Text
self.sb.Text = name + ', ' + born
def OnColumnClick(self, sender, event):
if sender.Sorting == SortOrder.Ascending:
sender.Sorting = SortOrder.Descending
else:
sender.Sorting = SortOrder.Ascending
Application.Run(IForm())
在我们的示例中,我们有一个包含两列的列表视图。 在第一列中,我们显示女演员的名字。 在第二个他们的出生日期。 数据存储在List
集合中。 通过选择一行,一行中的数据将显示在状态栏中。 另外,通过单击列标题,可以对数据进行排序。
actresses = { 'Jessica Alba' : '1981', 'Angelina Jolie' : '1975',
'Natalie Portman' : '1981', 'Rachel Weiss' : '1971',
'Scarlett Johansson' : 1984 }
我们将数据存储在女演员词典中。
name = ColumnHeader()
name.Text = 'Name'
name.Width = -1
对于列表视图中的每一列,我们创建一个ColumnHeader
。 通过将Width
设置为 -1,列的宽度等于列中最长的项目。
lv = ListView()
lv.Parent = self
ListView
控件已创建。
lv.FullRowSelect = True
lv.GridLines = True
lv.AllowColumnReorder = True
lv.Sorting = SortOrder.Ascending
在这里,我们设置控件的四个属性。 该代码行支持全行选择,显示网格线,通过拖动列对列进行重新排序并以升序对数据进行排序。
lv.Columns.AddRange((name, year))
在这里,我们将两个ColumnHeader
添加到ListView
控件中。
for act in actresses.keys():
item = ListViewItem()
item.Text = act
item.SubItems.Add(str(actresses[act]))
lv.Items.Add(item)
此循环填充列表视图控件。 每行作为ListViewItem
类添加到列表视图。
lv.View = View.Details
ListView
控件可以具有不同的视图。 不同的视图以不同的方式显示数据。
name = sender.SelectedItems[0].SubItems[0].Text
born = sender.SelectedItems[0].SubItems[1].Text
self.sb.Text = name + ', ' + born
在OnChanged()
方法内部,我们从选定的行中获取数据并将其显示在状态栏上。
if sender.Sorting == SortOrder.Ascending:
sender.Sorting = SortOrder.Descending
else:
sender.Sorting = SortOrder.Ascending
在这里,我们切换列的排序顺序。
图:ListView
TreeView
TreeView
控件显示项目的分层集合。 此控件中的每个项目都由TreeNode
对象表示。
treeview.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import TreeView, TreeNode, DockStyle
from System.Drawing import Size
class IForm(Form):
def __init__(self):
self.Text = 'TreeView'
tv = TreeView()
root = TreeNode()
root.Text = 'Languages'
child1 = TreeNode()
child1.Text = 'Python'
child2 = TreeNode()
child2.Text = 'Ruby'
child3 = TreeNode()
child3.Text = 'Java'
root.Nodes.AddRange((child1, child2, child3))
tv.Parent = self
tv.Nodes.Add(root)
tv.Dock = DockStyle.Fill
tv.AfterSelect += self.AfterSelect
self.sb = StatusBar()
self.sb.Parent = self
self.Size = Size(220, 220)
self.CenterToScreen()
def AfterSelect(self, sender, event):
self.sb.Text = event.Node.Text
Application.Run(IForm())
这是TreeView
控件的非常简单的演示。 我们有一个根项目和三个子项。
tv = TreeView()
我们创建TreeView
控件。
root = TreeNode()
root.Text = 'Languages'
...
tv.Nodes.Add(root)
在这里,我们创建一个根节点。
child1 = TreeNode()
child1.Text = 'Python'
子节点以类似的方式创建。
root.Nodes.AddRange((child1, child2, child3))
子节点插入到根节点的Nodes
属性中。
图:TreeView
目录
下面的代码示例将更深入地研究TreeView
控件。
directories.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import Button, TreeView, TreeNode
from System.Windows.Forms import DockStyle, AnchorStyles
from System.Drawing import Size, Point
from System.IO import Directory, DirectoryInfo
HOME_DIR = '/home/vronskij'
class IForm(Form):
def __init__(self):
self.Text = 'Directories'
self.Size = Size(400, 400)
self.tv = TreeView()
self.SuspendLayout()
self.tv.Parent = self
self.tv.Location = Point(10,10)
self.tv.Size = Size(self.ClientSize.Width - 20, self.Height - 200)
self.tv.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
self.tv.FullRowSelect = False
self.tv.ShowLines = True
self.tv.ShowPlusMinus = True
self.tv.Scrollable = True
self.tv.AfterSelect += self.AfterSelect
expand = Button()
expand.Parent = self
expand.Location = Point(20, self.tv.Bottom + 20)
expand.Text = 'Expand'
expand.Anchor = AnchorStyles.Left | AnchorStyles.Top
expand.Click += self.OnExpand
expandAll = Button()
expandAll.Parent = self
expandAll.Location = Point(20, expand.Bottom + 5)
expandAll.Text = 'Expand All'
expandAll.Anchor = AnchorStyles.Left | AnchorStyles.Top
expandAll.Click += self.OnExpandAll
collapse = Button()
collapse.Parent = self
collapse.Location = Point(expandAll.Right + 5, expand.Top)
collapse.Text = 'Collapse'
collapse.Anchor = AnchorStyles.Left | AnchorStyles.Top
collapse.Click += self.OnCollapse
collapseAll = Button()
collapseAll.Parent = self
collapseAll.Location = Point(collapse.Left, collapse.Bottom + 5)
collapseAll.Text = 'Collapse All'
collapseAll.Anchor = AnchorStyles.Left | AnchorStyles.Top
collapseAll.Click += self.OnCollapseAll
self.sb = StatusBar()
self.sb.Parent = self
self.ShowDirectories(self.tv.Nodes, HOME_DIR)
self.ResumeLayout()
self.CenterToScreen()
def AfterSelect(self, sender, event):
self.sb.Text = event.Node.Text
def ShowDirectories(self, trvNode, path):
dirInfo = DirectoryInfo(path)
if (dirInfo != None):
subDirs = dirInfo.GetDirectories()
tr = TreeNode(dirInfo.Name)
if (subDirs.Length > 0):
for dr in subDirs:
if not dr.Name.StartsWith("."):
self.ShowDirectories(tr.Nodes, dr.FullName)
trvNode.Add(tr)
def OnExpand(self, sender, event):
self.tv.SelectedNode.Expand()
def OnExpandAll(self, sender, event):
self.tv.ExpandAll()
def OnCollapse(self, sender, event):
self.tv.SelectedNode.Collapse()
def OnCollapseAll(self, sender, event):
self.tv.CollapseAll()
Application.Run(IForm())
我们的代码示例在TreeView
控件中显示指定主目录的目录。 该应用启动有些延迟,因为它首先读取主目录的目录结构。 表单上还有四个按钮。 这些按钮以编程方式展开和折叠节点。
self.tv.Scrollable = True
我们使TreeView
控件可滚动,因为该控件显示了大量目录。
self.ShowDirectories(self.tv.Nodes, HOME_DIR)
ShowDirectories()
方法使用指定主目录中可用的目录填充TreeView
控件的节点。
if (subDirs.Length > 0):
...
我们检查是否有任何子目录。
for dr in subDirs:
if not dr.Name.StartsWith("."):
self.ShowDirectories(tr.Nodes, dr.FullName)
我们遍历所有目录。 为此,我们使用了递归算法。 我们还跳过隐藏的目录。 它们以 Unix 系统上的点开头。
trvNode.Add(tr)
此代码行实际上将目录添加到TreeView
控件。
def OnExpand(self, sender, event):
self.tv.SelectedNode.Expand()
所有四个按钮都将事件插入到方法中。 这是展开按钮的方法。 它调用当前所选节点的Expand()
方法。
图:Directories
在 IronPython Mono Winforms 教程的这一部分中,我们介绍了 Winforms 编程库中提供的几个高级控件。
对话框
原文: http://zetcode.com/tutorials/ironpythontutorial/dialogs/
在 IronPython Mono Winforms 教程的这一部分中,我们将讨论对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
基本上有两种类型的对话框。 预定义对话框和自定义对话框。
FolderBrowserDialog
此对话框提示用户选择一个文件夹。
folderbrowserdialog.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import ToolBar, ToolBarButton, FolderBrowserDialog
from System.Windows.Forms import DialogResult
class IForm(Form):
def __init__(self):
self.Text = "FolderBrowserDialog"
toolbar = ToolBar()
toolbar.Parent = self
openb = ToolBarButton()
self.statusbar = StatusBar()
self.statusbar.Parent = self
toolbar.Buttons.Add(openb)
toolbar.ButtonClick += self.OnClicked
self.CenterToScreen()
def OnClicked(self, sender, event):
dialog = FolderBrowserDialog()
if (dialog.ShowDialog(self) == DialogResult.OK):
self.statusbar.Text = dialog.SelectedPath
Application.Run(IForm())
我们有一个工具栏和一个工具栏按钮。 点击按钮,FolderBrowserDialog
出现在屏幕上。 所选文件夹的名称显示在状态栏中。
dialog = FolderBrowserDialog()
FolderBrowserDialog
已创建。
if (dialog.ShowDialog(self) == DialogResult.OK):
self.statusbar.Text = dialog.SelectedPath
ShowDialog()
方法在屏幕上显示对话框。 如果单击对话框的“确定”按钮,则所选的目录路径将显示在状态栏上。
图:FolderBrowserDialog
ColorDialog
该对话框显示可用的颜色以及使用户能够定义自定义颜色的控件。
colordialog.py
#!/usr/bin/ipy
import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, ToolBar
from System.Windows.Forms import ToolBarButton, ControlStyles, ColorDialog
from System.Windows.Forms import DialogResult
from System.Drawing import Size, Color, SolidBrush, Rectangle
RECT_WIDTH = 100
RECT_HEIGHT = 100
class IForm(Form):
def __init__(self):
self.Text = "ColorDialog"
self.color = Color.Blue
toolbar = ToolBar()
toolbar.Parent = self
openb = ToolBarButton()
toolbar.Buttons.Add(openb)
toolbar.ButtonClick += self.OnClicked
self.LocateRect()
self.SetStyle(ControlStyles.ResizeRedraw, True)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
self.LocateRect()
brush = SolidBrush(self.color)
g.FillRectangle(brush, self.r)
def OnClicked(self, sender, events):
dialog = ColorDialog()
if (dialog.ShowDialog(self) == DialogResult.OK):
self.color = dialog.Color
self.Invalidate()
def LocateRect(self):
x = (self.ClientSize.Width - RECT_WIDTH) / 2
y = (self.ClientSize.Height - RECT_HEIGHT) / 2
self.r = Rectangle(x, y, RECT_WIDTH, RECT_HEIGHT)
Application.Run(IForm())
在此代码示例中,我们使用ColorDialog
为位于窗体控件中间的矩形选择颜色。
self.color = Color.Blue
开始时,矩形的颜色是蓝色。 我们使用self.color
变量来确定矩形的颜色。
dialog = ColorDialog()
ColorDialog
已创建。
if (dialog.ShowDialog(self) == DialogResult.OK):
self.color = dialog.Color
self.Invalidate()
该代码显示颜色对话框。 如果单击“确定”按钮,则将获得选定的颜色并调用Invalidate()
方法。 该方法会使控件的整个表面无效,并使控件重画。 结果是用新的颜色值绘制了矩形。
图:ColorDialog
FontDialog
FontDialog
用于选择字体。
fontdialog.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, StatusBar, Label
from System.Windows.Forms import ToolBar, ToolBarButton, FontDialog
from System.Windows.Forms import DialogResult
class IForm(Form):
def __init__(self):
self.Text = "FolderBrowserDialog"
self.text = Label()
self.text.Parent = self
self.text.Text = "Winforms tutorial"
self.LocateText()
toolbar = ToolBar()
toolbar.Parent = self
openb = ToolBarButton()
toolbar.Buttons.Add(openb)
toolbar.ButtonClick += self.OnClicked
self.text.AutoSize = True
self.Resize += self.OnResize
self.CenterToScreen()
def OnResize(self, sender, event):
self.LocateText()
def LocateText(self):
self.text.Top = (self.ClientSize.Height - self.text.Height) / 2
self.text.Left = (self.ClientSize.Width - self.text.Width) / 2
def OnClicked(self, sender, event):
dialog = FontDialog()
if (dialog.ShowDialog(self) == DialogResult.OK):
self.text.Font = dialog.Font
self.LocateText()
Application.Run(IForm())
我们在表单控件的中间绘制一些文本。 我们使用字体对话框更改此文本的字体。
dialog = FontDialog()
FontDialog
已创建。
if (dialog.ShowDialog(self) == DialogResult.OK):
self.text.Font = dialog.Font
self.LocateText()
单击“确定”按钮时,将为Label
控件设置新选择的字体。 由于文本的大小会随着字体的变化而变化,因此我们必须调用LocateText()
方法,该方法将文本定位在表单控件的中间。
图:FontDialog
OpenDialog
此对话框用于打开文件。
opendialog.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, TextBox
from System.Windows.Forms import ToolBar, ToolBarButton, OpenFileDialog
from System.Windows.Forms import DialogResult, ScrollBars, DockStyle
class IForm(Form):
def __init__(self):
self.Text = "OpenDialog"
toolbar = ToolBar()
toolbar.Parent = self
openb = ToolBarButton()
self.textbox = TextBox()
self.textbox.Parent = self
self.textbox.Multiline = True
self.textbox.ScrollBars = ScrollBars.Both
self.textbox.WordWrap = False
self.textbox.Parent = self
self.textbox.Dock = DockStyle.Fill
toolbar.Buttons.Add(openb)
toolbar.ButtonClick += self.OnClicked
self.CenterToScreen()
def OnClicked(self, sender, event):
dialog = OpenFileDialog()
dialog.Filter = "C# files (*.cs)|*.cs"
if dialog.ShowDialog(self) == DialogResult.OK:
f = open(dialog.FileName)
data = f.read()
f.Close()
self.textbox.Text = data
Application.Run(IForm())
我们使用OpenDialog
控件打开 C# 源文件。 我们有一个TextBox
控件,用于显示文件。
dialog = OpenFileDialog()
OpenDialog
已创建。
dialog.Filter = "C# files (*.cs)|*.cs"
我们将Filter
属性设置为 C# 源文件。 此对话框实例只能选择 C# 文件。
if dialog.ShowDialog(self) == DialogResult.OK:
f = open(dialog.FileName)
data = f.read()
f.Close()
self.textbox.Text = data
单击确定后,我们读取所选文件的内容并将其放入TextBox
控件。
在 IronPython Mono Winforms 教程的这一部分中,我们展示了各种对话框。
Mono Winforms 中的拖放
原文: http://zetcode.com/tutorials/ironpythontutorial/dragdrop/
IronPython Mono Winforms 教程的这一部分将专门用于拖放操作。
在计算机图形用户界面中,拖放是单击虚拟对象并将其拖动到其他位置或另一个虚拟对象上的动作(或支持以下动作)。 通常,它可用于调用多种动作,或在两个抽象对象之间创建各种类型的关联。 (维基百科)
拖放功能是图形用户界面最明显的方面之一。 拖放操作使您可以直观地完成复杂的事情。
拖动按钮
在第一个示例中,我们将在按钮控件上执行拖放操作。 该示例在拖放&放置协议之外执行作业。
dragbutton.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, Button
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'Drag & Drop'
button = Button()
button.Parent = self
button.Text = 'Button'
button.MouseDown += self.OnMousDown
button.MouseUp += self.OnMousUp
button.MouseMove += self.OnMousMove
button.Location = Point(20, 20)
self.isDragging = False
self.CenterToScreen()
def OnMousDown(self, sender, event):
self.isDragging = True
self.oldX = event.X
self.oldY = event.Y
def OnMousMove(self, sender, event):
if self.isDragging:
sender.Top = sender.Top + (event.Y - self.oldY)
sender.Left = sender.Left + (event.X - self.oldX)
def OnMousUp(self, sender, event):
self.isDragging = False
Application.Run(IForm())
该代码示例将一个常规按钮控件放在表单容器上。 通过单击按钮表面并同时用鼠标拖动它,我们可以重新放置按钮。
我们的示例中有一些支持变量。 isDragging
变量告诉我们是否正在拖动对象。 oldX
和oldY
变量在拖动过程开始之前存储 x,y 坐标。
button.MouseDown += self.OnMousDown
button.MouseUp += self.OnMousUp
button.MouseMove += self.OnMousMove
我们为按钮插入了三种不同的鼠标处理器。 它们实现了拖放过程的三个不同阶段。 当我们单击按钮时,过程开始。 这由OnMousDown()
方法处理。 第二部分是机芯。 这是当我们将对象移动到新位置时。 它以OnMousMove()
方法处理。 最后一部分是过程停止的时间。 当我们释放鼠标按钮时会发生这种情况。 适当的任务委托给OnMousUp()
方法。
def OnMousDown(self, sender, event):
self.isDragging = True
self.oldX = event.X
self.oldY = event.Y
OnMousDown()
方法实现了过程的第一部分。 它设置了三个必要的变量。
def OnMousMove(self, sender, event):
if self.isDragging:
sender.Top = sender.Top + (event.Y - self.oldY)
sender.Left = sender.Left + (event.X - self.oldX)
在OnMousMove()
方法中,我们重新定位按钮。 我们计算存储的 x,y 坐标与鼠标指针的新坐标之间的差。 差异将添加到按钮的Top
和Left
属性中,从而将其移动到新位置。
图:拖动按钮
拖动文字
在前面的示例中,我们确实拖动了控件上的&拖放。 接下来,我们将对文本数据进行拖放操作。 在这里,我们将使用 Winforms 库提供的拖放协议。
拖放操作是 Winforms 中的标准通信协议。 我们有两个基本对象。 拖动源和放置目标。
dragtext.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, Button
from System.Windows.Forms import TextBox, DragDropEffects, DataFormats
from System.Drawing import Size, Point
class IForm(Form):
def __init__(self):
self.Text = 'Drag & Drop'
self.AllowDrop = True
button = Button()
button.Parent = self
textBox = TextBox()
textBox.Parent = self
button.AllowDrop = True
button.Location = Point(150, 50)
button.DragDrop += self.OnDragDrop
button.DragEnter += self.OnDragEnter
textBox.Location = Point(15, 50)
textBox.MouseDown += self.OnMousDown
self.ClientSize = Size(250, 200)
self.CenterToScreen()
def OnMousDown(self, sender, event):
sender.SelectAll()
sender.DoDragDrop(sender.Text, DragDropEffects.Copy)
def OnDragEnter(self, sender, event):
if event.Data.GetDataPresent(DataFormats.Text):
event.Effect = DragDropEffects.Copy
def OnDragDrop(self, sender, event):
sender.Text = event.Data.GetData(DataFormats.Text)
Application.Run(IForm())
我们在表单上有两个控件。 一个按钮和一个文本框。 我们将文本从文本框中拖放到按钮上。
self.AllowDrop = True
我们将AllowDrop
属性设置为true
。 默认情况下不启用删除。
button.DragDrop += self.OnDragDrop
button.DragEnter += self.OnDragEnter
...
extBox.MouseDown += self.OnMousDown
同样,拖放过程分为三个步骤。 对于每个特定步骤,我们有三种方法。
def OnMousDown(self, sender, event):
sender.SelectAll()
sender.DoDragDrop(sender.Text, DragDropEffects.Copy)
在OnMousDown()
方法中,我们初始化了拖放过程。 我们使用DoDragDrop()
方法启动该过程。 DragDropEffects.Copy
参数指定操作的类型。 实质上,我们可以在拖放操作期间复制文本或移动文本。
def OnDragEnter(self, sender, event):
if event.Data.GetDataPresent(DataFormats.Text):
event.Effect = DragDropEffects.Copy
当鼠标指针进入放置目标控件的区域时,将启动DragEnter
事件。 必须设置Effect
属性。 拖动源和放置目标的DragDropEffects
必须相等。 否则,该操作将无法进行。
def OnDragDrop(self, sender, event):
sender.Text = event.Data.GetData(DataFormats.Text)
最后,我们有OnDragDrop()
方法。 在这里,我们从事件对象获取数据并将其设置为按钮Text
属性。
图:文本拖放
拖动图像
在最后一个示例中,我们将&拖放图像拖到窗体上。
dragimage.py
#!/usr/bin/ipy
import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, PictureBox, PictureBoxSizeMode
from System.Windows.Forms import Cursors
from System.Drawing import Size, Point, Rectangle, Brushes, Bitmap
class IForm(Form):
def __init__(self):
self.ClientSize = Size(350, 250)
self.Text = "Dragging Images"
self.Paint += self.OnPaint
self.isDragging = False
self.dropRect = Rectangle(10, 10, 200, 160)
self.brush = Brushes.Gray
picBox = PictureBox()
self.loadImage()
self.isDragging = False
self.CenterToScreen()
picBox.Parent = self
picBox.Location = Point(100, 50)
picBox.Size = Size(self.image.Width, self.image.Height)
picBox.Image = self.image
picBox.MouseDown += self.OnMousDown
picBox.MouseUp += self.OnMousUp
picBox.MouseMove += self.OnMousMove
picBox.Cursor = Cursors.Hand
def loadImage(self):
try:
self.image = Bitmap("image.jpg")
except Exception, e:
print "Error reading image"
print e.msg
sys.exit(1)
def OnMousMove(self, sender, event):
if self.isDragging:
sender.Top = sender.Top + (event.Y - self.oldY)
sender.Left = sender.Left + (event.X - self.oldX)
def OnMousUp(self, sender, event):
self.isDragging = False
if self.dropRect.Contains(sender.Bounds):
self.brush = Brushes.Gold
else:
self.brush = Brushes.Gray
self.Refresh()
def OnMousDown(self, sender, event):
self.isDragging = True
self.oldX = event.X
self.oldY = event.Y
def OnPaint(self, event):
g = event.Graphics
g.FillRectangle(self.brush, self.dropRect)
Application.Run(IForm())
在我们的示例中,我们有一个PictureBox
,并绘制了一个灰色矩形。 如果将图片放在矩形内,则矩形的颜色变为金色。
self.brush = Brushes.Gray
self.brush
变量保存矩形的笔刷。 默认情况下为灰色。
def loadImage(self):
try:
self.image = Bitmap("image.jpg")
except Exception, e:
print "Error reading image"
print e.msg
sys.exit(1)
loadImage()
方法为PictureBox
控件加载位图。
if self.dropRect.Contains(sender.Bounds):
self.brush = Brushes.Gold
else:
self.brush = Brushes.Gray
在OnMousUp()
方法中,我们确定矩形的笔刷。 如果图片框的边界在矩形内,则画笔为金色;否则,画笔为金色。 否则为灰色。
self.Refresh()
我们必须调用Refresh()
方法来激活新的画笔颜色。
图:拖放图像
本章专门使用 Mono Winforms 库拖放操作。
绘图
原文: http://zetcode.com/tutorials/ironpythontutorial/painting/
在 IronPython Mono Winforms 教程的这一部分中,我们将进行绘图。 当我们想要更改或增强现有控件时,将使用绘图。 或者,如果我们要从头开始创建自定义控件。 要进行绘图,我们使用 Winforms 库提供的绘图 API。 绘图是在一种方法中完成的,我们将其插入Paint
事件。
System.Drawing
名称空间提供对GDI+
基本图形功能的访问。 System.Drawing.Drawing2D
,System.Drawing.Imaging
和System.Drawing.Text
名称空间提供了更高级的功能。 Graphics
类提供了在表单上进行绘制的方法。
直线
我们的第一个示例将在Form
控件上绘制线条。
lines.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Pen, Color
from System.Drawing.Drawing2D import DashStyle
class IForm(Form):
def __init__(self):
self.Text = 'Lines'
self.Size = Size(280, 270)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
pen = 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 = (6, 8, 1, 1, 1, 1, 1, 1)
g.DrawLine(pen, 20, 200, 250, 200)
pen.Dispose()
g.Dispose()
Application.Run(IForm())
我们在表格上画了五行。 每行具有不同的DashStyle
。
self.Paint += self.OnPaint
绘图事件将传递给OnPaint()
方法。
def OnPaint(self, event):
这是OnPaint()
方法的签名。
g = event.Graphics
为了在表单上绘图,我们必须获取Graphics
对象。 在窗体上绘图实际上是在调用Graphics
对象的各种方法。
pen = Pen(Color.Black, 1)
pen.DashStyle = DashStyle.Dot
g.DrawLine(pen, 20, 40, 250, 40)
我们创建一个Pen
对象。 该对象用于绘制形状的轮廓。 比我们设置点划线DashStyle
。 最后,我们用DrawLine()
方法画线。 第一个参数是钢笔对象。 接下来的四个值是线的起点和终点的 x 和 y 值。
pen.DashPattern = (6, 8, 1, 1, 1, 1, 1, 1)
有几个内置的DashStyle
值。 我们可以使用DashPattern
属性来创建自己的样式。 乍一看可能很难。 但是模式只是填充和空值的元组。
pen.Dispose()
g.Dispose()
我们释放资源。
图:直线
色彩
Winforms 库中的颜色表示 ARGB(alpha,红色,绿色,蓝色)颜色。 它是 Alpha,红色,绿色和蓝色(RGB)强度值的组合。 还有一些可以在绘图中使用的预定义颜色名称。
colors.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, ControlStyles
from System.Drawing import Size, Brushes
class IForm(Form):
def __init__(self):
self.Text = 'Colors'
self.Size = Size(360, 300)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.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()
Application.Run(IForm())
我们用 9 种不同的颜色绘制 9 个矩形。
g.FillRectangle(Brushes.Sienna, 10, 15, 90, 60)
FillRectagle()
方法用画笔填充指定的矩形。 画笔可以是颜色或图案。 有一些预定义的颜色可用。 我们可以从Brushes
对象获得它们。 最后四个值是左上角点的 x,y 值以及矩形的宽度和高度。
图:颜色
HatchBrush
HatchBrush
对象用于填充形状的内部。 我们可以使用几种内置模式。
hatches.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Color
from System.Drawing.Drawing2D import HatchBrush, HatchStyle
class IForm(Form):
def __init__(self):
self.Text = 'Hatches'
self.Size = Size(360, 300)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
hb = HatchBrush(HatchStyle.Cross, Color.Black, self.BackColor)
g.FillRectangle(hb, 10, 15, 90, 60)
hb = HatchBrush(HatchStyle.Percent05, Color.Black, self.BackColor)
g.FillRectangle(hb, 130, 15, 90, 60)
hb = HatchBrush(HatchStyle.SolidDiamond, Color.Black, self.BackColor)
g.FillRectangle(hb, 250, 15, 90, 60)
hb = HatchBrush(HatchStyle.DiagonalBrick, Color.Black, self.BackColor)
g.FillRectangle(hb, 10, 105, 90, 60)
hb = HatchBrush(HatchStyle.Divot, Color.Black, self.BackColor)
g.FillRectangle(hb, 130, 105, 90, 60)
hb = HatchBrush(HatchStyle.Wave, Color.Black, self.BackColor)
g.FillRectangle(hb, 250, 105, 90, 60)
hb = HatchBrush(HatchStyle.ZigZag, Color.Black, self.BackColor)
g.FillRectangle(hb, 10, 195, 90, 60)
hb = HatchBrush(HatchStyle.Sphere, Color.Black, self.BackColor)
g.FillRectangle(hb, 130, 195, 90, 60)
hb = HatchBrush(HatchStyle.Shingle, Color.Black, self.BackColor)
g.FillRectangle(hb, 250, 195, 90, 60)
hb.Dispose()
g.Dispose()
Application.Run(IForm())
这次,我们用九种不同的图案(称为剖面线)填充了九个矩形。
hb = HatchBrush(HatchStyle.Cross, Color.Black, self.BackColor)
在这里,我们创建一个HatchBrush
对象。 参数是图案填充样式以及前景色和背景色。 背景颜色设置为表单的颜色,因此看起来就像我们在表单上绘制的一样。
g.FillRectangle(hb, 10, 15, 90, 60)
我们使用指定的阴影刷填充矩形。
图:通口
基本对象
下面的示例在窗体控件上绘制一些基本形状。
basicshapes.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Rectangle, Brushes, Pens, Point
from System.Drawing.Drawing2D import SmoothingMode
from System import Array
class IForm(Form):
def __init__(self):
self.Text = 'Basic shapes'
self.Size = Size(420, 280)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)
g.FillRectangle(Brushes.Gray, 180, 20, 80, 80)
g.FillEllipse(Brushes.Gray, 30, 120, 100, 100)
g.FillEllipse(Brushes.Gray, 160, 130, 100, 70)
p1 = Point(300, 40)
p2 = Point(340, 15)
p3 = Point(380, 40)
p4 = Point(380, 80)
p5 = Point(340, 105)
p6 = Point(300, 80)
g.FillPolygon(Brushes.Gray, Array[Point]([p1, p2, p3, p4, p5, p6]))
g.FillPie(Brushes.Gray, Rectangle(290, 130, 90, 90), 0, 315)
g.Dispose()
Application.Run(IForm())
该代码示例在表单上绘制六个形状。 矩形,正方形,圆形,椭圆形,多边形和扇形。
g.SmoothingMode = SmoothingMode.AntiAlias
这使绘图更平滑。
g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)
这条线用灰色填充矩形。 参数是画笔颜色,矩形左上角的 x,y 坐标以及矩形的宽度和高度。
g.FillPolygon(Brushes.Gray, Array[Point]([p1, p2, p3, p4, p5, p6]))
这条线绘制了一个包含六个单点的多边形。
g.FillPie(Brushes.Gray, Rectangle(290, 130, 90, 90), 0, 315)
这条线画了一个馅饼。 最后两个参数是起始角度和后掠角度。 以度为单位。
图:基本形状
绘制直线
要在 Winforms Form
上绘制字符串,我们使用DrawString()
方法。
lyrics.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Font, SolidBrush
from System.Drawing import PointF, Color
class IForm(Form):
def __init__(self):
self.Text = "You know I'm No Good"
self.Size = Size(380, 450)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
ft = Font("Purisa", 10)
br = SolidBrush(Color.Black)
pt = PointF(20.0, 20.0)
g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)
pt = PointF(20.0, 50.0)
g.DrawString("Your rolled up sleeves and your skull t-shirt", ft, br, pt)
pt = PointF(20.0, 80.0)
g.DrawString("You say why did you do it with him today?", ft, br, pt)
pt = PointF(20.0, 110.0)
g.DrawString("And sniffed me out like I was tanqueray", ft, br, pt)
pt = PointF(20.0, 160.0)
g.DrawString("Cause you're my fella, my guy", ft, br, pt)
pt = PointF(20.0, 190.0)
g.DrawString("Hand me your stella and fly", ft, br, pt)
pt = PointF(20.0, 220.0)
g.DrawString("By the time I'm out the door", ft, br, pt)
pt = PointF(20.0, 250.0)
g.DrawString("You tear me down like roger moore", ft, br, pt)
pt = PointF(20.0, 300.0)
g.DrawString("I cheated myself", ft, br, pt)
pt = PointF(20.0, 330.0)
g.DrawString("Like I knew I would", ft, br, pt)
pt = PointF(20.0, 360.0)
g.DrawString("I told ya, I was trouble", ft, br, pt)
pt = PointF(20.0, 390.0)
g.DrawString("You know that I'm no good", ft, br, pt)
g.Dispose()
Application.Run(IForm())
在我们的示例中,我们在 Winforms 窗体上绘制歌曲的歌词。
ft = Font("Purisa", 10)
我们使用 10 磅高的 Purisa 字体。
pt = PointF(20.0, 20.0)
要在表单上绘制字符串,我们必须使用浮点值。
g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)
DrawString()
方法采用以下参数:要绘制的文本,字体,笔刷和PointF
对象。
图:歌词
绘制图像
在最后一个示例中,我们将在Form
控件上绘制图像。
redrock.py
#!/usr/bin/ipy
import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Bitmap, Rectangle
class IForm(Form):
def __init__(self):
self.Text = 'Red Rock'
self.Size = Size(200, 150)
self.loadImage()
self.Size = Size(self.castle.Width, self.castle.Height)
self.Paint += self.OnPaint
self.CenterToScreen()
def loadImage(self):
try:
self.castle = Bitmap("redrock.png")
except Exception, e:
print e.msg
sys.exit(1)
def OnPaint(self, event):
g = event.Graphics
r = Rectangle(1, 1, self.castle.Width, self.castle.Height)
g.DrawImage(self.castle, r)
g.Dispose()
Application.Run(IForm())
此代码示例在窗体上绘制城堡的图像。
def loadImage(self):
try:
self.castle = Bitmap("redrock.png")
except Exception, e:
print e.msg
sys.exit(1)
我们加载城堡的图像。
r = Rectangle(1, 1, self.castle.Width, self.castle.Height)
我们确定将要绘制的矩形。
g.DrawImage(self.castle, r)
这条线实际上绘制图像。
图:图像
在本章中,我们在 Mono Winforms 库中做了一些绘图。
IronPython Mono Winforms 中的绘图 II
原文: http://zetcode.com/tutorials/ironpythontutorial/paintingII/
在 IronPython Mono Winforms 教程的这一部分中,我们将继续绘图。
甜甜圈
在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。
donut.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Color, SolidBrush, Pen
class IForm(Form):
def __init__(self):
self.Text = 'Donut'
self.Size = Size(350, 300)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
pen = Pen(Color.Gray, 1)
size = self.ClientSize
g.TranslateTransform(size.Width/2, size.Height/2)
g.DrawEllipse(pen, -125, -125, 250, 250)
for i in range(0, 36):
g.DrawEllipse(pen, 0, 0, 120, 50)
g.RotateTransform(10)
g.Dispose()
Application.Run(IForm())
我们在表格上画了五行。 每行具有不同的DashStyle
。
size = self.ClientSize
g.TranslateTransform(size.Width/2, size.Height/2)
g.DrawEllipse(pen, -125, -125, 250, 250)
我们在表格中间画一个圆。
for i in range(0, 36):
g.DrawEllipse(pen, 0, 0, 120, 50)
g.RotateTransform(10)
我们绘制 36 个椭圆,每个椭圆在最后一个椭圆之后旋转 10 度。 从而得到甜甜圈的对象。
图:多纳圈
透明矩形
透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)
transparentrectangles.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Color, SolidBrush
class IForm(Form):
def __init__(self):
self.Text = 'Transparent rectangles'
self.Size = Size(590, 110)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
for i in range(1, 11):
color = Color.FromArgb(i*25, 0, 0, 255)
brush = SolidBrush(color)
g.FillRectangle(brush, 50*i, 20, 40, 40)
Application.Run(IForm())
在示例中,我们将绘制十个具有不同透明度级别的矩形。
color = Color.FromArgb(i*25, 0, 0, 255)
该行创建一个颜色对象。 第一个值是 Alpha 透明度。
brush = SolidBrush(color)
我们用颜色创建画笔。
g.FillRectangle(brush, 50*i, 20, 40, 40)
我们画一个矩形。
图:透明矩形
灰度图像
下面的示例创建一个灰度图像。
grayscale.py
#!/usr/bin/ipy
import clr
import sys
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Rectangle
from System.Drawing import Bitmap, Color
class IForm(Form):
def __init__(self):
self.Text = 'Grayscale'
self.Size = Size(290, 150)
self.Paint += self.OnPaint
self.rotunda = self.loadImage()
self.gs = self.grayScale(self.rotunda.Clone())
self.CenterToScreen()
def loadImage(self):
try:
rotunda = Bitmap("rotunda.jpg")
return rotunda
except Exception, e:
print e.msg
sys.exit(1)
def grayScale(self, image):
w = image.Width
h = image.Height
for i in range(w):
for j in range(h):
c = image.GetPixel(i, j)
lum = 0.299*c.R + 0.587*c.G + 0.114*c.B
image.SetPixel(i, j, Color.FromArgb(lum, lum, lum))
return image
def OnPaint(self, event):
g = event.Graphics
r1 = Rectangle(15, 15, self.rotunda.Width, self.rotunda.Height)
g.DrawImage(self.rotunda, r1)
r2 = Rectangle(150, 15, self.gs.Width, self.gs.Height)
g.DrawImage(self.gs, r2)
g.Dispose()
Application.Run(IForm())
我们的示例中有两个图像。 一种颜色和一种灰度。
self.rotunda = self.loadImage()
loadImage()
方法从磁盘的当前工作目录加载位图。
self.gs = self.grayScale(self.rotunda.Clone())
grayScale()
方法从彩色图像制作灰度图像。 我们将圆形大厅图像的副本作为此方法的参数。
c = image.GetPixel(i, j)
我们从图像中获得所有像素。
lum = 0.299*c.R + 0.587*c.G + 0.114*c.B
该方程式计算灰度图像的亮度。 如果我们使用这些因素来缩放颜色的红色,绿色和蓝色部分,则人眼会将图像视为灰色。
image.SetPixel(i, j, Color.FromArgb(lum, lum, lum))
我们修改像素。
渐变
在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers.com)
gradients.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form
from System.Drawing import Size, Color, Point
from System.Drawing.Drawing2D import LinearGradientBrush
class IForm(Form):
def __init__(self):
self.Text = 'Gradients'
self.Size = Size(350, 350)
self.Paint += self.OnPaint
self.CenterToScreen()
def OnPaint(self, event):
g = event.Graphics
pt1 = Point(5, 5)
pt2 = Point(25, 25)
lg = LinearGradientBrush(pt1, pt2, Color.Red, Color.Black)
g.FillRectangle(lg, 20, 20, 300, 40)
pt1 = Point(5, 25)
pt2 = Point(20, 2)
lg = LinearGradientBrush(pt1, pt2, Color.Yellow, Color.Black)
g.FillRectangle(lg, 20, 80, 300, 40)
pt1 = Point(5, 25)
pt2 = Point(2, 2)
lg = LinearGradientBrush(pt1, pt2, Color.Green, Color.Black)
g.FillRectangle(lg, 20, 140, 300, 40)
pt1 = Point(25, 25)
pt2 = Point(15, 25)
lg = LinearGradientBrush(pt1, pt2, Color.Blue, Color.Black)
g.FillRectangle(lg, 20, 200, 300, 40)
pt1 = Point(0, 10)
pt2 = Point(0, 20)
lg = LinearGradientBrush(pt1, pt2, Color.Orange, Color.Black)
g.FillRectangle(lg, 20, 260, 300, 40)
lg.Dispose()
g.Dispose()
Application.Run(IForm())
我们绘制五个矩形,这些矩形填充有不同的线性渐变。
pt1 = Point(5, 5)
pt2 = Point(25, 25)
这两个是线性渐变画笔的控制点。
lg = LinearGradientBrush(pt1, pt2, Color.Red, Color.Black)
我们创建LinearGradientBrush
对象。 我们使用两个控制点和两种混合颜色。
图:渐变
等待
在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一种错觉,即一条线在移动。 这种效果通常用于通知用户,一项艰巨的任务正在幕后进行。 一个示例是通过互联网流式传输视频。
waiting.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
from System.Windows.Forms import Application, Form, Timer
from System.Drawing import Size, Color, SolidBrush, Pen
from System.Drawing.Drawing2D import SmoothingMode, LineCap
from System.ComponentModel import Container
trs = (
( 0, 35, 70, 100, 150, 180, 210, 250 ),
( 250, 0, 35, 70, 100, 150, 180, 210 ),
( 210, 250, 0, 35, 70, 100, 150, 180 ),
( 180, 210, 250, 0, 35, 70, 100, 150 ),
( 150, 180, 210, 250, 0, 35, 70, 100 ),
( 100, 150, 180, 210, 250, 0, 35, 70 ),
( 70, 100, 150, 180, 210, 250, 0, 35 ),
( 35, 70, 100, 150, 180, 210, 250, 0 )
)
class IForm(Form):
def __init__(self):
self.Text = 'Waiting'
self.Size = Size(250, 150)
self.Paint += self.OnPaint
self.count = 0
self.timer = Timer(Container())
self.timer.Enabled = True
self.timer.Interval = 80
self.timer.Tick += self.OnTick
self.CenterToScreen()
def OnTick(self, sender, event):
self.count = self.count + 1
self.Refresh()
def OnPaint(self, event):
g = event.Graphics
g.SmoothingMode = SmoothingMode.AntiAlias
size = self.ClientSize
g.TranslateTransform(size.Width/2, size.Height/2)
for i in range(0, 8):
color = Color.FromArgb(trs[self.count%8][i], 30, 30, 30)
pen = Pen(color, 3)
pen.StartCap = LineCap.Round
pen.EndCap = LineCap.Round
g.DrawLine(pen, 0, -10, 0, -40)
g.RotateTransform(45)
pen.Dispose()
g.Dispose()
Application.Run(IForm())
我们用八个不同的 alpha 值绘制八条线。
self.timer = Timer(Container())
self.timer.Enabled = True
self.timer.Interval = 80
self.timer.Tick += self.On
我们使用Timer
制作动画。
trs = (
( 0, 35, 70, 100, 150, 180, 210, 250 ),
...
)
这是此演示中使用的透明度值的二维集合。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。
pen = Pen(color, 3)
pen.StartCap = LineCap.Round
pen.EndCap = LineCap.Round
我们使线条更粗一些,以便更好地显示它们。 我们用带帽的线画线。
color = Color.FromArgb(trs[self.count%8][i], 30, 30, 30)
在这里,我们定义了一条线的透明度值。
g.DrawLine(pen, 0, -10, 0, -40)
g.RotateTransform(45)
我们画了 8 条线。 它们顺时针旋转。
图:等待
在 IronPython Winforms 教程的这一章中,我们在 Mono Winforms 库中做了一些更高级的绘图。
IronPython Mono Winforms 中的贪食蛇
在 Mono IronPython Winforms 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇游戏
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。 该游戏有时称为 Nibbles 。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 如果游戏结束,我们将在棋盘中间显示Game Over
消息。
board.py
import clr
clr.AddReference("System.Drawing")
clr.AddReference("System")
from System.Windows.Forms import UserControl, Keys, Timer
from System.Drawing import Size, Color, Bitmap, Brushes, RectangleF
from System.Drawing import Font, StringAlignment, StringFormat, PointF
from System import Random
from System.ComponentModel import Container
WIDTH = 300
HEIGHT = 300
DOT_SIZE = 10
ALL_DOTS = 900
RAND_POS = 27
x = [0] * ALL_DOTS
y = [0] * ALL_DOTS
class Board(UserControl):
def __init__(self):
self.Text = 'Snake'
self.components = Container()
self.BackColor = Color.Black
self.DoubleBuffered = True
self.ClientSize = Size(WIDTH, HEIGHT)
self.left = False
self.right = True
self.up = False
self.down = False
self.inGame = True
try:
self.dot = Bitmap("dot.png")
self.apple = Bitmap("apple.png")
self.head = Bitmap("head.png")
except Exception, e:
print e.Message
self.initGame()
def OnTick(self, sender, event):
if self.inGame:
self.checkApple()
self.checkCollision()
self.move()
self.Refresh()
def initGame(self):
self.dots = 3
for i in range(self.dots):
x[i] = 50 - i * 10
y[i] = 50
self.locateApple()
self.KeyUp += self.OnKeyUp
self.timer = Timer(self.components)
self.timer.Enabled = True
self.timer.Interval = 100
self.timer.Tick += self.OnTick
self.Paint += self.OnPaint
def OnPaint(self, event):
g = event.Graphics
if (self.inGame):
g.DrawImage(self.apple, self.apple_x, self.apple_y)
for i in range(self.dots):
if i == 0:
g.DrawImage(self.head, x[i], y[i])
else:
g.DrawImage(self.dot, x[i], y[i])
else:
self.gameOver(g)
def gameOver(self, g):
msg = "Game Over"
format = StringFormat()
format.Alignment = StringAlignment.Center
format.LineAlignment = StringAlignment.Center
width = float(self.ClientSize.Width)
height = float(self.ClientSize.Height)
rectf = RectangleF(0.0, 0.0, width, height)
g.DrawString(msg, self.Font, Brushes.White, rectf, format)
self.timer.Stop()
def checkApple(self):
if x[0] == self.apple_x and y[0] == self.apple_y:
self.dots = self.dots + 1
self.locateApple()
def move(self):
z = self.dots
while z > 0:
x[z] = x[(z - 1)]
y[z] = y[(z - 1)]
z = z - 1
if self.left:
x[0] -= DOT_SIZE
if self.right:
x[0] += DOT_SIZE
if self.up:
y[0] -= DOT_SIZE
if self.down:
y[0] += DOT_SIZE
def checkCollision(self):
z = self.dots
while z > 0:
if z > 4 and x[0] == x[z] and y[0] == y[z]:
self.inGame = False
z = z - 1
if y[0] >= HEIGHT - DOT_SIZE - self.TITLEBAR_HEIGHT:
self.inGame = False
if y[0] < 0:
self.inGame = False
if x[0] >= WIDTH - DOT_SIZE - self.BORDER_WIDTH:
self.inGame = False
if x[0] < 0:
self.inGame = False
def locateApple(self):
rand = Random()
r = rand.Next(RAND_POS)
self.apple_x = r * DOT_SIZE
r = rand.Next(RAND_POS)
self.apple_y = r * DOT_SIZE
def OnKeyUp(self, event):
key = event.KeyCode
if key == Keys.Left and not self.right:
self.left = True
self.up = False
self.down = False
if key == Keys.Right and not self.left:
self.right = True
self.up = False
self.down = False
if key == Keys.Up and not self.down:
self.up = True
self.right = False
self.left = False
if key == Keys.Down and not self.up:
self.down = True
self.right = False
self.left = False
首先,我们将定义游戏中使用的常量。
WIDTH
和HEIGHT
常数确定电路板的大小。 DOT_SIZE
是苹果的大小和蛇的点。 ALL_DOTS
常数定义了板上可能的最大点数。 (900 = 300 * 300 / 10 * 10
)RAND_POS
常数用于计算苹果的随机位置。 DELAY
常数确定游戏的速度。
x = [0] * ALL_DOTS
y = [0] * ALL_DOTS
这两个列表存储蛇的所有可能关节的 x,y 坐标。
在move()
方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
while z > 0:
x[z] = x[(z - 1)]
y[z] = y[(z - 1)]
z = z - 1
该代码将关节向上移动。
if self.left:
x[0] -= DOT_SIZE
将头向左移动。
在checkCollision()
方法中,我们确定蛇是否击中了自己或撞墙之一。
while z > 0:
if z > 4 and x[0] == x[z] and y[0] == y[z]:
self.inGame = False
z = z - 1
如果蛇用头撞到其关节之一,则游戏结束。
if y[0] >= HEIGHT - DOT_SIZE - self.TITLEBAR_HEIGHT:
self.inGame = False
如果蛇击中了棋盘的底部,则游戏结束。
下图有助于了解蛇形物体与棋盘底部的碰撞。
图:碰撞
locateApple()
方法在表格上随机定位一个苹果。
rand = Random()
r = rand.Next(RAND_POS)
我们得到一个从 0 到RAND_POS-1
的随机数。
self.apple_x = r * DOT_SIZE
...
self.apple_y = r * DOT_SIZE
这些行设置了apple
对象的 x,y 坐标。
在OnKeyUp()
方法中,我们确定了键击玩家击键的时间。
if key == Keys.Left and not self.right:
self.left = True
self.up = False
self.down = False
如果我们按左光标键,则将self.left
变量设置为True
。 在move()
方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
snake.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Application, Form, FormBorderStyle
from board import Board
class IForm(Form):
def __init__(self):
self.Text = 'Snake'
self.FormBorderStyle = FormBorderStyle.FixedSingle
borderWidth = (self.Width - self.ClientSize.Width) / 2
titleBarHeight = self.Height - self.ClientSize.Height - borderWidth
board = Board()
board.BORDER_WIDTH = borderWidth
board.TITLEBAR_HEIGHT = titleBarHeight
self.Controls.Add(board)
self.CenterToScreen()
Application.Run(IForm())
这是主要的类。
borderWidth = (self.Width - self.ClientSize.Width) / 2
titleBarHeight = self.Height - self.ClientSize.Height - borderWidth
在这里,我们获得窗体控件的边框宽度和标题栏高度。 这些值对于蛇与边界的碰撞检测是必需的。
board.BORDER_WIDTH = borderWidth
board.TITLEBAR_HEIGHT = titleBarHeight
我们将它们提供给董事会。
图:贪食蛇
这是使用 Iron Win 编程语言的 Mono Winforms 库编程的贪食蛇游戏。
IronPython Mono Winforms 中的俄罗斯方块游戏
俄罗斯方块游戏是有史以来最受欢迎的计算机游戏之一。 原始游戏是由俄罗斯程序员 Alexey Pajitnov 于 1985 年设计和编程的。此后,几乎所有版本的几乎所有计算机平台上都可以使用俄罗斯方块。 甚至我的手机都有俄罗斯方块游戏的修改版。
俄罗斯方块被称为下降块益智游戏。 在这个游戏中,我们有七个不同的形状,称为 tetrominoes 。 S 形,Z 形,T 形,L 形,线形,镜像 L 形和正方形。 这些形状中的每一个都形成有四个正方形。 形状从板上掉下来。 俄罗斯方块游戏的目的是移动和旋转形状,以便它们尽可能地适合。 如果我们设法形成一行,则该行将被破坏并得分。 我们玩俄罗斯方块游戏,直到达到顶峰。
图:Tetrominoes
开发
我们的俄罗斯方块游戏没有图像,我们使用 Winforms 库中可用的绘图 API 绘制四方块。 每个计算机游戏的背后都有一个数学模型。 在俄罗斯方块中也是如此。
游戏背后的一些想法。
- 我们使用
Timer
创建游戏周期 - 绘制四方块
- 形状以正方形为单位移动(不是逐个像素移动)
- 从数学上讲,棋盘是简单的数字列表
以下示例是俄罗斯方块游戏的修改版,可用于 PyQt4 安装文件。
tetris.py
#!/usr/bin/ipy
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
clr.AddReference("System")
from System.Windows.Forms import Application, Form, FormBorderStyle
from System.Windows.Forms import UserControl, Keys, Timer, StatusBar
from System.Drawing import Size, Color, SolidBrush, Pen
from System.Drawing.Drawing2D import LineCap
from System.ComponentModel import Container
from System import Random
class Tetrominoes(object):
NoShape = 0
ZShape = 1
SShape = 2
LineShape = 3
TShape = 4
SquareShape = 5
LShape = 6
MirroredLShape = 7
class Board(UserControl):
BoardWidth = 10
BoardHeight = 22
Speed = 200
ID_TIMER = 1
def __init__(self):
self.Text = 'Snake'
self.components = Container()
self.isWaitingAfterLine = False
self.curPiece = Shape()
self.nextPiece = Shape()
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
self.DoubleBuffered = True
self.isStarted = False
self.isPaused = False
self.timer = Timer(self.components)
self.timer.Enabled = True
self.timer.Interval = Board.Speed
self.timer.Tick += self.OnTick
self.Paint += self.OnPaint
self.KeyUp += self.OnKeyUp
self.ClearBoard()
def ShapeAt(self, x, y):
return self.board[(y * Board.BoardWidth) + x]
def SetShapeAt(self, x, y, shape):
self.board[(y * Board.BoardWidth) + x] = shape
def SquareWidth(self):
return self.ClientSize.Width / Board.BoardWidth
def SquareHeight(self):
return self.ClientSize.Height / Board.BoardHeight
def Start(self):
if self.isPaused:
return
self.isStarted = True
self.isWaitingAfterLine = False
self.numLinesRemoved = 0
self.ClearBoard()
self.NewPiece()
def Pause(self):
if not self.isStarted:
return
self.isPaused = not self.isPaused
statusbar = self.Parent.statusbar
if self.isPaused:
self.timer.Stop()
statusbar.Text = 'paused'
else:
self.timer.Start()
statusbar.Text = str(self.numLinesRemoved)
self.Refresh()
def ClearBoard(self):
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoes.NoShape)
def OnPaint(self, event):
g = event.Graphics
size = self.ClientSize
boardTop = size.Height - Board.BoardHeight * self.SquareHeight()
for i in range(Board.BoardHeight):
for j in range(Board.BoardWidth):
shape = self.ShapeAt(j, Board.BoardHeight - i - 1)
if shape != Tetrominoes.NoShape:
self.DrawSquare(g,
0 + j * self.SquareWidth(),
boardTop + i * self.SquareHeight(), shape)
if self.curPiece.GetShape() != Tetrominoes.NoShape:
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.DrawSquare(g, 0 + x * self.SquareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.SquareHeight(),
self.curPiece.GetShape())
g.Dispose()
def OnKeyUp(self, event):
if not self.isStarted or self.curPiece.GetShape() == Tetrominoes.NoShape:
return
key = event.KeyCode
if key == Keys.P:
self.Pause()
return
if self.isPaused:
return
elif key == Keys.Left:
self.TryMove(self.curPiece, self.curX - 1, self.curY)
elif key == Keys.Right:
self.TryMove(self.curPiece, self.curX + 1, self.curY)
elif key == Keys.Down:
self.TryMove(self.curPiece.RotatedRight(), self.curX, self.curY)
elif key == Keys.Up:
self.TryMove(self.curPiece.RotatedLeft(), self.curX, self.curY)
elif key == Keys.Space:
self.DropDown()
elif key == Keys.D:
self.OneLineDown()
def OnTick(self, sender, event):
if self.isWaitingAfterLine:
self.isWaitingAfterLine = False
self.NewPiece()
else:
self.OneLineDown()
def DropDown(self):
newY = self.curY
while newY > 0:
if not self.TryMove(self.curPiece, self.curX, newY - 1):
break
newY -= 1
self.PieceDropped()
def OneLineDown(self):
if not self.TryMove(self.curPiece, self.curX, self.curY - 1):
self.PieceDropped()
def PieceDropped(self):
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.SetShapeAt(x, y, self.curPiece.GetShape())
self.RemoveFullLines()
if not self.isWaitingAfterLine:
self.NewPiece()
def RemoveFullLines(self):
numFullLines = 0
statusbar = self.Parent.statusbar
rowsToRemove = []
for i in range(Board.BoardHeight):
n = 0
for j in range(Board.BoardWidth):
if not self.ShapeAt(j, i) == Tetrominoes.NoShape:
n = n + 1
if n == 10:
rowsToRemove.append(i)
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.SetShapeAt(l, k, self.ShapeAt(l, k + 1))
numFullLines = numFullLines + len(rowsToRemove)
if numFullLines > 0:
self.numLinesRemoved = self.numLinesRemoved + numFullLines
statusbar.Text = str(self.numLinesRemoved)
self.isWaitingAfterLine = True
self.curPiece.SetShape(Tetrominoes.NoShape)
self.Refresh()
def NewPiece(self):
self.curPiece = self.nextPiece
statusbar = self.Parent.statusbar
self.nextPiece.SetRandomShape()
self.curX = Board.BoardWidth / 2 + 1
self.curY = Board.BoardHeight - 1 + self.curPiece.MinY()
if not self.TryMove(self.curPiece, self.curX, self.curY):
self.curPiece.SetShape(Tetrominoes.NoShape)
self.timer.Stop()
self.isStarted = False
statusbar.Text = 'Game over'
def TryMove(self, newPiece, newX, newY):
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
return False
if self.ShapeAt(x, y) != Tetrominoes.NoShape:
return False
self.curPiece = newPiece
self.curX = newX
self.curY = newY
self.Refresh()
return True
def DrawSquare(self, g, x, y, shape):
colors = [ (0, 0, 0), (204, 102, 102),
(102, 204, 102), (102, 102, 204),
(204, 204, 102), (204, 102, 204),
(102, 204, 204), (218, 170, 0) ]
light = [ (0, 0, 0), (248, 159, 171),
(121, 252, 121), (121, 121, 252),
(252, 252, 121), (252, 121, 252),
(121, 252, 252), (252, 198, 0) ]
dark = [ (0, 0, 0), (128, 59, 59),
(59, 128, 59), (59, 59, 128),
(128, 128, 59), (128, 59, 128),
(59, 128, 128), (128, 98, 0) ]
pen = Pen(Color.FromArgb(light[shape][0], light[shape][1],
light[shape][2]), 1)
pen.StartCap = LineCap.Flat
pen.EndCap = LineCap.Flat
g.DrawLine(pen, x, y + self.SquareHeight() - 1, x, y)
g.DrawLine(pen, x, y, x + self.SquareWidth() - 1, y)
darkpen = Pen(Color.FromArgb(dark[shape][0], dark[shape][1],
dark[shape][2]), 1)
darkpen.StartCap = LineCap.Flat
darkpen.EndCap = LineCap.Flat
g.DrawLine(darkpen, x + 1, y + self.SquareHeight() - 1,
x + self.SquareWidth() - 1, y + self.SquareHeight() - 1)
g.DrawLine(darkpen, x + self.SquareWidth() - 1,
y + self.SquareHeight() - 1, x + self.SquareWidth() - 1, y + 1)
g.FillRectangle(SolidBrush(Color.FromArgb(colors[shape][0], colors[shape][1],
colors[shape][2])), x + 1, y + 1, self.SquareWidth() - 1,
self.SquareHeight() - 2)
pen.Dispose()
darkpen.Dispose()
class Shape(object):
coordsTable = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
((0, -1), (0, 0), (1, 0), (1, 1)),
((0, -1), (0, 0), (0, 1), (0, 2)),
((-1, 0), (0, 0), (1, 0), (0, 1)),
((0, 0), (1, 0), (0, 1), (1, 1)),
((-1, -1), (0, -1), (0, 0), (0, 1)),
((1, -1), (0, -1), (0, 0), (0, 1))
)
def __init__(self):
self.coords = [[0,0] for i in range(4)]
self.pieceShape = Tetrominoes.NoShape
self.SetShape(Tetrominoes.NoShape)
def GetShape(self):
return self.pieceShape
def SetShape(self, shape):
table = Shape.coordsTable[shape]
for i in range(4):
for j in range(2):
self.coords[i][j] = table[i][j]
self.pieceShape = shape
def SetRandomShape(self):
rand = Random()
self.SetShape(rand.Next(1, 7))
def x(self, index):
return self.coords[index][0]
def y(self, index):
return self.coords[index][1]
def SetX(self, index, x):
self.coords[index][0] = x
def SetY(self, index, y):
self.coords[index][1] = y
def MaxX(self):
m = self.coords[0][0]
for i in range(4):
m = max(m, self.coords[i][0])
return m
def MinY(self):
m = self.coords[0][1]
for i in range(4):
m = min(m, self.coords[i][1])
return m
def RotatedLeft(self):
if self.pieceShape == Tetrominoes.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.SetX(i, self.y(i))
result.SetY(i, -self.x(i))
return result
def RotatedRight(self):
if self.pieceShape == Tetrominoes.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.SetX(i, -self.y(i))
result.SetY(i, self.x(i))
return result
class IForm(Form):
def __init__(self):
self.Text = 'Tetris'
self.Width = 200
self.Height = 430
self.FormBorderStyle = FormBorderStyle.FixedSingle
board = Board()
board.Width = 195
board.Height = 380
self.Controls.Add(board)
self.statusbar = StatusBar()
self.statusbar.Parent = self
self.statusbar.Text = 'Ready'
board.Start()
self.CenterToScreen()
Application.Run(IForm())
我对游戏做了一些简化,以便于理解。 游戏启动后立即开始。 我们可以通过按p
键暂停游戏。 空格键将把俄罗斯方块放在底部。 d
键会将棋子下降一行。 (它可以用来加快下降速度。)游戏以恒定速度运行,没有实现加速。 分数是我们已删除的行数。
class Tetrominoes(object):
NoShape = 0
ZShape = 1
SShape = 2
LineShape = 3
TShape = 4
SquareShape = 5
LShape = 6
MirroredLShape = 7
tetrominoes 有七种不同类型。
...
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
...
在开始游戏周期之前,我们先初始化一些重要的变量。 self.board
变量是Tetrominoes
的列表。 它表示各种形状的位置以及板上形状的其余部分。
def ClearBoard(self):
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoes.NoShape)
ClearBoard()
方法清除电路板。 它用Tetrominoes.NoShape
值填充self.board
变量。
俄罗斯方块游戏中的绘图是通过OnPaint()
方法完成的。
for i in range(Board.BoardHeight):
for j in range(Board.BoardWidth):
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
if shape != Tetrominoes.NoShape:
self.drawSquare(g,
0 + j * self.squareWidth(),
boardTop + i * self.squareHeight(), shape)
游戏的绘图分为两个步骤。 在第一步中,我们绘制所有形状或已放置到板底部的形状的其余部分。 所有正方形都将记住在self.board
列表中。 我们使用ShapeAt()
方法访问它。
if self.curPiece.shape() != Tetrominoes.NoShape:
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.drawSquare(g, 0 + x * self.squareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
self.curPiece.shape())
下一步是绘制掉落的实际零件。
在OnKeyUp()
方法中,我们检查按键是否按下。
elif key == Keys.Left:
self.tryMove(self.curPiece, self.curX - 1, self.curY)
如果按向左箭头键,我们将尝试将棋子向左移动。 我们说尝试,因为这片可能无法移动。
在TryMove()
方法中,我们尝试移动形状。 如果无法移动该片段,则返回False
。
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
return False
if self.ShapeAt(x, y) != Tetrominoes.NoShape:
return False
如果形状在板的边缘或与其他零件相邻,则返回False
。
self.curPiece = newPiece
self.curX = newX
self.curY = newY
self.Refresh()
return True
否则,我们将当前的下降片放到新位置并返回True
。
def OnTick(self, sender, event):
if self.isWaitingAfterLine:
self.isWaitingAfterLine = False
self.NewPiece()
else:
self.OneLineDown()
在OnTick()
方法中,我们要么在前一个击中底部之后创建一个新片段,要么将下降的片段向下移动一行。
如果片段触底,我们将调用RemoveFullLines()
方法。 首先,我们找出所有实线。
rowsToRemove = []
for i in range(Board.BoardHeight):
n = 0
for j in range(Board.BoardWidth):
if not self.ShapeAt(j, i) == Tetrominoes.NoShape:
n = n + 1
if n == 10:
rowsToRemove.append(i)
我们在董事会中循环。 一排可以有十个形状。 如果该行已满,例如 n 等于 10,我们存储行号以供以后删除。
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.SetShapeAt(l, k, self.ShapeAt(l, k + 1))
这些代码行将删除所有行。 我们颠倒了rowsToRemove
列表的顺序,因此我们从最底部的全行开始。 我们要做的是通过将一行中的所有行向下放置一行来删除整行。 对于所有实线都会发生这种情况。在我们的情况下,我们使用天真重力。 这意味着碎片可能漂浮在空的间隙上方。
def NewPiece(self):
self.curPiece = self.nextPiece
statusbar = self.Parent.statusbar
self.nextPiece.SetRandomShape()
self.curX = Board.BoardWidth / 2 + 1
self.curY = Board.BoardHeight - 1 + self.curPiece.MinY()
if not self.TryMove(self.curPiece, self.curX, self.curY):
self.curPiece.SetShape(Tetrominoes.NoShape)
self.timer.Stop()
self.isStarted = False
statusbar.Text = 'Game over'
NewPiece()
方法随机创建一个新的俄罗斯方块。 如果棋子无法进入其初始位置,例如 TryMove()
方法返回False
,游戏结束。
colors = [ (0, 0, 0), (204, 102, 102),
... ]
light = [ (0, 0, 0), (248, 159, 171),
... ]
dark = [ (0, 0, 0), (128, 59, 59),
... ]
一共有三种颜色。 colours
列表存储正方形填充的颜色值。 七块每个都有其自己的颜色。 light
和dark
存储线条的颜色,使正方形看起来像 3D。 这些颜色是相同的,只是越来越浅。 我们将在正方形的顶部和左侧绘制两条浅色的线条,并在右侧和底部绘制两条深色的线条。
g.DrawLine(pen, x, y + self.SquareHeight() - 1, x, y)
g.DrawLine(pen, x, y, x + self.SquareWidth() - 1, y)
这两条线绘制一个正方形的亮线。
Shape
类保存有关俄罗斯方块的信息。
self.coords = [[0,0] for i in range(4)]
创建后,我们将创建一个空坐标列表。 该列表将保存俄罗斯方块的坐标。 例如,这些元组(0,-1),(0,0),(1,0),(1,1)表示旋转的 S 形。 下图说明了形状。
图:坐标
当绘制当前下降片时,将其绘制在self.curX
和self.curY
位置。 然后,我们查看坐标表并绘制所有四个正方形。
RotateLeft()
方法将一块向左旋转。
if self.pieceShape == Tetrominoes.SquareShape:
return self
如果我们有Tetrominoes.SquareShape
个,我们什么也不做。 此形状始终相同。
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.SetX(i, self.y(i))
result.SetY(i, -self.x(i))
return result
在其他情况下,我们更改作品的坐标。 要了解此代码,请查看上图。
图:俄罗斯方块
这是 IronPython Winforms 中的俄罗斯方块游戏。
Qt5 教程
这是 Qt5 教程。 在本教程中,您将学习使用 Qt5 和 C++ 进行 GUI 编程的基础。 Qt5 教程适合初学者和中级程序员。 这两个游戏的图像可以在中下移。 请注意,本教程使用 C++ 11 的功能。
目录
Qt
Qt 是一个跨平台的应用开发框架。 用 Qt 开发的一些知名应用是 KDE,Opera,Google Earth 和 Skype。 Qt 于 1995 年 5 月首次公开发布。它具有双重许可。 这意味着,它可以用于创建开源应用以及商业应用。 Qt 工具箱是一个非常强大的工具箱。 它在开源社区中已经建立。
相关教程
Qt4 教程涵盖了 Qt 库的先前版本。 Qt Quick 教程是 Qt Quick 的入门教程。 PyQt5 教程是将 Python 绑定到 Qt 的教程, Ruby Qt 教程是将 Qt Ruby 绑定的教程。
FreeBASIC GTK 教程
这是 FreeBASIC GTK 教程。 在本教程中,我们将使用 FreeBASIC 语言在 GTK 库中创建图形用户界面。 本教程适合初学者。 一旦了解了基础知识并且知道如何一起使用 GTK 和 FreeBASIC,您就可以继续从 GTK+ 教程中学习 GTK 库。
FreeBASIC 与与 GTK 一起使用的其他语言不同。 没有像 PyGTK 或 GTK# 这样的绑定。 在 FreeBASIC 中,我们直接在程序中调用 C 代码。 这样,FreeBASIC 与 C 语言紧密相连。
FreeBASIC
FreeBASIC 是一种流行的 BASIC(初学者的通用符号指令代码)编译器。 它于 2004 年出现,受到 QuickBASIC 和 C 语言的影响。 FreeBASIC 是一个免费的开源 32 位 BASIC 编译器。 为了完全支持许多 C 库,添加了许多功能,如指针,预处理器,宏,而其他 BASIC 编译器中找不到这些功能。
GTK
GTK 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK 库也称为 GIMP 工具包。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK 中创建的。 存在用于 C++ ,Python,Perl,Java,C# 和其他编程语言的语言绑定。
第一个例子
在我们的第一个代码示例中,我们将在屏幕上居中放置一个小窗口。
' ZetCode FreeBASIC GTK tutorial
'
' This program centers a window on
' the screen
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "Center")
gtk_widget_set_size_request(win, 250, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
gtk_widget_show(win)
gtk_main()
END 0
此代码在屏幕上使窗口居中。 它是 FreeBASIC 和 C 代码的混合体。
#include once "gtk/gtk.bi"
这是从 FreeBASIC 运行 GTK 代码所需的头文件。
#define NULL 0
FreeBASIC 中没有内置的NULL
值。 为了更类似于 GTK C 代码,我们定义了一个NULL
值。
Dim As GtkWidget Ptr win
我们声明一个指向GtkWidget
的指针。
gtk_init(NULL, NULL)
我们启动 GTK 库。
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
我们创建一个GtkWidget
窗口。 窗口类型为GTK_WINDOW_TOPLEVEL
。 顶层窗口具有标题栏和边框。 它们由窗口管理器管理。
gtk_window_set_title(GTK_WINDOW(win), "Center")
gtk_widget_set_size_request(win, 250, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
这三行为窗口设置标题,调整窗口大小并将其放置在屏幕中央。
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
如果单击 x 标记,或按 Alt + F4
终止应用,则会发出破坏信号。 默认情况下,该窗口不对销毁信号做出反应。 我们必须通过将destroy
信号连接到gtk_main_quit()
函数来显式终止应用。 有两个重要角色。 下划线和@
字符。 下划线是 FreeBASIC 中的换行符。 如果代码行超过一行,则必须使用它。 @
字符为我们提供了gtk_main_quit()
函数的地址。 在 C 语言中,函数名称是函数的地址。 在 FreeBASIC 中并非如此,这就是我们使用@字符的原因。
gtk_widget_show(win)
该窗口已在内存中创建。 现在我们使用gtk_widget_show
函数在屏幕上显示窗口。
gtk_main()
我们进入应用的主循环。 从这一点开始,应用就坐下来等待事件发生。
END 0
我们完成了 FreeBASIC 代码。
$ fbc simple.bas
$ ./simple
我们编译并运行该示例。
图:简单 example
关闭按钮
在第二个示例中,我们在窗口上放置一个按钮小部件。 首先,我们在窗口上放置一个固定的容器,然后在该容器上放置按钮。 单击该按钮将终止该应用。
' ZetCode FreeBASIC GTK tutorial
'
' In this example, we place a close
' button on the window. Clicking on the
' button will terminate the application.
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr fixed
Dim As GtkWidget Ptr button
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "Close")
gtk_widget_set_size_request(win, 250, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
fixed = gtk_fixed_new()
gtk_container_add(GTK_CONTAINER(win), fixed)
button = gtk_button_new_with_label("Close")
gtk_widget_set_size_request(button, 80, 35)
gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50)
g_signal_connect(G_OBJECT(button), "clicked", _
G_CALLBACK (@gtk_main_quit), NULL)
gtk_widget_show_all(win)
gtk_main()
END 0
这是关闭按钮示例的源代码。
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr fixed
Dim As GtkWidget Ptr button
我们声明三个变量。 一个用于顶层窗口,它是 GUI 应用的基本框架。 一个用于容器。 容器是一种特殊的小部件。 它是不可见的。 其唯一目的是包含其他小部件。 在我们的例子中,是按钮小部件。
fixed = gtk_fixed_new()
gtk_container_add(GTK_CONTAINER(win), fixed)
这两行创建一个容器小部件,并将其放置在窗口小部件内。
button = gtk_button_new_with_label("Close")
gtk_widget_set_size_request(button, 80, 35)
我们创建一个按钮小部件,并将其设置为80x35
像素。
gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50)
我们在x = 50
,y = 50
坐标处将一个按钮放入容器。 这称为绝对定位。 它适合于小示例,但在更复杂的程序中,我们使用布局管理器。
g_signal_connect(G_OBJECT(button), "clicked", _
G_CALLBACK (@gtk_main_quit), NULL)
当我们点击按钮时,发出点击的信号。 g_signal_connect()
函数将回调(在我们的情况下为内置gtk_main_quit()
函数)连接到按钮小部件发出的clicked
信号。
gtk_widget_show_all(win)
我们创建了三个小部件。 我们可以在每个窗口上调用gtk_widget_show()
,或者在窗口小部件上调用gtk_widget_show_all()
,该窗口小部件一步显示所有三个小部件。
图:关闭按钮
显示图像
在以下示例中,我们在窗口上显示图像。
' ZetCode FreeBASIC GTK tutorial
'
' This example shows an image on
' the window
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#include once "gtk/gdk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr image
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "Red Rock")
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
gtk_window_set_resizable(GTK_WINDOW(win), FALSE)
gtk_container_set_border_width(GTK_CONTAINER(win), 2)
image = gtk_image_new_from_file("redrock.png")
gtk_container_add(GTK_CONTAINER(win), image)
gtk_widget_show_all(win)
gtk_main()
END 0
我们在窗口上显示图像。
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr image
在此示例中,我们只有两个小部件。 窗口小部件和图像小部件。
gtk_window_set_resizable(GTK_WINDOW(win), FALSE)
窗口无法调整大小。 请注意,我们没有为窗口设置大小。 在这种情况下,窗口将自动适合图像的大小。
gtk_container_set_border_width(GTK_CONTAINER(win), 2)
我们在容器周围留出一些空白空间。
image = gtk_image_new_from_file("redrock.png")
gtk_container_add(GTK_CONTAINER(win), image)
我们从 PNG 文件创建图像小部件。 我们将图像放置在窗口上。 窗口本身就是一个简单的容器。 它可以只包含一个小部件。
简单菜单示例
在下一个代码示例中,我们将创建一个简单的菜单。
' ZetCode FreeBASIC GTK tutorial
'
' This example shows a simple menu
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr vbox
Dim As GtkWidget Ptr menubar
Dim As GtkWidget Ptr fmenu
Dim As GtkWidget Ptr fmi
Dim As GtkWidget Ptr qmi
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "Simple menu")
gtk_widget_set_size_request(win, 250, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
vbox = gtk_vbox_new(FALSE, 0)
gtk_container_add(GTK_CONTAINER(win), vbox)
menubar = gtk_menu_bar_new()
fmenu = gtk_menu_new()
fmi = gtk_menu_item_new_with_label("File")
qmi = gtk_menu_item_new_with_label("Quit")
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fmi), fmenu)
gtk_menu_shell_append(GTK_MENU_SHELL(fmenu), qmi)
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fmi)
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0)
g_signal_connect(G_OBJECT(qmi), "activate", _
G_CALLBACK(@gtk_main_quit), NULL)
gtk_widget_show_all(win)
gtk_main()
END 0
这些代码行创建一个简单的菜单。 它只有一个菜单项,选择该菜单项将终止应用。 创建菜单栏有点令人困惑。 我们必须记住,菜单栏和菜单都来自同一个窗口小部件,即菜单外壳。 菜单项仅是菜单的有效子项。 它们还用于实现子菜单。
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr vbox
Dim As GtkWidget Ptr menubar
Dim As GtkWidget Ptr fmenu
Dim As GtkWidget Ptr fmi
Dim As GtkWidget Ptr qmi
我们有六个小部件的六个变量。 一个小部件是一个垂直框,它将设置布局。 菜单栏是一个水平小部件,我们在其上放置菜单。 菜单包含菜单项,这些菜单项会执行某些操作。 就像保存文档或终止应用一样。
vbox = gtk_vbox_new(FALSE, 0)
gtk_container_add(GTK_CONTAINER(win), vbox)
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3)
我们创建一个垂直框,使其成为顶层窗口的容器。 gtk_vbox_new()
函数的第一个参数称为同质参数。 如果设置为TRUE
,则一个框中的所有小部件都具有相等的空间分配。 我们不希望这样,因为菜单栏仅占用窗口的一小部分。 第二个参数是子项之间的空间。
menubar = gtk_menu_bar_new()
fmenu = gtk_menu_new()
在这段代码中,我们创建一个菜单栏和一个菜单。
fmi = gtk_menu_item_new_with_label("File")
qmi = gtk_menu_item_new_with_label("Quit")
创建两个菜单项。
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fmi), fmenu)
此代码行实现了文件菜单。 逻辑是菜单栏是菜单外壳。 文件菜单也是菜单外壳。 这就是为什么我们将文件菜单视为子菜单或子外壳。
gtk_menu_shell_append(GTK_MENU_SHELL(fmenu), qmi)
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fmi)
菜单项通过调用gtk_menu_shell_append()
函数实现。 菜单项将附加到菜单外壳。 在我们的情况下,退出菜单项被附加到文件菜单,并且文件菜单项也被附加到菜单栏。
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0)
gtk_box_pack_start()
函数调用将菜单栏添加到垂直框中。 第一个参数是容器,我们在其中放置子窗口小部件。 第二个参数是子窗口小部件。 第三个参数是expand
参数。 扩展参数设置为TRUE
的子代将占用垂直框的额外空间。 这些额外的空间将在它们之间平均分配。 我们不希望菜单栏占用任何额外的空间,因此我们将expand
参数设置为FALSE
。 如果将扩展设置为FALSE
,则第四个参数无效。 最后一个参数是padding
,它在子小部件之间增加了一些额外的空间。 我们不添加任何额外的空间。
g_signal_connect(G_OBJECT(qmi), "activate", _
G_CALLBACK(@gtk_main_quit), NULL)
通过选择退出菜单项,我们终止了该应用。
图:简单菜单 example
输入信号
以下示例将说明我们如何对输入信号做出反应。 当我们使用鼠标指针进入小部件的区域时,将发出enter
信号。
' ZetCode FreeBASIC GTK tutorial
'
' In this code example, we react to the
' enter signal by changing the background
' color of a button widget
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr fixed
Dim As GtkWidget Ptr btn
Sub enter_button Cdecl (Byval widget As GtkWidget Ptr, _
Byval dat As gpointer)
Dim col As GdkColor
col.red = 27000
col.green = 30325
col.blue = 34181
gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, @col)
End Sub
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "enter signal")
gtk_widget_set_size_request(win, 230, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
fixed = gtk_fixed_new()
gtk_container_add(GTK_CONTAINER(win), fixed)
btn = gtk_button_new_with_label("Button")
gtk_widget_set_size_request(btn, 80, 35)
gtk_fixed_put(GTK_FIXED(fixed), btn, 50, 50)
g_signal_connect(G_OBJECT(btn), "enter", _
G_CALLBACK(@enter_button), NULL)
gtk_widget_show_all(win)
gtk_main()
END 0
我们将按钮小部件放入固定的容器中。 当我们输入按钮小部件的区域时,将调用enter_button()
子例程。 在子例程中,我们更改按钮的背景色。
Sub enter_button Cdecl (Byval widget As GtkWidget Ptr, _
Byval dat As gpointer)
...
End Sub
这是enter_button()
子例程,我们在其中对enter
信号做出反应。 Cdecl
关键字指定子例程的调用约定。 在此调用约定中,所有参数都以列出它们的相反顺序(即从右到左)传递。 对于我们而言,这并不重要。
Dim col As GdkColor
我们创建一个本地GdkColor
变量。 它是用于描述 GTK 应用中的颜色的结构。
col.red = 27000
col.green = 30325
col.blue = 34181
在这里,我们设置颜色。
gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, @col)
gtk_widget_modify_bg()
设置处于特定状态的小部件的背景颜色。 在我们的情况下,状态为GTK_STATE_PRELIGHT
,这是鼠标指针悬停在小部件上方时的状态。 第三个参数是指向颜色结构的指针。
g_signal_connect(G_OBJECT(btn), "enter", _
G_CALLBACK(@enter_button), NULL)
在这里,我们将enter_button()
子例程连接到按钮小部件发出的enter
信号。
复选按钮示例
在下面的示例中,我们将一个检查按钮放入固定容器中。 我们将通过选中和取消选中复选按钮来显示和隐藏窗口的标题。
' ZetCode FreeBASIC GTK tutorial
'
' This example a check button widget
' toggles the title of the window
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr cbtn
Dim As GtkWidget Ptr frame
Sub toggle_title Cdecl (Byval widget As GtkWidget Ptr, _
Byval win As gpointer)
If gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) Then
gtk_window_set_title(win, "Check button")
Else
gtk_window_set_title(win, "")
End If
End Sub
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "Check button")
gtk_widget_set_size_request(win, 250, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
frame = gtk_fixed_new()
gtk_container_add(GTK_CONTAINER(win), frame)
cbtn = gtk_check_button_new_with_label("Show title")
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cbtn), TRUE)
gtk_fixed_put(GTK_FIXED(frame), cbtn, 50, 50)
g_signal_connect(cbtn, "clicked", _
G_CALLBACK(@toggle_title), cast(gpointer, win))
gtk_widget_show_all(win)
gtk_main()
END 0
这是检查按钮示例的代码。
Sub toggle_title Cdecl (Byval widget As GtkWidget Ptr, _
Byval win As gpointer)
如果单击复选按钮,我们将调用toggle_title()
函数。 在这种情况下,我们需要指向两个对象的指针。 我们需要一个指向检查按钮的指针来确定它是否被检查。 我们还需要指向窗口的指针来设置或取消设置其标题。
If gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) Then
gtk_window_set_title(win, "Check button")
Else
gtk_window_set_title(win, "")
我们使用gtk_toggle_button_get_active()
函数确定检查按钮的状态。 我们使用gtk_window_set_title()
函数设置窗口的标题。
g_signal_connect(cbtn, "clicked", _
G_CALLBACK(@toggle_title), cast(gpointer, win))
g_signal_connect()
的最后一个参数通常是一些我们要传递给回调函数的数据。 这次,我们需要将另一个指针传递给窗口对象。 在这种情况下,我们需要进行铸造。 这是因为函数需要gpointer
,并且窗口是GtkWidget
类型。 FreeBASIC 有一个case
关键字来进行转换。
图:复选按钮 example
ComboBox
示例
在 FreeBASIC GTK 教程的最后一个示例中,我们将展示组合框小部件。
' ZetCode FreeBASIC GTK tutorial
'
' In this example, we present the combo box
' widget
'
' author Jan Bodnar
' last modified July 2010
' website www.zetcode.com
#include once "gtk/gtk.bi"
#define NULL 0
Dim As GtkWidget Ptr win
Dim As GtkWidget Ptr combo
Dim As GtkWidget Ptr fixed
Dim As GtkWidget Ptr label
Sub combo_selected Cdecl (Byval widget As GtkWidget Ptr, _
Byval win As gpointer)
Dim As gchar Ptr text
text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget))
gtk_label_set_text(GTK_LABEL(win), text)
g_free(text)
End Sub
gtk_init(NULL, NULL)
win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "Check button")
gtk_widget_set_size_request(win, 230, 150)
gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER)
g_signal_connect(G_OBJECT(win), "destroy", _
G_CALLBACK (@gtk_main_quit), NULL)
fixed = gtk_fixed_new()
combo = gtk_combo_box_new_text()
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Ubuntu")
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Mandriva")
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Fedora")
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Mint")
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Gentoo")
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Debian")
gtk_fixed_put(GTK_FIXED(fixed), combo, 50, 50)
gtk_container_add(GTK_CONTAINER(win), fixed)
label = gtk_label_new("-")
gtk_fixed_put(GTK_FIXED(fixed), label, 50, 110)
g_signal_connect(G_OBJECT(combo), "changed", _
G_CALLBACK(@combo_selected), cast(gpointer, label))
gtk_widget_show_all(win)
gtk_main()
END 0
我们有两个小部件:组合框和标签。 从组合框中选择的选项将显示在标签中。
Dim As gchar Ptr text
text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget))
gtk_label_set_text(GTK_LABEL(win), text)
g_free(text)
在这些行中,我们从组合框中检索文本并将其设置为标签。 我们使用指向gchar
的指针。 它是glib
库的基本类型,它是 GTK 库的基础。 我们释放从组合框中检索文本时创建的内存。
combo = gtk_combo_box_new_text()
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Ubuntu")
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Mandriva")
...
将创建组合框窗口小部件并填充数据。
label = gtk_label_new("-")
gtk_fixed_put(GTK_FIXED(fixed), label, 50, 110)
标签窗口小部件已创建并放入容器中。
g_signal_connect(G_OBJECT(combo), "changed", _
G_CALLBACK(@combo_selected), cast(gpointer, label))
我们将combo_selected()
函数插入到组合框的changed
信号中。 我们再次进行铸造。
图:组合框小部件
这是 FreeBASIC GTK 教程。 我们有几个示例,这些示例说明了如何使用 FreeBASIC 语言和 GTK 库。 这些示例清楚地表明,我们混合使用 FreeBASIC 和 C 语言。 一旦知道如何将它们放在一起,就可以进行 GTK 编程。 有关 GTK 库的更多信息,请参见。
Jython Swing 教程
这是 Jython Swing 教程。 在本教程中,您将学习 Jython & Swing 中 GUI 编程的基础。 本教程适合初学者和中级程序员。
目录
Swing
Swing 库是 Java 编程语言的官方 Java GUI 工具箱。 它用于使用 Java 创建图形用户界面。 Swing 是一个高级 GUI 工具箱。 它具有丰富的组件集。 从基本的按钮,标签,滚动条到高级的组件(例如树和表格)。 Swing 本身是用 Java 编写的。 Swing 也可用于其他语言。 例如 JRuby,Jython,Groovy 或 Scala。
相关教程
在 ZetCode 上有两个类似的教程:原始的 Java Swing 教程和 JRuby Swing 教程。
Jython Swing 简介
在 Jython Swing 教程的这一部分中,我们将介绍 Swing 工具包并使用 Jython 编程语言创建第一个程序。
本教程的目的是帮助您开始使用带有 Jython 语言的 Swing 工具包。 可以在此处下载本教程中使用的图像。 我使用了 Gnome 项目的 Tango 图标包中的一些图标。
关于
Swing 库是 Java 编程语言的官方 Java GUI 工具箱。 它用于使用 Java 创建图形用户界面。 Swing 是一个高级 GUI 工具箱。 它具有丰富的组件集。 从基本的按钮,标签,滚动条到高级的组件(例如树和表格)。 Swing 本身是用 Java 编写的。 Swing 也可用于其他语言。 例如 Jython,JRuby,Groovy 或 Scala。
Jython 是用 Java 编写的 Python 编程语言的实现。 Jython 可以导入任何 Java 类。
执行本教程中的示例有两种基本方法。 一种方法是安装 Python NetBeans 插件。 它还包含 Jython。 创建新的 Python 项目时,请确保选择 Jython 平台。
另一种方法是从 jython.org 网站下载安装程序。
$ java -jar jython_installer-2.5.2rc2.jar
我们安装了 Jython。 您将经历一系列对话框。
$ java -jar jython.jar simple.py
我们已经在选定目录中安装了 Jython。 在此目录中,我们将找到jython.jar
文件,该文件用于执行 Jython 脚本。
$ cat /usr/local/bin/jython
#!/bin/bash
/home/vronskij/bin/jdk1.6.0_21/bin/java -jar /home/vronskij/bin/jython/jython.jar $1
(可选)我们可以创建一个 bash 文件,该文件将自动启动我们的 Jython 脚本。 然后,我们可以将#!/usr/bin/local/jython
路径放入脚本。
简单的例子
在第一个示例中,我们将在屏幕上显示一个基本窗口。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This example shows a simple
window on the screen.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from javax.swing import JFrame
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setTitle("Simple")
self.setSize(250, 200)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
尽管这段代码很小,但是应用窗口可以做很多事情。 可以调整大小,最大化,最小化。 随之而来的所有复杂性对应用员都是隐藏的。
from javax.swing import JFrame
我们导入一个JFrame
类。 JFrame
是带有标题和边框的顶层窗口。
self.initUI()
我们将用户界面的创建委托给initUI()
方法。
self.setTitle("Simple")
我们使用setTitle()
方法设置窗口的标题。
self.setSize(250, 200)
我们设置窗口的大小。
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
如果单击标题栏的关闭按钮,此方法可确保窗口终止。 默认情况下,没有任何反应。
self.setLocationRelativeTo(None)
我们将窗口置于屏幕中央。
self.setVisible(True)
最后,窗口显示在屏幕上。
工具提示
工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This code shows a tooltip on
a window and a button.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from javax.swing import JButton
from javax.swing import JFrame
from javax.swing import JPanel
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
panel = JPanel()
self.getContentPane().add(panel)
panel.setLayout(None)
panel.setToolTipText("A Panel container")
button = JButton("Button")
button.setBounds(100, 60, 100, 30)
button.setToolTipText("A button component")
panel.add(button)
self.setTitle("Tooltips")
self.setSize(300, 200)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在示例中,我们为框架和按钮设置工具提示。
panel = JPanel()
self.getContentPane().add(panel)
我们创建一个JPanel
组件。 它是一个通用的轻量级容器。 JFrame
有一个区域,您可以在其中放置名为内容窗格的组件。 我们将面板放入此窗格。
panel.setLayout(None)
默认情况下,JPanel
具有一个FlowLayout
管理器。 布局管理器用于将小部件放置在容器上。 如果我们调用setLayout(None)
,则可以绝对定位组件。 为此,我们使用setBounds()
方法。
panel.setToolTipText("A Panel container")
要启用工具提示,我们调用setTooltipText()
方法。
图:工具提示
退出按钮
在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program creates a quit
button. When we press the button,
the application terminates.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.lang import System
from javax.swing import JButton
from javax.swing import JFrame
from javax.swing import JPanel
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
panel = JPanel()
self.getContentPane().add(panel)
panel.setLayout(None)
qbutton = JButton("Quit", actionPerformed=self.onQuit)
qbutton.setBounds(50, 60, 80, 30)
panel.add(qbutton)
self.setTitle("Quit button")
self.setSize(300, 200)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onQuit(self, e):
System.exit(0)
if __name__ == '__main__':
Example()
我们在窗口上放置一个JButton
。 我们将向该按钮添加一个动作监听器。
qbutton = JButton("Quit", actionPerformed=self.onQuit)
qbutton.setBounds(50, 60, 80, 30)
在这里,我们创建一个按钮。 我们通过调用setBounds()
方法对其进行定位。 actionPerformed
参数指定单击按钮时调用的方法。
def onQuit(self, e):
System.exit(0)
onQuit() method
退出应用。
图:退出按钮
本节介绍了使用 Jython 语言的 Swing 工具包。
Jython Swing 中的布局管理
在 Jython Swing 编程教程的这一部分中,我们将介绍布局管理器。
在设计应用的 GUI 时,我们决定要使用哪些组件以及如何在应用中组织这些组件。 为了组织我们的组件,我们使用专门的不可见对象,称为布局管理器。 Swing 工具箱包含两种组件:容器和子组件。 容器将子项分组为合适的布局。 要创建布局,我们使用布局管理器。
绝对定位
在大多数情况下,程序员应使用布局管理器。 在某些情况下,我们可以使用绝对定位。 在绝对定位中,程序员以像素为单位指定每个小部件的位置和大小。 如果我们调整窗口大小,则小部件的大小和位置不会改变。 在各种平台上,应用看起来都不同,在 Linux 上看起来不错,在 Mac OS 上看起来不太正常。 在应用中更改字体可能会破坏布局。 如果将应用翻译成另一种语言,则必须重做布局。 对于所有这些问题,仅在有理由时才使用绝对定位。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, we lay out widgets
using absolute positioning
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Color
from javax.swing import ImageIcon
from javax.swing import JFrame
from javax.swing import JPanel
from javax.swing import JLabel
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
panel = JPanel()
panel.setLayout(None)
panel.setBackground(Color(66, 66, 66))
self.getContentPane().add(panel)
rot = ImageIcon("rotunda.jpg")
rotLabel = JLabel(rot)
rotLabel.setBounds(20, 20, rot.getIconWidth(), rot.getIconHeight())
min = ImageIcon("mincol.jpg")
minLabel = JLabel(min)
minLabel.setBounds(40, 160, min.getIconWidth(), min.getIconHeight())
bar = ImageIcon("bardejov.jpg")
barLabel = JLabel(bar)
barLabel.setBounds(170, 50, bar.getIconWidth(), bar.getIconHeight())
panel.add(rotLabel)
panel.add(minLabel)
panel.add(barLabel)
self.setTitle("Absolute")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(350, 300)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在此示例中,我们使用绝对定位显示了三幅图像。
panel.setLayout(None)
Swing 中的容器已经具有默认的布局管理器。 JPanel
具有FlowLayout
管理器作为其默认布局管理器。 我们将setLayout()
方法与无参数一起使用,以删除默认的布局管理器,而改用绝对定位。
rot = ImageIcon("rotunda.jpg")
rotLabel = JLabel(rot)
rotLabel.setBounds(20, 20, rot.getIconWidth(), rot.getIconHeight())
我们创建一个ImageIcon
对象。 我们将图标放入JLabel
组件中以显示它。 然后,我们使用setBounds()
方法将标签放置在面板上。 前两个参数是标签的 x,y 位置。 第 3 和第 4 个参数是图标的宽度和高度。
panel.add(rotLabel)
我们将标签添加到面板容器中。
图:绝对定位
按钮示例
在下面的示例中,我们将在窗口的右下角放置两个按钮。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, use box layouts
to position two buttons in the
bottom right corner of the window
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Dimension
from javax.swing import JButton
from javax.swing import JFrame
from javax.swing import JPanel
from javax.swing import BoxLayout
from javax.swing import Box
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
basic = JPanel()
basic.setLayout(BoxLayout(basic, BoxLayout.Y_AXIS))
self.add(basic)
basic.add(Box.createVerticalGlue())
bottom = JPanel()
bottom.setAlignmentX(1.0)
bottom.setLayout(BoxLayout(bottom, BoxLayout.X_AXIS))
okButton = JButton("OK")
closeButton = JButton("Close")
bottom.add(okButton)
bottom.add(Box.createRigidArea(Dimension(5, 0)))
bottom.add(closeButton)
bottom.add(Box.createRigidArea(Dimension(15, 0)))
basic.add(bottom)
basic.add(Box.createRigidArea(Dimension(0, 15)))
self.setTitle("Buttons")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(300, 150)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
我们将创建两个面板。 基本面板具有垂直框布局。 底部面板有一个水平面板。 我们将在基础面板中放置一个底部面板。 我们将右对齐底部面板。 窗口顶部和底部面板之间的空间是可扩展的。 这是通过垂直胶水完成的。
basic = JPanel()
basic.setLayout(BoxLayout(basic, BoxLayout.Y_AXIS))
...
bottom = JPanel()
...
bottom.setLayout(BoxLayout(bottom, BoxLayout.X_AXIS))
基本面板具有垂直框布局。 底部面板具有水平框布局。
bottom.setAlignmentX(1.0)
底部面板右对齐。
basic.add(Box.createVerticalGlue())
我们创建一个垂直胶水。 胶水是垂直可扩展的白色空间,它将带有按钮的水平框推到底部。
okButton = JButton("OK")
closeButton = JButton("Close")
这是两个将进入窗口右下角的按钮。
bottom.add(okButton)
bottom.add(Box.createRigidArea(Dimension(5, 0)))
我们将“确定”按钮放入水平框中。 我们在按钮旁边放置了一些刚性空间。 这样两个按钮之间会有一些空间。
basic.add(Box.createRigidArea(Dimension(0, 15)))
我们在按钮和窗口的边框之间留出一些空间。
图:按钮示例
Windows 示例
以下示例使用GroupLayout
管理器创建 Windows 对话框。 该对话框来自 JDeveloper 应用。
GroupLayout
管理器将布局的创建分为两个步骤。 第一步,我们沿着水平轴布置组件。 在第二步中,我们沿垂直轴布置组件。 这在布局管理器中是一个不寻常的想法,但效果很好。
有两种类型的安排:顺序安排和并行安排。 在两种布局中,我们都可以顺序或并行排列组件。 在水平布局中,一行组件称为顺序组。 一列组件称为并行组。 在垂直布局中,一列组件称为顺序组。 一排组件称为并行组。 您必须正确理解这些定义才能与GroupLayout
管理器一起使用。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This code lays out components
using the GroupLayout manager
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Dimension
from java.awt import Color
from javax.swing import JButton
from javax.swing import SwingConstants
from javax.swing import JFrame
from javax.swing import JLabel
from javax.swing import JTextArea
from javax.swing import BorderFactory
from javax.swing import GroupLayout
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
layout = GroupLayout(self.getContentPane())
self.getContentPane().setLayout(layout)
layout.setAutoCreateGaps(True)
layout.setAutoCreateContainerGaps(True)
self.setPreferredSize(Dimension(350, 300))
windows = JLabel("Windows")
area = JTextArea()
area.setEditable(False)
area.setBorder(BorderFactory.createLineBorder(Color.gray))
activate = JButton("Activate")
close = JButton("Close")
help = JButton("Help")
ok = JButton("OK")
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(windows)
.addComponent(area)
.addComponent(help))
.addGroup(layout.createParallelGroup()
.addComponent(activate)
.addComponent(close)
.addComponent(ok))
)
layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(windows)
.addGroup(layout.createParallelGroup()
.addComponent(area)
.addGroup(layout.createSequentialGroup()
.addComponent(activate)
.addComponent(close)))
.addGroup(layout.createParallelGroup()
.addComponent(help)
.addComponent(ok))
)
layout.linkSize(SwingConstants.HORIZONTAL, [ok, help, close, activate])
self.pack()
self.setTitle("Windows")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在上面的示例中,我们看到了addComponent()
方法的链式调用。 这是可能的,因为addComponent()
方法返回了调用它的组。 因此,我们不需要局部变量来保存组。 另请注意,代码已适当缩进以提高可读性。
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(windows)
.addComponent(area)
.addComponent(help))
.addGroup(layout.createParallelGroup()
.addComponent(activate)
.addComponent(close)
.addComponent(ok))
)
第一步,我们有一个水平布局。 它由两组平行的三个部分组成。
layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(windows)
.addGroup(layout.createParallelGroup()
.addComponent(area)
.addGroup(layout.createSequentialGroup()
.addComponent(activate)
.addComponent(close)))
.addGroup(layout.createParallelGroup()
.addComponent(help)
.addComponent(ok))
)
垂直布局有点复杂。 首先,我们添加一个组件。 然后,我们添加一个包含单个组件的并行组和一个包含两个组件的顺序组。 最后,我们添加两个组件的并行组。
layout.linkSize(SwingConstants.HORIZONTAL, [ok, help, close, activate])
此行使所有按钮的大小相同。 我们只需要设置它们的宽度,因为默认情况下它们的高度已经相同。
图:窗口示例
查看示例的屏幕截图。 注意,可以将组件分为垂直和水平组件集。 例如,标签区域和“帮助”按钮组件可以形成垂直的组件组。 这正是GroupLayout
管理器所做的。 它通过形成组件的垂直和水平组来布局组件。
在 Jython Swing 教程的这一部分中,我们提到了小部件的布局管理。
Jython Swing 中的组件
在 Jython Swing 编程教程的这一部分中,我们将介绍基本的 Swing 组件。
组件是 GUI 应用的基本构建块。 多年来,一些组件已成为所有 OS 平台上所有工具包中的标准组件。 例如,按钮,复选框或滚动条。 Swing 具有丰富的组件集,可满足大多数编程需求。 可以将更多专用组件创建为自定义组件。
JCheckBox
JCheckBox
是具有两种状态的组件:开和关。 开状态通过复选标记显示。 它用来表示一些布尔属性。 JCheckBox
组件提供了一个带有文本标签的复选框。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program uses JCheckBox
component to show/hide the title
of the window
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Dimension
from javax.swing import Box
from javax.swing import BoxLayout
from javax.swing import JCheckBox
from javax.swing import JFrame
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setLayout(BoxLayout(self.getContentPane(), BoxLayout.Y_AXIS))
self.add(Box.createRigidArea(Dimension(15, 20)))
cb = JCheckBox("Show Title", True, actionPerformed=self.onSelect)
cb.setFocusable(False)
self.add(cb)
self.setTitle("JCheckBox example")
self.setSize(280, 200)
self.setResizable(False)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onSelect(self, e):
source = e.getSource()
isSelected = source.isSelected()
if isSelected:
self.setTitle("JCheckBox example")
else:
self.setTitle("")
if __name__ == '__main__':
Example()
在我们的示例中,我们在窗口上放置了一个复选框。 复选框显示/隐藏窗口的标题。
self.setLayout(BoxLayout(self.getContentPane(), BoxLayout.Y_AXIS))
self.add(Box.createRigidArea(Dimension(15, 20)))
在此示例中,我们使用BoxLayout
布局管理器。 我们在此处放置一些空间,以使复选框不太靠近角落。
cb = JCheckBox("Show Title", True, actionPerformed=self.onSelect)
JCheckBox
组件已创建。 构造器的第一个参数是其文本标签。 第二个参数是一个布尔值,指示初始选择状态。 如果为True
,则选中该复选框。 第三个参数指定方法,当我们选中或取消选中复选框时将调用该方法。
cb.setFocusable(False)
我们禁用复选框的焦点。 可以使用空格键选择或取消选择具有焦点的JCheckBox
。
source = e.getSource()
isSelected = source.isSelected()
if isSelected:
self.setTitle("JCheckBox example")
else:
self.setTitle("")
从事件对象,我们获得源组件。 在我们的例子中是一个复选框。 我们找出复选框的选择状态。 根据复选框的状态,我们显示或隐藏窗口的标题。
图:JCheckBox
JLabel
JLabel
组件用于显示文本,图像或两者。 没有用户交互。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program uses JLabel component to
show lyrics of a song
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BorderLayout
from java.awt import Font
from javax.swing import BorderFactory
from javax.swing import JFrame
from javax.swing import JLabel
from javax.swing import JPanel
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
lyrics = """<html>It's way too late to think of<br>
Someone I would call now<br>
And neon signs got tired<br>
Red eye flights help the stars out<br>
I'm safe in a corner<br>
Just hours before me<br>
<br>
I'm waking with the roaches<br>
The world has surrendered<br>
I'm dating ancient ghosts<br>
The ones I made friends with<br>
The comfort of fireflies<br>
Long gone before daylight<br>
<br>
And if I had one wishful field tonight<br>
I'd ask for the sun to never rise<br>
If God leant his voice for me to speak<br>
I'd say go to bed, world<br>
<br>
I've always been too late<br>
To see what's before me<br>
And I know nothing sweeter than<br>
Champaign from last New Years<br>
Sweet music in my ears<br>
And a night full of no fears<br>
<br>
But if I had one wishful field tonight<br>
I'd ask for the sun to never rise<br>
If God passed a mic to me to speak<br>
I'd say stay in bed, world<br>
Sleep in peace</html>"""
panel = JPanel()
panel.setLayout(BorderLayout(10, 10))
label = JLabel(lyrics)
label.setFont(Font("Georgia", Font.PLAIN, 14))
panel.add(label, BorderLayout.CENTER)
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
self.add(panel)
self.pack()
self.setTitle("No Sleep")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
我们的示例在窗口中显示了歌曲的歌词。 我们可以在JLabel
组件中使用 HTML 标签。 我们使用<br>
标签来分隔行。
lyrics = """<html>It's way too late to think of<br>
Someone I would call now<br>
And neon signs got tired<br>
...
我们定义了多行文字。
label = JLabel(lyrics)
label.setFont(Font("Georgia", Font.PLAIN, 14))
在这里,我们创建标签组件。 我们将其字体设置为 14 像素高的普通乔治亚州。
panel.add(label, BorderLayout.CENTER)
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
我们将标签放在面板的中央。 我们在标签周围放置了 10px。
图:JLabel
组件
JSlider
JSlider
是一个组件,使用户可以通过在有限的间隔内滑动旋钮来以图形方式选择一个值。 我们的示例将显示音量控制。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program we use the JSlider
component to create a volume control
user interface
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BorderLayout
from java.awt import Dimension
from javax.swing import BorderFactory
from javax.swing import Box
from javax.swing import BoxLayout
from javax.swing import ImageIcon
from javax.swing import JFrame
from javax.swing import JLabel
from javax.swing import JPanel
from javax.swing import JSlider
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.mute = ImageIcon("mute.png")
self.min = ImageIcon("min.png")
self.med = ImageIcon("med.png")
self.max = ImageIcon("max.png")
panel = JPanel()
panel.setLayout(BoxLayout(panel, BoxLayout.X_AXIS))
panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40))
self.setLayout(BorderLayout())
panel.add(Box.createHorizontalGlue())
slider = JSlider(0, 150, 0, stateChanged=self.onSlide)
slider.setPreferredSize(Dimension(150, 30))
panel.add(slider)
panel.add(Box.createRigidArea(Dimension(5, 0)))
self.label = JLabel(self.mute, JLabel.CENTER)
panel.add(self.label)
panel.add(Box.createHorizontalGlue())
self.add(panel, BorderLayout.CENTER)
self.pack()
self.setTitle("JSlider")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onSlide(self, e):
sender = e.getSource()
value = sender.getValue()
if value == 0:
self.label.setIcon(self.mute)
elif value > 0 and value <= 30:
self.label.setIcon(self.min)
elif value > 30 and value < 80:
self.label.setIcon(self.med)
else:
self.label.setIcon(self.max)
if __name__ == '__main__':
Example()
在代码示例中,我们显示了JSlider
和JLabel
。 通过拖动滑块,我们可以更改标签组件上的图标。 我们有四个代表声音各种状态的图像。
self.mute = ImageIcon("mute.png")
在这里,我们创建一个图像图标。
panel.setLayout(BoxLayout(panel, BoxLayout.X_AXIS))
面板组件具有水平BoxLayout
。
panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40))
我们在面板周围创建 40px 的边框。
panel.add(Box.createHorizontalGlue())
我们在左右两侧都放置了可调整大小的空间。 这是为了防止JSlider
增长到不自然的大小。
slider = JSlider(0, 150, 0, stateChanged=self.onSlide)
这是一个JSlider
构造器。 参数为最小值,最大值和当前值。 当我们滑动滑块的旋钮时,将调用onSlide()
方法。
panel.add(Box.createRigidArea(Dimension(5, 0)))
我们在两个组件之间放置一个 5px 的刚性空间。 当滑块位于末端位置时,它们彼此之间过于靠近。
self.label = JLabel(self.mute, JLabel.CENTER)
该行创建一个具有指定图像和水平对齐方式的JLabel
实例。 默认情况下,标签在其显示区域中垂直居中。
图:JSlider
组件
JToggleButton
JToggleButton
是具有两种状态的按钮。 已按下但未按下。 通过单击可以在这两种状态之间切换。 在某些情况下此功能非常合适。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program uses toggle buttons to
change the background color of
a panel
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Color
from java.awt import Dimension
from javax.swing import BorderFactory
from javax.swing import Box
from javax.swing import BoxLayout
from javax.swing import JFrame
from javax.swing import JPanel
from javax.swing import JToggleButton
from javax.swing.border import LineBorder
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setPreferredSize(Dimension(280, 200))
bottom = JPanel()
bottom.setLayout(BoxLayout(bottom, BoxLayout.X_AXIS))
bottom.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20))
leftPanel = JPanel()
leftPanel.setLayout(BoxLayout(leftPanel, BoxLayout.Y_AXIS))
redButton = JToggleButton("red", actionPerformed=self.onToggle)
greenButton = JToggleButton("green", actionPerformed=self.onToggle)
blueButton = JToggleButton("blue", actionPerformed=self.onToggle)
blueButton.setMaximumSize(greenButton.getMaximumSize())
redButton.setMaximumSize(greenButton.getMaximumSize())
leftPanel.add(redButton)
leftPanel.add(Box.createRigidArea(Dimension(25, 7)))
leftPanel.add(greenButton)
leftPanel.add(Box.createRigidArea(Dimension(25, 7)))
leftPanel.add(blueButton)
bottom.add(leftPanel)
bottom.add(Box.createRigidArea(Dimension(20, 0)))
self.display = JPanel()
self.display.setPreferredSize(Dimension(110, 110))
self.display.setBorder(LineBorder.createGrayLineBorder())
self.display.setBackground(Color.black)
bottom.add(self.display)
self.add(bottom)
self.pack()
self.setTitle("JToggleButton")
self.setResizable(False)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onToggle(self, e):
color = self.display.getBackground()
red = color.getRed()
green = color.getGreen()
blue = color.getBlue()
if e.getActionCommand() == "red":
if red == 0:
red = 255
else:
red = 0
if e.getActionCommand() == "green":
if green == 0:
green = 255
else:
green = 0
if e.getActionCommand() == "blue":
if blue == 0:
blue = 255
else:
blue = 0
setCol = Color(red, green, blue)
self.display.setBackground(setCol)
if __name__ == '__main__':
Example()
在代码示例中,我们使用三个切换按钮来更改矩形组件的颜色。
redButton = JToggleButton("red", actionPerformed=self.onToggle)
我们创建一个JToggleButton
组件。 当我们单击该按钮时,将启动onToggle()
方法。
blueButton.setMaximumSize(greenButton.getMaximumSize())
redButton.setMaximumSize(greenButton.getMaximumSize())
我们使三个按钮的大小相等。
color = self.display.getBackground()
red = color.getRed()
green = color.getGreen()
blue = color.getBlue()
我们确定显示背景颜色的当前红色,绿色,蓝色部分。
if e.getActionCommand() == "red":
if red == 0:
red = 255
else:
red = 0
我们确定切换了哪个按钮,并相应地更新 RGB 值的颜色部分。
setCol = Color(red, green, blue)
self.display.setBackground(setCol)
在此创建新的颜色,并将显示面板更新为新的颜色。
图:JToggleButton
组件
JList
JList
是显示对象列表的组件。 它允许用户选择一项或多项。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program shows all system fonts
in a JList component
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BorderLayout
from java.awt import Dimension
from java.awt import Font
from java.awt import GraphicsEnvironment
from javax.swing import JFrame
from javax.swing import BorderFactory
from javax.swing import JScrollPane
from javax.swing import JPanel
from javax.swing import JLabel
from javax.swing import JList
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
panel = JPanel()
panel.setLayout(BorderLayout())
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20))
ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
fonts = ge.getAvailableFontFamilyNames()
list = JList(fonts, valueChanged=self.onChanged)
pane = JScrollPane()
pane.getViewport().add(list)
pane.setPreferredSize(Dimension(250, 200))
panel.add(pane)
self.label = JLabel("Aguirre, der Zorn Gottes")
self.label.setFont(Font("Serif", Font.PLAIN, 12))
self.add(self.label, BorderLayout.SOUTH)
self.add(panel)
self.pack()
self.setTitle("JList")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onChanged(self, e):
sender = e.getSource()
if not e.getValueIsAdjusting():
name = sender.getSelectedValue()
font = Font(name, Font.PLAIN, 13)
self.label.setFont(font)
if __name__ == '__main__':
Example()
在我们的示例中,我们将显示JList
和JLabel
组件。 列表组件包含我们系统上所有可用字体系列名称的列表。 如果我们从列表中选择一项,则标签将以我们选择的字体显示。
ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
fonts = ge.getAvailableFontFamilyNames()
在这里,我们获得系统上所有可能的字体系列名称。
list = JList(fonts, valueChanged=self.onChanged)
我们创建JList
组件的实例。 如果我们从列表中选择一个选项,则会调用onChanged()
方法。
if not e.getValueIsAdjusting():
列表选择中的事件被分组。 我们收到选择和取消选择事件。 为了仅过滤选择事件,我们使用getValueIsAdjusting()
方法。
name = sender.getSelectedValue()
font = Font(name, Font.PLAIN, 13)
self.label.setFont(font)
我们得到所选项目并为标签设置新字体。
pane = JScrollPane()
pane.getViewport().add(list)
默认情况下,JList
组件不可滚动。 我们将列表放入JScrollPane
以使其可滚动。
图:JList
组件
在 Jython Swing 教程的这一部分中,我们介绍了几个 Swing 组件。
Jython Swing 中的菜单和工具栏
在 Jython Swing 编程教程的这一部分中,我们将使用菜单和工具栏。
菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。 菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。
简单菜单
第一个示例将显示一个简单的菜单。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program creates a simple
menu.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt.event import KeyEvent
from java.lang import System
from javax.swing import ImageIcon
from javax.swing import JFrame
from javax.swing import JMenu
from javax.swing import JMenuBar
from javax.swing import JMenuItem
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
menubar = JMenuBar()
icon = ImageIcon("exit.png")
file = JMenu("File")
file.setMnemonic(KeyEvent.VK_F)
fileExit = JMenuItem("Exit", icon,
actionPerformed=self.onSelect)
fileExit.setMnemonic(KeyEvent.VK_C)
fileExit.setToolTipText("Exit application")
file.add(fileExit)
menubar.add(file)
self.setJMenuBar(menubar)
self.setTitle("Simple menu")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(250, 200)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onSelect(self, e):
System.exit(0)
if __name__ == '__main__':
Example()
我们的示例将显示一个菜单项。 通过选择退出菜单项,我们关闭应用。
menubar = JMenuBar()
在这里,我们创建一个菜单栏。
icon = ImageIcon("exit.png")
我们将在菜单项中显示一个图标。
file = JMenu("File")
file.setMnemonic(KeyEvent.VK_F)
我们创建一个菜单对象。 菜单是一个包含JMenuItems
的弹出窗口。 菜单位于菜单栏上。 也可以通过键盘访问菜单。 要将菜单绑定到特定键,我们使用setMnemonic()
方法。 在我们的情况下,可以使用ALT + F
快捷方式打开菜单。
fileExit = JMenuItem("Exit", icon,
actionPerformed=self.onSelect)
fileExit.setMnemonic(KeyEvent.VK_C)
fileExit.setToolTipText("Exit application")
在这里,我们创建一个JMenuItem
。 菜单项是显示在所选菜单的弹出窗口中的对象。 我们还为菜单项和工具提示提供了快捷方式。
file.add(fileExit)
菜单项被添加到菜单中。
menubar.add(file)
菜单添加到菜单栏。
图:简单菜单
子菜单
子菜单是插入另一个菜单对象的菜单。 下一个示例对此进行了演示。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program creates a simple
submenu.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.lang import System
from java.awt.event import KeyEvent
from java.awt.event import ActionEvent
from javax.swing import JFrame
from javax.swing import JMenuBar
from javax.swing import JMenuItem
from javax.swing import JMenu
from javax.swing import ImageIcon
from javax.swing import KeyStroke
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
menubar = JMenuBar()
iconNew = ImageIcon("new.png")
iconOpen = ImageIcon("open.png")
iconSave = ImageIcon("save.png")
iconExit = ImageIcon("exit.png")
file = JMenu("File")
file.setMnemonic(KeyEvent.VK_F)
imp = JMenu("Import")
imp.setMnemonic(KeyEvent.VK_M)
newsf = JMenuItem("Import newsfeed list...")
bookm = JMenuItem("Import bookmarks...")
mail = JMenuItem("Import mail...")
imp.add(newsf)
imp.add(bookm)
imp.add(mail)
fileNew = JMenuItem("New", iconNew)
fileNew.setMnemonic(KeyEvent.VK_N)
fileOpen = JMenuItem("Open", iconOpen)
fileNew.setMnemonic(KeyEvent.VK_O)
fileSave = JMenuItem("Save", iconSave)
fileSave.setMnemonic(KeyEvent.VK_S)
fileExit = JMenuItem("Exit", iconExit,
actionPerformed=self.onSelect)
fileExit.setMnemonic(KeyEvent.VK_C)
fileExit.setToolTipText("Exit application")
fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W,
ActionEvent.CTRL_MASK))
file.add(fileNew)
file.add(fileOpen)
file.add(fileSave)
file.addSeparator()
file.add(imp)
file.addSeparator()
file.add(fileExit)
menubar.add(file)
self.setJMenuBar(menubar)
self.setTitle("Submenu")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(320, 220)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onSelect(self, e):
System.exit(0)
if __name__ == '__main__':
Example()
在示例中,文件菜单的子菜单中有三个选项。
imp = JMenu("Import")
...
file.add(imp)
子菜单与其他任何普通菜单一样。 它是用相同的方式创建的。 我们只需将菜单添加到现有菜单即可。
fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W,
ActionEvent.CTRL_MASK))
加速器是启动菜单项的快捷键。 在我们的情况下,通过按 Ctrl + W
关闭我们的应用。
file.addSeparator()
分隔符是一条水平线,用于在视觉上分隔菜单项。 这样,我们可以将项目分组到一些合理的位置。
图:子菜单
弹出菜单
在下一个示例中,我们创建一个弹出菜单。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program creates a popup menu.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt.event import MouseListener
from java.lang import System
from javax.swing import JFrame
from javax.swing import JMenuItem
from javax.swing import JPopupMenu
class Example(JFrame, MouseListener):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.menu = JPopupMenu()
menuItemBeep = JMenuItem("Beep", actionPerformed=self.onBeep)
self.menu.add(menuItemBeep)
menuItemClose = JMenuItem("Exit", actionPerformed=self.onExit)
self.menu.add(menuItemClose);
self.addMouseListener(self)
self.setTitle("Popup menu")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(250, 200)
self.setLocationRelativeTo(None)
self.setVisible(True)
def mouseReleased(self, e):
if e.getButton() == e.BUTTON3:
self.menu.show(e.getComponent(), e.getX(), e.getY())
def onExit(self, e):
System.exit(0)
def onBeep(self, e):
toolkit = self.getToolkit()
toolkit.beep()
if __name__ == '__main__':
Example()
在我们的示例中,我们创建一个带有两个菜单项的弹出菜单。
self.menu = JPopupMenu()
menuItemBeep = JMenuItem("Beep", actionPerformed=self.onBeep)
我们创建一个弹出菜单和一个菜单项。
def mouseReleased(self, e):
if e.getButton() == e.BUTTON3:
self.menu.show(e.getComponent(), e.getX(), e.getY())
我们在鼠标单击的 x,y 坐标处显示弹出菜单窗口。
图:弹出菜单
工具栏
菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 在 Swing 中,JToolBar
类在应用中创建一个工具栏。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, we create a simple
toolbar.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BorderLayout
from java.lang import System
from javax.swing import ImageIcon
from javax.swing import JFrame
from javax.swing import JMenu
from javax.swing import JMenuBar
from javax.swing import JToolBar
from javax.swing import JButton
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
menubar = JMenuBar()
file = JMenu("File")
menubar.add(file)
self.setJMenuBar(menubar)
toolbar = JToolBar()
icon = ImageIcon("exit.png")
exitButton = JButton(icon, actionPerformed=self.onClick)
toolbar.add(exitButton)
self.add(toolbar, BorderLayout.NORTH)
self.setTitle("Toolbar")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(350, 250)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onClick(self, e):
System.exit(0)
if __name__ == '__main__':
Example()
该示例创建一个带有一个退出按钮的工具栏。
toolbar = JToolBar()
工具栏已创建。
exitButton = JButton(icon, actionPerformed=self.onClick)
toolbar.add(exitButton)
我们创建一个按钮并将其添加到工具栏。
self.add(toolbar, BorderLayout.NORTH)
工具栏位于BorderLayout
管理器的北部。
图:工具栏
在 Jython Swing 教程的这一部分中,我们提到了菜单和工具栏。
Jython Swing 中的对话框
在 Jython Swing 编程教程的这一部分中,我们将使用对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
MessageDialog
消息框是方便的对话框,可向应用的用户提供消息。 该消息由文本和图像数据组成。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, we show various
message boxes.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import GridLayout
from javax.swing import JButton
from javax.swing import JFrame
from javax.swing import JOptionPane
from javax.swing import JPanel
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.panel = JPanel()
self.panel.setLayout(GridLayout(2, 2))
error = JButton("Error", actionPerformed=self.onError)
warning = JButton("Warning", actionPerformed=self.onWarning)
question = JButton("Question", actionPerformed=self.onQuestion)
inform = JButton("Information", actionPerformed=self.onInform)
self.panel.add(error)
self.panel.add(warning)
self.panel.add(question)
self.panel.add(inform)
self.add(self.panel)
self.setTitle("Message boxes")
self.setSize(300, 200)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onError(self, e):
JOptionPane.showMessageDialog(self.panel, "Could not open file",
"Error", JOptionPane.ERROR_MESSAGE)
def onWarning(self, e):
JOptionPane.showMessageDialog(self.panel, "A deprecated call",
"Warning", JOptionPane.WARNING_MESSAGE)
def onQuestion(self, e):
JOptionPane.showMessageDialog(self.panel, "Are you sure to quit?",
"Question", JOptionPane.QUESTION_MESSAGE)
def onInform(self, e):
JOptionPane.showMessageDialog(self.panel, "Download completed",
"Information", JOptionPane.INFORMATION_MESSAGE)
if __name__ == '__main__':
Example()
我们使用GridLayout
管理器来设置四个按钮的网格。 每个按钮显示一个不同的消息框。
def onError(self, e):
JOptionPane.showMessageDialog(self.panel, "Could not open file",
"Error", JOptionPane.ERROR_MESSAGE)
如果按下错误按钮,则会显示错误对话框。 我们使用showMessageDialog()
方法在屏幕上显示对话框。 此方法的第一个参数是显示对话框的框架。 第二个参数是要显示的消息。 第三个参数是对话框的标题。 最后一个参数是消息类型。 默认图标由消息类型决定。 在本例中,错误对话框的消息类型为ERROR_MESSAGE
。
图:错误消息 dialog
JColorChooser
JColorChooser
是用于选择颜色的标准对话框。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, we use the
JColorChooser to change the color
of a panel.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BorderLayout
from java.awt import Color
from javax.swing import BorderFactory
from javax.swing import JColorChooser
from javax.swing import JButton
from javax.swing import JToolBar
from javax.swing import JPanel
from javax.swing import JFrame
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.panel = JPanel()
self.panel.setLayout(BorderLayout())
toolbar = JToolBar()
openb = JButton("Choose color", actionPerformed=self.onClick)
toolbar.add(openb)
self.display = JPanel()
self.display.setBackground(Color.WHITE)
self.panel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50))
self.panel.add(self.display)
self.add(self.panel)
self.add(toolbar, BorderLayout.NORTH)
self.setTitle("Color chooser")
self.setSize(300, 250)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onClick(self, e):
clr = JColorChooser()
color = clr.showDialog(self.panel, "Choose Color", Color.white)
self.display.setBackground(color)
if __name__ == '__main__':
Example()
在示例中,窗口中央有一个白色面板。 我们将通过从颜色选择器对话框中选择一种颜色来更改面板的背景色。
clr = JColorChooser()
color = clr.showDialog(self.panel, "Choose Color", Color.white)
self.display.setBackground(color)
此代码显示一个颜色选择器对话框。 showDialog()
方法返回所选的颜色值。 我们将显示面板的背景更改为新选择的颜色。
图:ColorDialog
JFileChooser
JFileChooser
对话框允许用户从文件系统中选择一个文件。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, we use the
JFileChooser to select a file from
a filesystem.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BorderLayout
from javax.swing import BorderFactory
from javax.swing import JFileChooser
from javax.swing import JTextArea
from javax.swing import JScrollPane
from javax.swing import JButton
from javax.swing import JToolBar
from javax.swing import JPanel
from javax.swing import JFrame
from javax.swing.filechooser import FileNameExtensionFilter
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.panel = JPanel()
self.panel.setLayout(BorderLayout())
toolbar = JToolBar()
openb = JButton("Choose file", actionPerformed=self.onClick)
toolbar.add(openb)
self.area = JTextArea()
self.area.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
pane = JScrollPane()
pane.getViewport().add(self.area)
self.panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
self.panel.add(pane)
self.add(self.panel)
self.add(toolbar, BorderLayout.NORTH)
self.setTitle("File chooser")
self.setSize(300, 250)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
def onClick(self, e):
chooseFile = JFileChooser()
filter = FileNameExtensionFilter("c files", ["c"])
chooseFile.addChoosableFileFilter(filter)
ret = chooseFile.showDialog(self.panel, "Choose file")
if ret == JFileChooser.APPROVE_OPTION:
file = chooseFile.getSelectedFile()
text = self.readFile(file)
self.area.setText(text)
def readFile(self, file):
filename = file.getCanonicalPath()
f = open(filename, "r")
text = f.read()
return text
if __name__ == '__main__':
Example()
在我们的代码示例中,我们使用JFileChooser
对话框选择一个 C 文件并将其内容显示在JTextArea
中。
self.area = JTextArea()
这是JTextArea
,我们将在其中显示所选文件的内容。
chooseFile = JFileChooser()
filter = FileNameExtensionFilter("c files", ["c"])
chooseFile.addChoosableFileFilter(filter)
我们创建JFileChooser
对话框的实例。 我们创建一个仅显示 C 文件的过滤器。
ret = chooseFile.showDialog(self.panel, "Choose file")
对话框显示在屏幕上。 我们得到了返回值。
if ret == JFileChooser.APPROVE_OPTION:
file = chooseFile.getSelectedFile()
text = self.readFile(file)
self.area.setText(text)
如果用户选择了文件,我们将获得文件名。 阅读其内容并将文本设置为文本区域组件。
def readFile(self, file):
filename = file.getCanonicalPath()
f = open(filename, "r")
text = f.read()
return text
此代码从文件中读取文本。 getCanonicalPath()
返回绝对文件名。
图:JFileChooser
在 Jython Swing 教程的这一部分中,我们使用了对话框窗口。
Jython Swing 中的绘图
在 Jython Swing 编程教程的这一部分中,我们将进行绘图。
我们使用绘图来创建图表,自定义组件或创建游戏。 要进行绘图,我们使用 Swing 工具包提供的绘图 API。 绘图是在paintComponent()
方法中完成的。 在绘图过程中,我们使用Graphics2D
对象。 它是一个图形上下文,允许应用绘制到组件上。 它是渲染二维形状,文本和图像的基础类。
色彩
颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 我们使用Color
类在 Swing 中处理颜色。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program draws ten
rectangles filled with different
colors.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Color
from javax.swing import JFrame
from javax.swing import JPanel
class Canvas(JPanel):
def __init__(self):
super(Canvas, self).__init__()
def paintComponent(self, g):
self.drawColorRectangles(g)
def drawColorRectangles(self, g):
g.setColor(Color(125, 167, 116))
g.fillRect(10, 15, 90, 60)
g.setColor(Color(42, 179, 231))
g.fillRect(130, 15, 90, 60)
g.setColor(Color(70, 67, 123))
g.fillRect(250, 15, 90, 60)
g.setColor(Color(130, 100, 84))
g.fillRect(10, 105, 90, 60)
g.setColor(Color(252, 211, 61))
g.fillRect(130, 105, 90, 60)
g.setColor(Color(241, 98, 69))
g.fillRect(250, 105, 90, 60)
g.setColor(Color(217, 146, 54))
g.fillRect(10, 195, 90, 60)
g.setColor(Color(63, 121, 186))
g.fillRect(130, 195, 90, 60)
g.setColor(Color(31, 21, 1))
g.fillRect(250, 195, 90, 60)
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.canvas = Canvas()
self.getContentPane().add(self.canvas)
self.setTitle("Colors")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(360, 300)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在代码示例中,我们绘制了九个矩形,并用不同的颜色值填充它们。
def paintComponent(self, g):
在大多数情况下,自定义绘图是在paintComponent()
中完成的。 g
参数是图形上下文。 我们称此对象为绘图操作。
g.setColor(Color(125, 167, 116))
我们将上下文的当前颜色设置为指定的颜色。 使用此图形上下文的所有后续图形操作均使用此指定的颜色。
g.fillRect(10, 15, 90, 60)
我们使用上面指定的颜色值填充位于x = 10
,y = 15
且宽度= 90
和高度= 60
的矩形。
图:颜色
形状
Swing 绘图 API 可以绘制各种形状。 以下编程代码示例将显示其中的一些。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program draws basic shapes
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Color
from java.awt import RenderingHints
from java.awt.geom import Ellipse2D
from javax.swing import JFrame
from javax.swing import JPanel
class Canvas(JPanel):
def __init__(self):
super(Canvas, self).__init__()
def paintComponent(self, g):
self.drawShapes(g)
def drawShapes(self, g):
g.setColor(Color(150, 150, 150))
rh = RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON)
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY)
g.setRenderingHints(rh)
g.fillRect(20, 20, 50, 50)
g.fillRect(120, 20, 90, 60)
g.fillRoundRect(250, 20, 70, 60, 25, 25)
g.fill(Ellipse2D.Double(10, 100, 80, 100))
g.fillArc(120, 130, 110, 100, 5, 150)
g.fillOval(270, 130, 50, 50)
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.canvas = Canvas()
self.getContentPane().add(self.canvas)
self.setTitle("Shapes")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(350, 250)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在此代码示例中,我们在窗口上绘制了六个不同的形状。 正方形,矩形,圆角矩形,椭圆形,弧形和椭圆形。 我们不会绘制形状的轮廓,但是会用灰色填充形状的内部空间。
rh = RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON)
借助渲染提示,我们可以控制绘图的质量。 在上面的代码中,我们实现了抗锯齿。 使用抗锯齿,形状更平滑。
g.setColor(Color(150, 150, 150))
我们将以某种灰色绘图。
g.fillRect(20, 20, 50, 50)
g.fillRect(120, 20, 90, 60)
g.fillRoundRect(250, 20, 70, 60, 25, 25)
在这里,我们绘制一个矩形,一个正方形和一个圆角矩形。 这些方法中的前四个参数是 x,y 坐标以及宽度和高度。 fillRoundRect()
的最后两个参数是四个角处圆弧的水平和垂直直径。
g.fill(Ellipse2D.Double(10, 100, 80, 100))
g.fillArc(120, 130, 110, 100, 5, 150)
g.fillOval(270, 130, 50, 50)
这三条线绘制一个椭圆,一个弧形和一个椭圆形。
图:形状
透明矩形
透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)
"""
ZetCode Jython Swing tutorial
This program draws ten
rectangles with different
levels of transparency.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import AlphaComposite
from java.awt import Color
from javax.swing import JFrame
from javax.swing import JPanel
class Canvas(JPanel):
def __init__(self):
super(Canvas, self).__init__()
def paintComponent(self, g):
self.drawRectangles(g)
def drawRectangles(self, g):
g.setColor(Color.BLUE)
for i in range(1, 11):
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
i * 0.1))
g.fillRect(50 * i, 20, 40, 40)
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.canvas = Canvas()
self.getContentPane().add(self.canvas)
self.setTitle("Transparent rectangles")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(590, 120)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在示例中,我们将绘制十个具有不同透明度级别的矩形。
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, i * 0.1))
AlphaComposite
类实现基本的 alpha 合成规则。
图:透明矩形
甜甜圈形状
在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。 仿射变换由零个或多个线性变换(旋转,缩放或剪切)和平移(移位)组成。 AffineTransform
是 Swing 中用于执行仿射变换的类。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
In this program, we create a donut
shape.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import BasicStroke
from java.awt import Color
from java.awt import RenderingHints
from java.awt.geom import AffineTransform
from java.awt.geom import Ellipse2D
from javax.swing import JFrame
from javax.swing import JPanel
import math
class Canvas(JPanel):
def __init__(self):
super(Canvas, self).__init__()
def paintComponent(self, g):
self.drawDonutShape(g)
def drawDonutShape(self, g):
rh = RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON)
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY)
g.setRenderingHints(rh)
size = self.getSize()
w = size.getWidth()
h = size.getHeight()
e = Ellipse2D.Double(0, 0, 80, 130)
g.setStroke(BasicStroke(1))
g.setColor(Color.gray)
for deg in range(0, 360, 5):
at = AffineTransform.getTranslateInstance(w / 2, h / 2)
at.rotate(math.radians(deg))
g.draw(at.createTransformedShape(e))
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.canvas = Canvas()
self.getContentPane().add(self.canvas)
self.setTitle("Donut")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(350, 320)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。 甜甜圈在窗口中居中。
size = self.getSize()
w = size.getWidth()
h = size.getHeight()
在这里,我们确定窗口的宽度和高度。 我们需要这些值来使甜甜圈形状居中。
e = Ellipse2D.Double(0, 0, 80, 130)
我们创建一个椭圆形。 我们将旋转此椭圆以创建甜甜圈形状。
g.setStroke(BasicStroke(1))
g.setColor(Color.gray)
我们为形状的轮廓设置笔触和颜色。
for deg in range(0, 360, 5):
我们绘制一个椭圆对象 72 次。 每次,我们再将椭圆旋转 5 度。 这将创建我们的甜甜圈形状。
at = AffineTransform.getTranslateInstance(w / 2, h / 2)
at.rotate(math.radians(deg))
g.draw(at.createTransformedShape(e))
在AffineTransform
类的帮助下,我们将图形转换到窗口的中心。 然后我们进行旋转。 createTransformedShape()
方法会将这些仿射变换应用于椭圆。 然后使用draw()
方法绘制变换后的椭圆。
绘制文字
在最后一个示例中,我们将在窗口上绘制文本。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This program draws lyrics of a
song on the window.
author: Jan Bodnar
website: www.zetcode.com
last modified: November 2010
"""
from java.awt import Font
from java.awt import RenderingHints
from javax.swing import JFrame
from javax.swing import JPanel
class Canvas(JPanel):
def __init__(self):
super(Canvas, self).__init__()
def paintComponent(self, g):
self.drawLyrics(g)
def drawLyrics(self, g):
rh = RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
g.setRenderingHints(rh)
g.setFont(Font("Purisa", Font.PLAIN, 13))
g.drawString("Most relationships seem so transitory", 20, 30)
g.drawString("They're all good but not the permanent one", 20, 60)
g.drawString("Who doesn't long for someone to hold", 20, 90)
g.drawString("Who knows how to love you without being told", 20, 120)
g.drawString("Somebody tell me why I'm on my own", 20, 150)
g.drawString("If there's a soulmate for everyone", 20, 180)
class Example(JFrame):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.canvas = Canvas()
self.getContentPane().add(self.canvas)
self.setTitle("Soulmate")
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setSize(400, 250)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Example()
我们在窗口上画一首歌的歌词。
rh = RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
g.setRenderingHints(rh)
我们在画上应用文字抗锯齿。
g.setFont(Font("Purisa", Font.PLAIN, 13))
我们指定字体名称,样式和磅值,并在其中绘制歌词。
g.drawString("Most relationships seem so transitory", 20, 30)
drawString()
方法绘制文本。
图:绘制文本
在 Jython Swing 编程教程的这一部分中,我们做了一些绘图。
Jython Swing 中的贪食蛇
在 Jython Swing 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"
消息。
import random
from java.awt import Color
from java.awt import Font
from java.awt import Toolkit
from java.awt.event import ActionListener
from java.awt.event import KeyEvent
from java.awt.event import KeyListener
from javax.swing import ImageIcon
from javax.swing import JPanel
from javax.swing import Timer
WIDTH = 300
HEIGHT = 300
DOT_SIZE = 10
ALL_DOTS = WIDTH * HEIGHT / (DOT_SIZE * DOT_SIZE)
RAND_POS = 29
DELAY = 140
x = [0] * ALL_DOTS
y = [0] * ALL_DOTS
class Board(JPanel, KeyListener, ActionListener):
def __init__(self):
super(Board, self).__init__()
self.initUI()
def initUI(self):
self.setBackground(Color.black)
iid = ImageIcon("dot.png")
self.ball = iid.getImage()
iia = ImageIcon("apple.png")
self.apple = iia.getImage()
iih = ImageIcon("head.png")
self.head = iih.getImage()
self.setFocusable(True)
self.addKeyListener(self)
self.initGame()
def initGame(self):
self.left = False
self.right = True
self.up = False
self.down = False
self.inGame = True
self.dots = 3
for i in range(self.dots):
x[i] = 50 - i * 10
y[i] = 50
self.locateApple()
self.timer = Timer(DELAY, self)
self.timer.start()
def paint(self, g):
# due to bug, cannot call super()
JPanel.paint(self, g)
if self.inGame:
self.drawObjects(g)
else:
self.gameOver(g)
def drawObjects(self, g):
g.drawImage(self.apple, self.apple_x, self.apple_y, self)
for z in range(self.dots):
if (z == 0):
g.drawImage(self.head, x[z], y[z], self)
else:
g.drawImage(self.ball, x[z], y[z], self)
Toolkit.getDefaultToolkit().sync()
g.dispose()
def gameOver(self, g):
msg = "Game Over"
small = Font("Helvetica", Font.BOLD, 14)
metr = self.getFontMetrics(small)
g.setColor(Color.white)
g.setFont(small)
g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2,
HEIGHT / 2)
def checkApple(self):
if x[0] == self.apple_x and y[0] == self.apple_y:
self.dots = self.dots + 1
self.locateApple()
def move(self):
z = self.dots
while z > 0:
x[z] = x[(z - 1)]
y[z] = y[(z - 1)]
z = z - 1
if self.left:
x[0] -= DOT_SIZE
if self.right:
x[0] += DOT_SIZE
if self.up:
y[0] -= DOT_SIZE
if self.down:
y[0] += DOT_SIZE
def checkCollision(self):
z = self.dots
while z > 0:
if z > 4 and x[0] == x[z] and y[0] == y[z]:
self.inGame = False
z = z - 1
if y[0] > HEIGHT - DOT_SIZE:
self.inGame = False
if y[0] < 0:
self.inGame = False
if x[0] > WIDTH - DOT_SIZE:
self.inGame = False
if x[0] < 0:
self.inGame = False
def locateApple(self):
r = random.randint(0, RAND_POS)
self.apple_x = r * DOT_SIZE
r = random.randint(0, RAND_POS)
self.apple_y = r * DOT_SIZE
# public void actionPerformed(ActionEvent e) {
def actionPerformed(self, e):
if self.inGame:
self.checkApple()
self.checkCollision()
self.move()
else:
self.timer.stop()
self.repaint()
def keyPressed(self, e):
key = e.getKeyCode()
if key == KeyEvent.VK_LEFT and not self.right:
self.left = True
self.up = False
self.down = False
if key == KeyEvent.VK_RIGHT and not self.left:
self.right = True
self.up = False
self.down = False
if key == KeyEvent.VK_UP and not self.down:
self.up = True
self.right = False
self.left = False
if key == KeyEvent.VK_DOWN and not self.up:
self.down = True
self.right = False
self.left = False
首先,我们将定义一些在游戏中使用的常量。
WIDTH
和HEIGHT
常数确定电路板的大小。 DOT_SIZE
是苹果的大小和蛇的点。 ALL_DOTS
常数定义了板上可能的最大点数。 RAND_POS
常数用于计算苹果的随机位置。 DELAY
常数确定游戏的速度。
x = [0] * ALL_DOTS
y = [0] * ALL_DOTS
这两个数组存储蛇的所有可能关节的 x,y 坐标。
initGame()
方法初始化变量,加载图像并启动超时功能。
def paint(self, g):
JPanel.paint(self, g)
if self.inGame:
self.drawObjects(g)
else:
self.gameOver(g)
在paint()
方法内部,我们检查inGame
变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"
文本。
def drawObjects(self, g):
g.drawImage(self.apple, self.apple_x, self.apple_y, self)
for z in range(self.dots):
if (z == 0):
g.drawImage(self.head, x[z], y[z], self)
else:
g.drawImage(self.ball, x[z], y[z], self)
Toolkit.getDefaultToolkit().sync()
g.dispose()
drawObjects()
方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。 Toolkit.getDefaultToolkit().sync()
方法可确保显示为最新。 这对于动画很有用。
def checkApple(self):
if x[0] == self.apple_x and y[0] == self.apple_y:
self.dots = self.dots + 1
self.locateApple()
checkApple()
方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locateApple()
方法,该方法将随机放置一个新的Apple
对象。
在move()
方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
while z > 0:
x[z] = x[(z - 1)]
y[z] = y[(z - 1)]
z = z - 1
该代码将关节向上移动。
if self.left:
x[0] -= DOT_SIZE
将头向左移动。
在checkCollision()
方法中,我们确定蛇是否击中了自己或撞墙之一。
while z > 0:
if z > 4 and x[0] == x[z] and y[0] == y[z]:
self.inGame = False
z = z - 1
如果蛇用头撞到关节之一,我们就结束游戏。
if y[0] > HEIGHT - DOT_SIZE:
self.inGame = False
如果蛇击中了棋盘的底部,我们就结束了游戏。
locateApple()
方法在板上随机放置一个苹果。
r = random.randint(0, RAND_POS)
我们得到一个从 0 到RAND_POS-1
的随机数。
self.apple_x = r * DOT_SIZE
...
self.apple_y = r * DOT_SIZE
这些行设置了apple
对象的 x,y 坐标。
def actionPerformed(self, e):
if self.inGame:
self.checkApple()
self.checkCollision()
self.move()
else:
self.timer.stop()
self.repaint()
每隔DELAY
ms,将调用actionPerformed()
方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 否则,我们将停止计时器。
在Board
类的keyPressed()
方法中,我们确定按下的键。
if key == KeyEvent.VK_LEFT and not self.right:
self.left = True
self.up = False
self.down = False
如果单击左光标键,则将left
变量设置为true
。 在move()
方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
#!/usr/local/bin/jython
# -*- coding: utf-8 -*-
"""
ZetCode Jython Swing tutorial
This is a simple Nibbles game
clone.
author: Jan Bodnar
website: zetcode.com
last edited: December 2010
"""
from java.awt import Dimension
from javax.swing import JFrame
from Board import Board
class Nibbles(JFrame):
def __init__(self):
super(Nibbles, self).__init__()
self.initUI()
def initUI(self):
self.board = Board()
self.board.setPreferredSize(Dimension(300, 300))
self.add(self.board)
self.setTitle("Nibbles")
self.pack()
self.setResizable(False)
self.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
self.setLocationRelativeTo(None)
self.setVisible(True)
if __name__ == '__main__':
Nibbles()
在这个类中,我们设置了贪食蛇游戏。
图:贪食蛇
这是使用 Swing 库和 Jython 编程语言编写的贪食蛇电脑游戏。
JRuby Swing 教程
这是 JRuby Swing 教程。 在本教程中,您将学习 JRuby & Swing 中 GUI 编程的基础。 本教程适合初学者和中级程序员。
目录
Swing
Swing 库是 Java 编程语言的官方 Java GUI 工具箱。 它用于使用 Java 创建图形用户界面。 Swing 是一个高级 GUI 工具箱。 它具有丰富的组件集。 从基本的按钮,标签,滚动条到高级的组件(例如树和表格)。 Swing 本身是用 Java 编写的。 Swing 也可用于其他语言。 例如 JRuby,Jython,Groovy 或 Scala。
相关教程
关于 ZetCode,有两个类似的教程:原始的 Java Swing 教程和 Jython Swing 教程。
Qt5 工具包简介
在 Qt5 教程的这一部分中,我们将介绍 Qt5 库。 我们将安装 Qt5 库并创建我们的第一个小型 Qt5 应用。
Qt 最初是由挪威软件公司 Trolltech 开发的。 2008 年,该公司被诺基亚收购。 2012 年 8 月,一家芬兰开发公司 Digia 从诺基亚那里收购了 Qt 软件技术。 同时,创建了一个 Qt 项目,其中开源 Qt 的开发继续进行。 开源 Qt 工具包的网站可以在 qt.io 中找到。 目前,由 Digia 的子公司 Qt 公司和开放源代码治理下的 Qt 项目(包括个人开发者和公司)共同开发 Qt。
Qt
Qt 是一个跨平台的应用开发框架。 使用 Qt 开发的一些知名应用是 KDE,Opera,Google Earth,Skype,VLC,Maya 或 Mathematica。 Qt 于 1995 年 5 月首次公开发布。它具有双重许可。 它可以用于创建开源应用以及商业应用。 Qt 工具箱是一个非常强大的工具箱。 它在开源社区中已经建立。 全世界有成千上万的开源开发者在使用 Qt。
下载并解压缩
我们转到 download.qt.io/official_releases/qt/ 页面。 (由于下载链接过去经常更改,因此您可能需要用 Google 搜索当前链接。)我们选择最新的 Qt 5.x 来源。 在创建本教程时,最新的数据是 Qt 5.5.1。 接下来,我们将从源代码安装 Qt。
$ ls qt-everywhere-opensource-src-5.5.1.tar.gz
qt-everywhere-opensource-src-5.5.1.tar.gz
从下载页面,我们下载 Qt5 源。 我们使用 TAR 文件。 (我们为自己省了一些麻烦。ZIP 文件具有 Windows 行尾。)
$ tar -xzvf qt-everywhere-opensource-src-5.5.1.tar.gz
该命令会将所有文件解压缩到目录qt-everywhere-opensource-src-5.5.1/
。
$ du -hs qt-everywhere-opensource-src-5.5.1
2.0G qt-everywhere-opensource-src-5.5.1
现在目录的大小为 2G。
$ cd qt-everywhere-opensource-src-5.5.1/
我们转到创建的目录。 在README
文件中,有安装说明。 安装简单明了,但需要花费大量时间。
从源安装
在开始构建 Qt5 之前,我们可能需要安装一些其他库。 例如,如果要从 Qt 连接到 MySQL,则需要在系统上安装libmysqld-dev
。
我们以经典方式安装库。 在 Unix 系统上,软件的安装分为三个步骤。
- 配置
- 构建
- 安装
$ ./configure -prefix /usr/local/qt5
Which edition of Qt do you want to use ?
Type 'c' if you want to use the Commercial Edition.
Type 'o' if you want to use the Open Source Edition.
首先,我们运行配置脚本。 该脚本将询问我们是否需要 Qt5 库的商业版或开源版。 该脚本将为我们的机器类型配置库。 默认情况下,Qt 将安装在/usr/local/Qt-5.5.1/
目录中。 这可以通过配置脚本的-prefix
参数进行更改。 我们将库安装到/usr/local/qt5/
目录中。 请注意,此处的安装字有两个含义。 这是整个过程,包括所有三个步骤。 这也意味着“将文件移动到特定目录”,这是最后第三步。
This is the Open Source Edition.
You are licensed to use this software under the terms of
the Lesser GNU General Public License (LGPL) versions 2.1.
You are also licensed to use this software under the terms of
the GNU General Public License (GPL) versions 3.
Type '3' to view the GNU General Public License version 3.
Type 'L' to view the Lesser GNU General Public License version 2.1.
Type 'yes' to accept this license offer.
Type 'no' to decline this license offer.
Do you accept the terms of either license? yes
确认许可协议。
Running configuration tests...
The test for linking against libxcb and support libraries failed!
You might need to install dependency packages, or pass -qt-xcb.
如果脚本失败并显示上述消息,则我们需要安装一些其他 xcb 库,或者使用-qt-xcb
选项再次运行该脚本。
$ ./configure -prefix /usr/local/qt5 -qt-xcb
使用-qt-xcb
选项,可以构建某些库,而不是针对系统库进行链接。
...
Qt modules and options:
Qt D-Bus ............... yes (loading dbus-1 at runtime)
Qt Concurrent .......... yes
Qt GUI ................. yes
Qt Widgets ............. yes
Large File ............. yes
QML debugging .......... yes
Use system proxies ..... no
...
SQL drivers:
DB2 .................. no
InterBase ............ no
MySQL ................ yes (plugin)
OCI .................. no
ODBC ................. no
PostgreSQL ........... yes (plugin)
SQLite 2 ............. no
SQLite ............... yes (plugin, using bundled copy)
TDS .................. no
tslib .................. no
udev ................... yes
xkbcommon-x11........... yes (bundled copy, XKB config root: /usr/share/X11/xkb)
xkbcommon-evdev......... yes
zlib ................... yes (system library)
Qt is now configured for building. Just run 'make'.
Once everything is built, you must run 'make install'.
Qt will be installed into /usr/local/qt5
Prior to reconfiguration, make sure you remove any leftovers from
the previous build.
这是配置脚本的部分输出。 输出告诉我们准备构建哪些组件。 例如,将创建用于 MySQL 和 PostgreSQL 的 SQL 驱动程序,而不用于 DB2 或 InterBase。
$ make
我们使用make
命令开始构建过程。 Qt 工具包的构建可能要花费几个小时。 这取决于处理器的能力。
最后一步是安装文件或将文件移动到目录中。
$ sudo make install
此命令完成安装过程。 该库现在安装在/usr/local/qt5/
目录中。
我们要做的最后一件事是将 Qt5 路径添加到PATH
系统变量。 bash 用户需要编辑.profile
文件或.bashrc
文件。
$ PATH=/usr/local/qt5/bin:$PATH
$ export PATH
我们已经将 Qt5 库的 bin 目录的路径添加到PATH
环境变量。 再次登录后,更改将处于活动状态。
从包安装
从包安装 Qt 更加容易。 Linux 包通常不包含最新的 Qt 版本。
$ sudo apt-get install qt5-default
上面的命令在基于 Debian 的 Linux 上安装 Qt5。
版本
我们的第一个程序打印 Qt5 库的版本。
version.cpp
#include <QtCore>
#include <iostream>
int main() {
std::cout << "Qt version: " << qVersion() << std::endl;
}
qVersion()
函数在运行时以字符串形式返回 Qt 的版本号。
$ g++ -o version version.cpp -I/usr/local/qt5/include/QtCore -I/usr/local/qt5/include -L/usr/local/qt5/lib -lQt5Core -fPIC
上面的命令将编译示例。 请注意,您的 Qt5 库可能安装在其他位置。
$ ./version
Qt version: 5.5.1
本教程中使用的 Qt5 库的版本是 5.5.1。
测试一个小的 GUI 示例
最后,我们编写一个小应用。 该应用包含一个普通窗口。
simple.cpp
#include <QApplication>
#include <QWidget>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
window.resize(250, 150);
window.setWindowTitle("Simple example");
window.show();
return app.exec();
}
要构建此示例,我们使用qmake
工具。
$ qmake -project
该命令创建一个扩展名为.pro
的项目文件。
simple.pro
######################################################################
# Automatically generated by qmake (3.0) Fri Oct 30 17:11:00 2015
######################################################################
TEMPLATE = app
TARGET = simple
INCLUDEPATH += .
# Input
SOURCES += simple.cpp
QT += widgets
默认情况下,项目中不包括 Qt Widgets 模块。 因此,我们将模块添加到文件的末尾。
$ qmake
$ make
我们使用上述命令构建程序。 qmake
创建一个Makefile
,make
命令生成该程序。
如果 Qt5 安装目录不是PATH
变量的一部分,我们可以提供qmake
工具的完整路径。
$ /usr/local/qt5/bin/qmake -project
$ /usr/local/qt5/bin/qmake
$ make
图:简单 example
本章是 Qt5 库的简介。
JRuby Swing 简介
在 JRuby Swing 教程的这一部分中,我们将介绍 Swing 工具包并使用 JRuby 编程语言创建第一个程序。
本教程的目的是让您开始使用 JRuby 语言的 Swing 工具包。 可以在此处下载本教程中使用的图像。 我使用了 Gnome 项目的 Tango 图标包中的一些图标。
关于
Swing 库是 Java 编程语言的官方 Java GUI 工具箱。 它用于使用 Java 创建图形用户界面。 Swing 是一个高级 GUI 工具箱。 它具有丰富的组件集。 从基本的按钮,标签,滚动条到高级的组件(例如树和表格)。 Swing 本身是用 Java 编写的。 Swing 也可用于其他语言。 例如,JRuby,Jython,Groovy 或 Scala。
JRuby 是 Ruby 编程语言的 Java 实现。 JRuby 可以导入任何 Java 类。
执行本教程中的示例有两种基本方法。 一种方法是安装 Ruby NetBeans 插件。 它还包含 JRuby。 创建新的 Ruby 项目时,请确保选择 JRuby 平台。
另一种方法是从 jruby.org 网站下载发行版。
$ tar -xzvf jruby-bin-1.5.6.tar.gz
$ mv jruby-1.5.6/ ~/bin
安装 JRuby 非常容易。 我们提取压缩档案的内容,然后将目录移动到选定位置。 在我的系统上,我已将目录移到主目录的 bin 目录中。
$ ~/bin/jdk1.6.0_21/bin/java -jar ~/bin/jruby-1.5.6/lib/jruby.jar simple.rb
我们已经在选定目录中安装了 JRuby。 在lib
子目录中,我们将找到jruby.jar
文件,该文件用于执行 JRuby 脚本。
$ cat /usr/local/bin/jruby
#!/bin/bash
~/bin/jdk1.6.0_21/bin/java -jar ~/bin/jruby-1.5.6/lib/jruby.jar $1
(可选)我们可以创建一个 bash 文件,该文件将自动启动我们的 JRuby 脚本。 然后,我们可以将#!/usr/local/bin/jruby
路径放入脚本。
简单的例子
在第一个示例中,我们将在屏幕上显示一个基本窗口。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This example shows a simple
# window in the center of the screen.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import javax.swing.JFrame
class Example < JFrame
def initialize
super "Simple"
self.initUI
end
def initUI
self.setSize 300, 200
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
尽管这段代码很小,但是应用窗口可以做很多事情。 可以调整大小,最大化,最小化。 随之而来的所有复杂性对应用员都是隐藏的。
include Java
我们将 Java API 包含在 JRuby 中。
import javax.swing.JFrame
我们导入一个JFrame
类。 JFrame
是带有标题栏和边框的顶层窗口。
self.initUI
我们将用户界面的创建委托给initUI
方法。
self.setSize 300, 200
我们设置窗口的大小。
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
如果单击标题栏的关闭按钮,此方法可确保窗口终止。 默认情况下,没有任何反应。
self.setLocationRelativeTo nil
我们将窗口置于屏幕中央。
self.setVisible true
最后,窗口显示在屏幕上。
工具提示
工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This code shows a tooltip on
# a window and a button
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JPanel
class Example < JFrame
def initialize
super "Tooltips"
self.initUI
end
def initUI
panel = JPanel.new
self.getContentPane.add panel
panel.setLayout nil
panel.setToolTipText "A Panel container"
button = JButton.new "Button"
button.setBounds 100, 60, 100, 30
button.setToolTipText "A button component"
panel.add button
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在示例中,我们为框架和按钮设置工具提示。
panel = JPanel.new
self.getContentPane.add panel
我们创建一个JPanel
组件。 它是一个通用的轻量级容器。 JFrame
有一个区域,您可以在其中放置名为内容窗格的组件。 我们将面板放入此窗格。
panel.setLayout nil
默认情况下,JPanel
具有一个FlowLayout
管理器。 布局管理器用于将小部件放置在容器上。 如果我们调用setLayout nil
,则可以绝对定位组件。 为此,我们使用setBounds
方法。
panel.setToolTipText "A Panel container"
要启用工具提示,我们调用setTooltipText
方法。
图:工具提示
退出按钮
在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program creates a quit
# button. When we press the button,
# the application terminates.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JPanel
import java.lang.System
class Example < JFrame
def initialize
super "Quit button"
self.initUI
end
def initUI
panel = JPanel.new
self.getContentPane.add panel
panel.setLayout nil
qbutton = JButton.new "Quit"
qbutton.setBounds 50, 60, 80, 30
qbutton.add_action_listener do |e|
System.exit 0
end
panel.add qbutton
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们在窗口上放置一个JButton
。 我们将向该按钮添加一个动作监听器。
qbutton = JButton.new "Quit"
qbutton.setBounds 50, 60, 80, 30
在这里,我们创建一个按钮。 我们通过调用setBounds
方法对其进行定位。
qbutton.add_action_listener do |e|
System.exit 0
end
我们向按钮添加一个动作监听器。 监听器终止应用。
图:退出按钮
本节介绍了使用 JRuby 语言的 Swing 工具包。
JRuby Swing 中的布局管理
在 JRuby Swing 编程教程的这一部分中,我们将介绍布局管理器。
在设计应用的 GUI 时,我们决定要使用哪些组件以及如何在应用中组织这些组件。 为了组织我们的组件,我们使用专门的不可见对象,称为布局管理器。 Swing 工具箱包含两种组件。 容器和子项。 容器将子项分组为合适的布局。 要创建布局,我们使用布局管理器。
绝对定位
在大多数情况下,程序员应使用布局管理器。 在某些情况下,我们可以使用绝对定位。 在绝对定位中,程序员以像素为单位指定每个组件的位置和大小。 如果调整窗口大小,则组件的大小和位置不会更改。 在各种平台上,应用看起来都不同,在 Linux 上看起来不错,在 Mac OS 上看起来不太正常。 在应用中更改字体可能会破坏布局。 如果将应用翻译成另一种语言,则必须重做布局。 对于所有这些问题,仅在有理由时才使用绝对定位。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, we lay out three images
# using absolute positioning.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import javax.swing.ImageIcon
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.JFrame
class Example < JFrame
def initialize
super "Absolute"
self.initUI
end
def initUI
panel = JPanel.new
panel.setLayout nil
panel.setBackground Color.new 66, 66, 66
self.getContentPane.add panel
rot = ImageIcon.new "rotunda.jpg"
rotLabel = JLabel.new rot
rotLabel.setBounds 20, 20, rot.getIconWidth, rot.getIconHeight
min = ImageIcon.new "mincol.jpg"
minLabel = JLabel.new min
minLabel.setBounds 40, 160, min.getIconWidth, min.getIconHeight
bar = ImageIcon.new "bardejov.jpg"
barLabel = JLabel.new bar
barLabel.setBounds 170, 50, bar.getIconWidth, bar.getIconHeight
panel.add rotLabel
panel.add minLabel
panel.add barLabel
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 350, 300
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在此示例中,我们使用绝对定位显示了三幅图像。
panel.setLayout nil
Swing 中的容器已经具有默认的布局管理器。 JPanel
具有FlowLayout
管理器作为其默认布局管理器。 我们将setLayout
方法与nil
参数一起使用,以删除默认的布局管理器,而改用绝对定位。
rot = ImageIcon.new "rotunda.jpg"
rotLabel = JLabel.new rot
rotLabel.setBounds 20, 20, rot.getIconWidth, rot.getIconHeight
我们创建一个ImageIcon
对象。 我们将图标放入JLabel
组件中以显示它。 然后,我们使用setBounds
方法将标签放置在面板上。 前两个参数是标签的 x,y 位置。 第 3 和第 4 个参数是图标的宽度和高度。
panel.add rotLabel
我们将标签添加到面板容器中。
图:绝对定位
按钮示例
在下面的示例中,我们将在窗口的右下角放置两个按钮。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, we use the BoxLayout
# manager to position two buttons in the
# bottom right corner of the window.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Dimension
import javax.swing.JButton
import javax.swing.JPanel
import javax.swing.JFrame
import javax.swing.BoxLayout
import javax.swing.Box
class Example < JFrame
def initialize
super "Buttons"
self.initUI
end
def initUI
basic = JPanel.new
basic.setLayout BoxLayout.new basic, BoxLayout::Y_AXIS
self.add basic
basic.add Box.createVerticalGlue
bottom = JPanel.new
bottom.setLayout BoxLayout.new bottom, BoxLayout::X_AXIS
bottom.setAlignmentX 1.0
okButton = JButton.new "OK"
closeButton = JButton.new "Close"
bottom.add okButton
bottom.add Box.createRigidArea Dimension.new 5, 0
bottom.add closeButton
bottom.add Box.createRigidArea Dimension.new 15, 0
basic.add bottom
basic.add Box.createRigidArea Dimension.new 0, 15
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们将创建两个面板。 基本面板具有垂直框布局。 底部面板有一个水平面板。 我们将在基础面板中放置一个底部面板。 我们将右对齐底部面板。 窗口顶部和底部面板之间的空间是可扩展的。 这是通过垂直胶水完成的。
basic = JPanel.new
basic.setLayout BoxLayout.new basic, BoxLayout::Y_AXIS
...
bottom = JPanel.new
bottom.setLayout BoxLayout.new bottom, BoxLayout::X_AXIS
基本面板具有垂直框布局。 底部面板具有水平框布局。
bottom.setAlignmentX 1.0
底部面板右对齐。
basic.add Box.createVerticalGlue
我们创建一个垂直胶水。 胶水是垂直可扩展的白色空间,它将带有按钮的水平框推到底部。
okButton = JButton.new "OK"
closeButton = JButton.new "Close"
这是两个将进入窗口右下角的按钮。
bottom.add okButton
bottom.add Box.createRigidArea Dimension.new 5, 0
我们将“确定”按钮放入水平框中。 我们在按钮旁边放置了一些刚性空间。 这样两个按钮之间会有一些空间。
basic.add Box.createRigidArea Dimension.new 0, 15
我们在按钮和窗口的边框之间留出一些空间。
图:按钮示例
Windows 示例
以下示例使用GroupLayout
管理器创建 Windows 对话框。 该对话框来自 JDeveloper 应用。
GroupLayout
管理器将布局的创建分为两个步骤。 第一步,我们沿着水平轴布置组件。 在第二步中,我们沿垂直轴布置组件。 这在布局管理器中是一个不寻常的想法,但效果很好。
有两种类型的安排:顺序安排和并行安排。 在两种布局中,我们都可以顺序或并行排列组件。 在水平布局中,一行组件称为顺序组。 一列组件称为并行组。 在垂直布局中,一列组件称为顺序组。 一排组件称为并行组。 您必须正确理解这些定义才能与GroupLayout
管理器一起使用。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, GroupLayout
# manager to create a Windows
# example.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Dimension
import java.awt.Color
import javax.swing.JButton
import javax.swing.SwingConstants
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.JTextArea
import javax.swing.BorderFactory
import javax.swing.GroupLayout
class Example < JFrame
def initialize
super "Windows"
self.initUI
end
def initUI
layout = GroupLayout.new self.getContentPane
self.getContentPane.setLayout layout
layout.setAutoCreateGaps true
layout.setAutoCreateContainerGaps true
self.setPreferredSize Dimension.new 350, 300
windows = JLabel.new "Windows"
area = JTextArea.new
area.setEditable false
area.setBorder BorderFactory.createLineBorder Color.gray
activateButton = JButton.new "Activate"
closeButton = JButton.new "Close"
helpButton = JButton.new "Help"
okButton = JButton.new "OK"
sg = layout.createSequentialGroup
pg1 = layout.createParallelGroup
pg2 = layout.createParallelGroup
pg1.addComponent windows
pg1.addComponent area
pg1.addComponent helpButton
sg.addGroup pg1
pg2.addComponent activateButton
pg2.addComponent closeButton
pg2.addComponent okButton
sg.addGroup pg2
layout.setHorizontalGroup sg
sg1 = layout.createSequentialGroup
sg2 = layout.createSequentialGroup
pg1 = layout.createParallelGroup
pg2 = layout.createParallelGroup
sg1.addComponent windows
pg1.addComponent area
sg2.addComponent activateButton
sg2.addComponent closeButton
pg1.addGroup sg2
sg1.addGroup pg1
pg2.addComponent helpButton
pg2.addComponent okButton
sg1.addGroup pg2
layout.setVerticalGroup sg1
layout.linkSize SwingConstants::HORIZONTAL,
okButton, helpButton, closeButton, activateButton
self.pack
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们使用GroupLayout
管理器创建由六个组件组成的布局。 组件组沿两个轴形成。
sg = layout.createSequentialGroup
pg1 = layout.createParallelGroup
pg2 = layout.createParallelGroup
pg1.addComponent windows
pg1.addComponent area
pg1.addComponent helpButton
sg.addGroup pg1
pg2.addComponent activateButton
pg2.addComponent closeButton
pg2.addComponent okButton
sg.addGroup pg2
layout.setHorizontalGroup sg
第一步,我们有一个水平布局。 它由两组平行的三个部分组成。
sg1 = layout.createSequentialGroup
sg2 = layout.createSequentialGroup
pg1 = layout.createParallelGroup
pg2 = layout.createParallelGroup
sg1.addComponent windows
pg1.addComponent area
sg2.addComponent activateButton
sg2.addComponent closeButton
pg1.addGroup sg2
sg1.addGroup pg1
pg2.addComponent helpButton
pg2.addComponent okButton
sg1.addGroup pg2
layout.setVerticalGroup sg1
垂直布局有点复杂。 首先,我们添加一个组件。 然后,我们添加一个包含单个组件的并行组和一个包含两个组件的顺序组。 最后,我们添加两个组件的并行组。
layout.linkSize SwingConstants::HORIZONTAL,
okButton, helpButton, closeButton, activateButton
此代码使所有按钮的大小相同。 我们只需要设置它们的宽度,因为默认情况下它们的高度已经相同。
图:窗口示例
查看示例的屏幕截图。 注意,可以将组件分为垂直和水平组件集。 例如,标签,区域和“帮助”按钮组件可以形成垂直的组件组。 这正是GroupLayout
管理器所做的。 它通过形成组件的垂直和水平组来布局组件。
在 JRuby Swing 教程的这一部分中,我们提到了组件的布局管理。
JRuby Swing 中的组件
在 JRuby Swing 编程教程的这一部分中,我们将介绍基本的 Swing 组件。
组件是 GUI 应用的基本构建块。 多年来,一些组件已成为所有 OS 平台上所有工具包中的标准组件。 例如,按钮,复选框或滚动条。 Swing 具有丰富的组件集,可满足大多数编程需求。 可以将更多专用组件创建为自定义组件。
JCheckBox
JCheckBox
是具有两种状态的组件:开和关。 开状态通过复选标记显示。 它用来表示一些布尔属性。 JCheckBox
组件提供了一个带有文本标签的复选框。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program uses JCheckBox
# component to show/hide the title
# of the window
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Dimension
import javax.swing.JCheckBox
import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.JFrame
class Example < JFrame
def initialize
super "JCheckBox example"
self.initUI
end
def initUI
self.setLayout BoxLayout.new getContentPane, BoxLayout::Y_AXIS
self.add Box.createRigidArea Dimension.new 15, 20
cb = JCheckBox.new "Show Title", true
cb.setBounds 50, 60, 80, 30
cb.setFocusable(false)
cb.add_action_listener do |e|
if self.getTitle.empty?
self.setTitle "JCheckBox example"
else
self.setTitle ""
end
end
add cb
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在我们的示例中,我们在窗口上放置了一个复选框。 复选框显示或隐藏窗口的标题。
self.setLayout BoxLayout.new getContentPane, BoxLayout::Y_AXIS
self.add Box.createRigidArea Dimension.new 15, 20
在此示例中,我们使用BoxLayout
布局管理器。 我们在此处放置一些空间,以使复选框不太靠近角落。
cb = JCheckBox.new "Show Title", true
JCheckBox
组件已创建。 构造器的第一个参数是其文本标签。 第二个参数是一个布尔值,指示初始选择状态。 如果为true
,则选中该复选框。
cb.setFocusable false
我们禁用复选框的焦点。 可以使用空格键选择或取消选择具有焦点的JCheckBox
。
cb.add_action_listener do |e|
if self.getTitle.empty?
self.setTitle "JCheckBox example"
else
self.setTitle ""
end
end
在动作监听器内部,我们检查标题是否已设置。 如果有标题,我们将其删除。 如果没有标题,我们设置一个。 这样,我们可以切换标题的可见性。
图:JCheckBox
JLabel
JLabel
组件用于显示文本,图像或两者。 没有用户交互。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, we show lyrics of a
# song in a window.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Font
import javax.swing.JFrame
import javax.swing.BorderFactory
import javax.swing.JPanel
import javax.swing.JLabel
class Example < JFrame
def initialize
super "Lyrics"
self.initUI
end
def initUI
lyrics = "<html>It's way too late to think of<br>
Someone I would call now<br>
And neon signs got tired<br>
Red eye flights help the stars out<br>
I'm safe in a corner<br>
Just hours before me<br>
<br>
I'm waking with the roaches<br>
The world has surrendered<br>
I'm dating ancient ghosts<br>
The ones I made friends with<br>
The comfort of fireflies<br>
Long gone before daylight<br>
<br>
And if I had one wishful field tonight<br>
I'd ask for the sun to never rise<br>
If God leant his voice for me to speak<br>
I'd say go to bed, world<br>
<br>
I've always been too late<br>
To see what's before me<br>
And I know nothing sweeter than<br>
Champaign from last New Years<br>
Sweet music in my ears<br>
And a night full of no fears<br>
<br>
But if I had one wishful field tonight<br>
I'd ask for the sun to never rise<br>
If God passed a mic to me to speak<br>
I'd say stay in bed, world<br>
Sleep in peace</html>"
panel = JPanel.new
panel.setLayout BorderLayout.new 10, 10
label = JLabel.new lyrics
label.setFont Font.new "Georgia", Font::PLAIN, 14
label.setForeground Color.new 50, 50, 25
panel.add label, BorderLayout::CENTER
panel.setBorder BorderFactory.createEmptyBorder 10, 10, 10, 10
self.add panel
self.pack
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们的示例在窗口中显示了歌曲的歌词。 我们可以在JLabel
组件中使用 HTML 标签。 我们使用<br>
标签来分隔行。
lyrics = "<html>It's way too late to think of<br>
Someone I would call now<br>
And neon signs got tired<br>
...
我们定义了多行文字。
label = JLabel.new lyrics
label.setFont Font.new "Georgia", Font::PLAIN, 14
在这里,我们创建标签组件。 我们将其字体设置为 14 像素高的纯乔治亚州。
panel.add label, BorderLayout::CENTER
panel.setBorder BorderFactory.createEmptyBorder 10, 10, 10, 10
我们将标签放在面板的中央。 我们在标签周围放置了 10px。
图:JLabel
组件
JSlider
JSlider
是一个组件,使用户可以通过在有限的间隔内滑动旋钮来以图形方式选择一个值。 我们的示例将显示音量控制。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program we use a JSlider
# component to control volume images.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Dimension
import java.awt.BorderLayout
import javax.swing.event.ChangeListener
import javax.swing.JFrame
import javax.swing.JSlider
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.BorderFactory
import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.ImageIcon
class ChangeEvent
include ChangeListener
def setLabel label
@label = label
end
def setIcons mute, min, med, max
@mute = mute
@min = min
@med = med
@max = max
end
def stateChanged e
sender = e.getSource
value = sender.getValue
if value == 0
@label.setIcon(@mute)
elsif value > 0 and value <= 30
@label.setIcon(@min)
elsif value > 30 and value < 80
@label.setIcon(@med)
else
@label.setIcon(@max)
end
end
end
class Example < JFrame
def initialize
super "JSlider"
self.initUI
end
def initUI
mute = ImageIcon.new "mute.png"
min = ImageIcon.new "min.png"
med = ImageIcon.new "med.png"
max = ImageIcon.new "max.png"
panel = JPanel.new
panel.setLayout BoxLayout.new panel, BoxLayout::X_AXIS
panel.setBorder BorderFactory.createEmptyBorder 40, 40, 40, 40
self.setLayout BorderLayout.new
panel.add Box.createHorizontalGlue
label = JLabel.new mute, JLabel::CENTER
slider = JSlider.new 0, 150, 0
ce = ChangeEvent.new
ce.setLabel label
ce.setIcons mute, min, med, max
slider.add_change_listener ce
slider.setPreferredSize Dimension.new 150, 30
panel.add slider
panel.add label
panel.add Box.createRigidArea Dimension.new 5, 0
panel.add Box.createHorizontalGlue
self.add panel, BorderLayout::CENTER
self.pack
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在代码示例中,我们显示了JSlider
和JLabel
。 通过拖动滑块,我们可以更改标签组件上的图标。 我们有四个代表声音各种状态的图像。
mute = ImageIcon.new "mute.png"
在这里,我们创建一个图像图标。
panel.setLayout BoxLayout.new panel, BoxLayout::X_AXIS
面板组件具有水平BoxLayout
。
panel.setBorder BorderFactory.createEmptyBorder 40, 40, 40, 40
我们在面板周围创建 40px 的边框。
panel.add Box.createHorizontalGlue
我们在左右两侧都放置了可调整大小的空间。 这是为了防止JSlider
增长到不自然的大小。
label = JLabel.new mute, JLabel::CENTER
该行创建一个具有指定图像和水平对齐方式的JLabel
实例。 默认情况下,标签在其显示区域中垂直居中。
slider = JSlider.new 0, 150, 0
这是一个JSlider
构造器。 参数为最小值,最大值和当前值。
ce = ChangeEvent.new
ce.setLabel label
ce.setIcons mute, min, med, max
创建一个ChangeEvent
对象。 我们为此对象设置了标签和图标。
slider.add_change_listener ce
每次我们移动滑块时,都会调用ChangeEvent
对象的stateChanged
方法。
panel.add Box.createRigidArea Dimension.new 5, 0
我们在两个组件之间放置一个 5px 的刚性空间。 当滑块位于末端位置时,它们彼此之间过于靠近。
class ChangeEvent
include ChangeListener
这是一个ChangeEvent
类,它实现了ChangeListener
。 因此,此类必须实现changeEvent
方法。
sender = e.getSource
value = sender.getValue
在changeEvent
方法内部,我们获取事件源。 它是产生事件的滑块。 从发送者,我们获得当前值。
if value == 0
@label.setIcon(@mute)
如果该值等于零,我们将更新标签以具有mute.png
图像。
图:JSlider
JToggleButton
JToggleButton
是具有两种状态的按钮。 已按下但未按下。 通过单击可以在这两种状态之间切换。 在某些情况下此功能非常合适。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program uses toggle buttons to
# change the background color of
# a panel.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import java.awt.Dimension
import java.awt.event.ActionListener
import javax.swing.JToggleButton
import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.BorderFactory
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.border.LineBorder
class Example < JFrame
include ActionListener
def initialize
super "JToggleButton"
self.initUI
end
def initUI
self.setPreferredSize Dimension.new 280, 200
bottom = JPanel.new
bottom.setLayout BoxLayout.new bottom, BoxLayout::X_AXIS
bottom.setBorder BorderFactory.createEmptyBorder 20, 20, 20, 20
leftPanel = JPanel.new
leftPanel.setLayout BoxLayout.new leftPanel, BoxLayout::Y_AXIS
@display = JPanel.new
@display.setPreferredSize Dimension.new 110, 110
@display.setBorder LineBorder.createGrayLineBorder
@display.setBackground Color.black
bottom.add @display
redButton = JToggleButton.new "red"
redButton.addActionListener self
greenButton = JToggleButton.new "green"
greenButton.addActionListener self
blueButton = JToggleButton.new "blue"
blueButton.addActionListener self
blueButton.setMaximumSize greenButton.getMaximumSize
redButton.setMaximumSize greenButton.getMaximumSize
leftPanel.add redButton
leftPanel.add Box.createRigidArea Dimension.new 25, 7
leftPanel.add greenButton
leftPanel.add Box.createRigidArea Dimension.new 25, 7
leftPanel.add blueButton
bottom.add leftPanel
bottom.add Box.createRigidArea Dimension.new 20, 0
self.add bottom
self.pack
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
def actionPerformed e
color = @display.getBackground
red = color.getRed
green = color.getGreen
blue = color.getBlue
if e.getActionCommand == "red"
if red == 0
red = 255
else
red = 0
end
end
if e.getActionCommand == "green"
if green == 0
green = 255
else
green = 0
end
end
if e.getActionCommand == "blue"
if blue == 0
blue = 255
else
blue = 0
end
end
setCol = Color.new red, green, blue
@display.setBackground setCol
end
end
Example.new
在代码示例中,我们使用三个切换按钮来更改矩形组件的颜色。
class Example < JFrame
include ActionListener
该类实现ActionListener
。 我们将在Example
类的actionPerformed
方法中执行一些操作。
redButton = JToggleButton.new "red"
redButton.addActionListener self
我们创建一个JToggleButton
组件。 我们向按钮添加一个动作监听器。 动作监听器是Example
类。 当我们单击redButton
时,将调用Example
类的actionPerformed
方法。
blueButton.setMaximumSize greenButton.getMaximumSize
redButton.setMaximumSize greenButton.getMaximumSize
我们使三个按钮的大小相等。
color = @display.getBackground
red = color.getRed
green = color.getGreen
blue = color.getBlue
我们确定显示背景颜色的当前红色,绿色,蓝色部分。
if e.getActionCommand == "red"
if red == 0
red = 255
else
red = 0
end
end
我们确定切换了哪个按钮,并相应地更新 RGB 值的颜色部分。
setCol = Color.new red, green, blue
@display.setBackground setCol
在此创建新的颜色,并将显示面板更新为新的颜色。
图:JToggleButton
JList
JList
是显示对象列表的组件。 它允许用户选择一项或多项。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, we show all
# available fonts of a system in
# a JList component.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.Font
import java.awt.GraphicsEnvironment
import javax.swing.JFrame
import javax.swing.BorderFactory
import javax.swing.JScrollPane
import javax.swing.JPanel
import javax.swing.JLabel
import javax.swing.JList
class Example < JFrame
def initialize
super "JList"
initUI
end
def initUI
panel = JPanel.new
panel.setLayout BorderLayout.new
panel.setBorder BorderFactory.createEmptyBorder 20, 20, 20, 20
ge = GraphicsEnvironment.getLocalGraphicsEnvironment
fonts = ge.getAvailableFontFamilyNames
list = JList.new fonts
list.add_list_selection_listener do |e|
sender = e.source
if not e.getValueIsAdjusting
name = sender.getSelectedValue
font = Font.new name, Font::PLAIN, 13
@label.setFont font
end
end
pane = JScrollPane.new
pane.getViewport.add list
pane.setPreferredSize Dimension.new 250, 200
panel.add pane
@label = JLabel.new "Aguirre, der Zorn Gottes"
@label.setFont Font.new "Serif", Font::PLAIN, 12
self.add @label, BorderLayout::SOUTH
self.add panel
self.pack
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在我们的示例中,我们将显示JList
和JLabel
组件。 列表组件包含我们系统上所有可用字体系列名称的列表。 如果我们从列表中选择一项,则标签将以我们选择的字体显示。
ge = GraphicsEnvironment.getLocalGraphicsEnvironment
fonts = ge.getAvailableFontFamilyNames
在这里,我们获得系统上所有可能的字体系列名称。
list = JList.new fonts
我们创建JList
组件的实例。 它将显示所有字体系列名称。
if not e.getValueIsAdjusting
列表选择中的事件被分组。 我们收到选择和取消选择事件。 为了仅过滤选择事件,我们使用getValueIsAdjusting
方法。
name = sender.getSelectedValue
font = Font.new name, Font::PLAIN, 13
@label.setFont font
我们得到所选项目并为标签设置新字体。
pane = JScrollPane.new
pane.getViewport.add list
默认情况下,JList
组件不可滚动。 我们将列表放入JScrollPane
以使其可滚动。
图:JList
组件
在 JRuby Swing 教程的这一部分中,我们介绍了几个 Swing 组件。
菜单和工具栏
在 JRuby Swing 编程教程的这一部分中,我们将使用菜单和工具栏。
菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。 菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。
简单菜单
第一个示例将显示一个简单的菜单。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program creates a simple
# menu.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.event.KeyEvent
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JMenuBar
import javax.swing.JMenuItem
import javax.swing.JMenu
import javax.swing.ImageIcon
import java.lang.System
class Example < JFrame
def initialize
super "Simple menu"
self.initUI
end
def initUI
menubar = JMenuBar.new
icon = ImageIcon.new "exit.png"
fileMenu = JMenu.new "File"
fileMenu.setMnemonic KeyEvent::VK_F
itemExit = JMenuItem.new "Exit", icon
itemExit.addActionListener do |e|
System.exit 0
end
itemExit.setMnemonic KeyEvent::VK_C
itemExit.setToolTipText "Exit application"
fileMenu.add itemExit
menubar.add fileMenu
self.setJMenuBar menubar
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 250, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们的示例将显示一个菜单项。 通过选择退出菜单项,我们关闭应用。
menubar = JMenuBar.new
在这里,我们创建一个菜单栏。
icon = ImageIcon.new "exit.png"
我们将在菜单项中显示一个图标。
fileMenu = JMenu.new "File"
fileMenu.setMnemonic KeyEvent::VK_F
我们创建一个菜单对象。 菜单是包含JMenuItem
的弹出窗口。 菜单位于菜单栏上。 也可以通过键盘访问菜单。 要将菜单绑定到特定键,我们使用setMnemonic
方法。 在我们的情况下,可以使用ALT + F
快捷方式打开菜单。
itemExit = JMenuItem.new "Close", icon
itemExit.addActionListener do |e|
System.exit 0
end
在这里,我们创建一个JMenuItem
。 菜单项是显示在所选菜单的弹出窗口中的对象。 我们还为菜单项和工具提示提供了快捷方式。
fileMenu.add itemExit
菜单项被添加到菜单中。
menubar.add fileMenu
菜单添加到菜单栏。
图:简单菜单
子菜单
子菜单是插入另一个菜单对象的菜单。 下一个示例对此进行了演示。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program creates a
# submenu.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.event.KeyEvent
import java.awt.event.ActionEvent
import javax.swing.JFrame
import javax.swing.ImageIcon
import javax.swing.JMenuBar
import javax.swing.JMenu
import javax.swing.JMenuItem
import javax.swing.KeyStroke
import java.lang.System
class Example < JFrame
def initialize
super "Submenu"
self.initUI
end
def initUI
menubar = JMenuBar.new
iconNew = ImageIcon.new "new.png"
iconOpen = ImageIcon.new "open.png"
iconSave = ImageIcon.new "save.png"
iconExit = ImageIcon.new "exit.png"
fileMenu = JMenu.new "File"
fileMenu.setMnemonic KeyEvent::VK_F
imp = JMenu.new "Import"
imp.setMnemonic KeyEvent::VK_M
newsf = JMenuItem.new "Import newsfeed list..."
bookm = JMenuItem.new "Import bookmarks..."
mail = JMenuItem.new "Import mail..."
imp.add newsf
imp.add bookm
imp.add mail
fileNew = JMenuItem.new "New", iconNew
fileNew.setMnemonic KeyEvent::VK_N
fileOpen = JMenuItem.new "Open", iconOpen
fileNew.setMnemonic KeyEvent::VK_O
fileSave = JMenuItem.new "Save", iconSave
fileSave.setMnemonic KeyEvent::VK_S
fileExit = JMenuItem.new "Exit", iconExit
fileExit.addActionListener do |e|
System.exit 0
end
fileExit.setMnemonic KeyEvent::VK_C
fileExit.setToolTipText "Exit application"
fileExit.setAccelerator KeyStroke.getKeyStroke KeyEvent::VK_W,
ActionEvent::CTRL_MASK
fileMenu.add fileNew
fileMenu.add fileOpen
fileMenu.add fileSave
fileMenu.addSeparator
fileMenu.add imp
fileMenu.addSeparator
fileMenu.add fileExit
menubar.add fileMenu
self.setJMenuBar menubar
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 320, 220
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在示例中,文件菜单的子菜单中有三个选项。
imp = JMenu.new "Import"
...
fileMenu.add imp
子菜单与其他任何普通菜单一样。 它是用相同的方式创建的。 我们只需将菜单添加到现有菜单即可。
fileExit.setAccelerator KeyStroke.getKeyStroke KeyEvent::VK_W,
ActionEvent::CTRL_MASK
加速器是启动菜单项的快捷键。 在我们的情况下,通过按 Ctrl + W
关闭我们的应用。
fileMenu.addSeparator
分隔符是一条水平线,用于在视觉上分隔菜单项。 这样,我们可以将项目分组到一些合理的位置。
图:子菜单
弹出菜单
在下一个示例中,我们创建一个弹出菜单。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program creates a
# popup menu.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.event.MouseAdapter
import javax.swing.JFrame
import javax.swing.JPopupMenu
import javax.swing.JMenuItem
import java.lang.System
class MouseAction < MouseAdapter
def mouseReleased e
source = e.source
menu = source.getMenu
if e.getButton == e.button
menu.show e.getComponent, e.getX, e.getY
end
end
end
class Example < JFrame
def initialize
super "Popup menu"
self.initUI
end
def initUI
@menu = JPopupMenu.new
menuItemBeep = JMenuItem.new "Beep"
menuItemBeep.addActionListener do |e|
toolkit = getToolkit
toolkit.beep
end
@menu.add menuItemBeep
menuItemExit = JMenuItem.new "Exit"
menuItemExit.addActionListener do |e|
System.exit 0
end
@menu.add menuItemExit
self.addMouseListener MouseAction.new
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 250, 200
self.setLocationRelativeTo nil
self.setVisible true
end
def getMenu
@menu
end
end
Example.new
在我们的示例中,我们创建一个带有两个菜单项的弹出菜单。
@menu = JPopupMenu.new
menuItemBeep = JMenuItem.new "Beep"
我们创建一个弹出菜单和一个菜单项。
self.addMouseListener MouseAction.new
我们将鼠标监听器添加到Example
类。 鼠标监听器是MouseAction
用户定义的类,该类继承自MouseAdapter
。 这是一个便利类,可实现所有五个必需的方法。 方法为空。 除了实现所有五个方法之外,我们仅实现所需的方法。
class MouseAction < MouseAdapter
def mouseReleased e
...
在我们的MouseAction
类中,我们实现了mouseReleased
方法。
if e.getButton == e.button:
menu.show e.getComponent, e.getX, e.getY
end
我们在鼠标单击的 x,y 坐标处显示弹出菜单窗口。
图:弹出菜单
工具栏
菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 在 Swing 中,JToolBar
类在应用中创建一个工具栏。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program creates a
# toolbar.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.BorderLayout
import javax.swing.JFrame
import javax.swing.ImageIcon
import javax.swing.JButton
import javax.swing.JMenuBar
import javax.swing.JMenu
import javax.swing.JToolBar
import java.lang.System
class Example < JFrame
def initialize
super "Toolbar"
self.initUI
end
def initUI
menubar = JMenuBar.new
fileMenu = JMenu.new "File"
menubar.add fileMenu
toolbar = JToolBar.new
iconExit = ImageIcon.new "exit2.png"
exitButton = JButton.new iconExit
exitButton.addActionListener do |e|
System.exit 0
end
toolbar.add exitButton
self.add toolbar, BorderLayout::NORTH
self.setJMenuBar menubar
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 350, 250
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
该示例创建一个带有一个退出按钮的工具栏。
toolbar = JToolBar.new
工具栏已创建。
exitButton = JButton.new iconExit
...
toolbar.add exitButton
我们创建一个按钮并将其添加到工具栏。
self.add toolbar, BorderLayout::NORTH
工具栏位于BorderLayout
管理器的北部。 BorderLayout
管理器是JFrame
容器的默认布局管理器。
图:工具栏
在 JRuby Swing 教程的这一部分中,我们提到了菜单和工具栏。
JRuby Swing 中的对话框
在 JRuby Swing 编程教程的这一部分中,我们将使用对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
MessageDialog
消息框是方便的对话框,可向应用的用户提供消息。 该消息由文本和图像数据组成。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program demonstrates
# message dialogs.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.GridLayout
import javax.swing.JFrame
import javax.swing.JButton
import javax.swing.JPanel
import javax.swing.JOptionPane
class Example < JFrame
def initialize
super "Message boxes"
self.initUI
end
def initUI
panel = JPanel.new
panel.setLayout GridLayout.new 2, 2
errorButton = JButton.new "Error"
errorButton.addActionListener do |e|
JOptionPane.showMessageDialog panel, "Could not open file",
"Error", JOptionPane::ERROR_MESSAGE
end
warningButton = JButton.new "Warning"
warningButton.addActionListener do |e|
JOptionPane.showMessageDialog panel, "A deprecated call",
"Warning", JOptionPane::WARNING_MESSAGE
end
questionButton = JButton.new "Question"
questionButton.addActionListener do |e|
JOptionPane.showMessageDialog panel, "Are you sure to quit?",
"Question", JOptionPane::QUESTION_MESSAGE
end
informButton = JButton.new "Information"
informButton.addActionListener do |e|
JOptionPane.showMessageDialog panel, "Download completed",
"Information", JOptionPane::INFORMATION_MESSAGE
end
panel.add errorButton
panel.add warningButton
panel.add questionButton
panel.add informButton
self.add panel
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 300, 200
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们使用GridLayout
管理器来设置四个按钮的网格。 每个按钮显示一个不同的消息框。
errorButton.addActionListener do |e|
JOptionPane.showMessageDialog panel, "Could not open file",
"Error", JOptionPane::ERROR_MESSAGE
end
如果按下错误按钮,则会显示错误对话框。 我们使用showMessageDialog
方法在屏幕上显示对话框。 此方法的第一个参数是面板,在其中显示对话框。 第二个参数是要显示的消息。 第三个参数是对话框的标题。 最后一个参数是消息类型。 默认图标由消息类型决定。 在本例中,错误对话框的消息类型为ERROR_MESSAGE
。
图:错误消息 dialog
JFileChooser
JFileChooser
对话框允许用户从文件系统中选择一个文件。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, we use a JFileChooser
# to load a c file.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.BorderLayout
import java.awt.Color
import javax.swing.JFrame
import javax.swing.JButton
import javax.swing.JPanel
import javax.swing.JToolBar
import javax.swing.JFileChooser
import javax.swing.JTextArea
import javax.swing.JTextPane
import javax.swing.JScrollPane
import javax.swing.BorderFactory
import javax.swing.filechooser::FileNameExtensionFilter
class Example < JFrame
def initialize
super "FileChooser"
self.initUI
end
def initUI
@panel = JPanel.new
@panel.setLayout BorderLayout.new
toolbar = JToolBar.new
openb = JButton.new "Choose file"
openb.addActionListener do |e|
chooseFile = JFileChooser.new
filter = FileNameExtensionFilter.new "c files", "c"
chooseFile.addChoosableFileFilter filter
ret = chooseFile.showDialog @panel, "Choose file"
if ret == JFileChooser::APPROVE_OPTION
file = chooseFile.getSelectedFile
text = self.readFile file
@area.setText text.to_s
end
end
toolbar.add openb
@area = JTextArea.new
@area.setBorder BorderFactory.createEmptyBorder 10, 10, 10, 10
pane = JScrollPane.new
pane.getViewport.add @area
@panel.setBorder BorderFactory.createEmptyBorder 10, 10, 10, 10
@panel.add pane
self.add @panel
self.add toolbar, BorderLayout::NORTH
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 450, 400
self.setLocationRelativeTo nil
self.setVisible true
end
def readFile file
filename = file.getCanonicalPath
f = File.open filename, "r"
text = IO.readlines filename
return text
end
end
Example.new
在我们的代码示例中,我们使用JFileChooser
对话框选择一个 C 文件并将其内容显示在JTextArea
中。
@area = JTextArea.new
这是JTextArea
,我们将在其中显示所选文件的内容。
chooseFile = JFileChooser.new
filter = FileNameExtensionFilter.new "c files", "c"
chooseFile.addChoosableFileFilter filter
我们创建JFileChooser
对话框的实例。 我们创建一个仅显示 C 文件的过滤器。
ret = chooseFile.showDialog @panel, "Choose file"
对话框显示在屏幕上。 我们得到了返回值。
if ret == JFileChooser::APPROVE_OPTION
file = chooseFile.getSelectedFile
text = self.readFile file
@area.setText text.to_s
end
如果用户选择了文件,我们将获得文件名。 阅读其内容并将文本设置为文本区域组件。
def readFile file
filename = file.getCanonicalPath
f = File.open filename, "r"
text = IO.readlines filename
return text
end
此代码从文件中读取文本。 getCanonicalPath
返回绝对文件名。
图:JFileChooser
在 JRuby Swing 教程的这一部分中,我们使用了对话框窗口。
在 JRuby Swing 中绘图
在 JRuby Swing 编程教程的这一部分中,我们将进行绘图。
我们使用绘图来创建图表,自定义组件或创建游戏。 要进行绘图,我们使用 Swing 工具包提供的绘图 API。 绘图是在paintComponent
方法中完成的。 在绘图过程中,我们使用Graphics2D
对象。 它是一个图形上下文,允许应用绘制到组件上。 它是渲染二维形状,文本和图像的基础类。
色彩
颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 我们使用Color
类在 Swing 中处理颜色。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this example we draw nine rectangles
# filled with nine different colors.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import javax.swing.JFrame
import javax.swing.JPanel
class Canvas < JPanel
def paintComponent g
self.drawColorRectangles g
end
def drawColorRectangles g
g.setColor Color.new 125, 167, 116
g.fillRect 10, 15, 90, 60
g.setColor Color.new 42, 179, 231
g.fillRect 130, 15, 90, 60
g.setColor Color.new 70, 67, 123
g.fillRect 250, 15, 90, 60
g.setColor Color.new 130, 100, 84
g.fillRect 10, 105, 90, 60
g.setColor Color.new 252, 211, 61
g.fillRect 130, 105, 90, 60
g.setColor Color.new 241, 98, 69
g.fillRect 250, 105, 90, 60
g.setColor Color.new 217, 146, 54
g.fillRect 10, 195, 90, 60
g.setColor Color.new 63, 121, 186
g.fillRect 130, 195, 90, 60
g.setColor Color.new 31, 21, 1
g.fillRect 250, 195, 90, 60
end
end
class Example < JFrame
def initialize
super "Colors"
self.initUI
end
def initUI
canvas = Canvas.new
self.getContentPane.add canvas
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 360, 300
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在代码示例中,我们绘制了九个矩形,并用不同的颜色值填充它们。
def paintComponent g
在大多数情况下,自定义绘图是在paintComponent
中完成的。 g
参数是图形上下文。 我们称此对象为绘图操作。
g.setColor Color.new 125, 167, 116
我们将上下文的当前颜色设置为指定的颜色。 使用此图形上下文的所有后续图形操作均使用此指定的颜色。
g.fillRect 10, 15, 90, 60
我们使用上面指定的颜色值填充位于x = 10
,y = 15
且宽度= 90
和高度= 60
的矩形。
图:颜色
形状
Swing 绘图 API 可以绘制各种形状。 以下编程代码示例将显示其中的一些。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This example draws simple shapes
# on a panel.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import java.awt.RenderingHints
import java.awt.geom.Ellipse2D
import javax.swing.JFrame
import javax.swing.JPanel
class Canvas < JPanel
def paintComponent g
self.drawShapes g
end
def drawShapes g
g.setColor Color.new 150, 150, 150
rh = RenderingHints.new RenderingHints::KEY_ANTIALIASING,
RenderingHints::VALUE_ANTIALIAS_ON
rh.put RenderingHints::KEY_RENDERING,
RenderingHints::VALUE_RENDER_QUALITY
g.setRenderingHints rh
g.fillRect 20, 20, 50, 50
g.fillRect 120, 20, 90, 60
g.fillRoundRect 250, 20, 70, 60, 25, 25
g.fill Ellipse2D::Double.new 10, 100, 80, 100
g.fillArc 120, 130, 110, 100, 5, 150
g.fillOval 270, 130, 50, 50
end
end
class Example < JFrame
def initialize
super "Shapes"
self.initUI
end
def initUI
canvas = Canvas.new
self.getContentPane.add canvas
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 350, 250
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在此代码示例中,我们在窗口上绘制了六个不同的形状。 正方形,矩形,圆角矩形,椭圆形,弧形和椭圆形。 我们不会绘制形状的轮廓,但是会用灰色填充形状的内部空间。
rh = RenderingHints.new RenderingHints::KEY_ANTIALIASING,
RenderingHints::VALUE_ANTIALIAS_ON
借助渲染提示,我们可以控制绘图的质量。 在上面的代码中,我们实现了抗锯齿。 使用抗锯齿,形状更平滑。
g.setColor Color.new 150, 150, 150
我们将以某种灰色绘图。
g.fillRect 20, 20, 50, 50
g.fillRect 120, 20, 90, 60
g.fillRoundRect 250, 20, 70, 60, 25, 25
在这里,我们绘制一个矩形,一个正方形和一个圆角矩形。 这些方法中的前四个参数是 x,y 坐标以及宽度和高度。 fillRoundRect
的最后两个参数是四个角处圆弧的水平和垂直直径。
g.fill Ellipse2D::Double.new 10, 100, 80, 100
g.fillArc 120, 130, 110, 100, 5, 150
g.fillOval 270, 130, 50, 50
这三条线绘制一个椭圆,一个弧形和一个椭圆形。
图:形状
透明矩形
透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# This program draws ten
# rectangles with different
# levels of transparency.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import java.awt.AlphaComposite
import javax.swing.JFrame
import javax.swing.JPanel
class Canvas < JPanel
def paintComponent g
g.setColor Color::BLUE
for i in 1..10 do
g.setComposite AlphaComposite.getInstance AlphaComposite::SRC_OVER,
i * 0.1
g.fillRect 50 * i, 20, 40, 40
end
end
end
class Example < JFrame
def initialize
super "Transparent rectangles"
self.initUI
end
def initUI
canvas = Canvas.new
self.getContentPane.add canvas
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 590, 120
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在示例中,我们将绘制十个具有不同透明度级别的矩形。
g.setComposite AlphaComposite.getInstance AlphaComposite::SRC_OVER,
i * 0.1
AlphaComposite
类实现基本的 alpha 合成规则。
图:透明矩形
甜甜圈形状
在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。 仿射变换由零个或多个线性变换(旋转,缩放或剪切)和平移(移位)组成。 AffineTransform
是 Swing 中用于执行仿射变换的类。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this code example, we create
# a Donut shape.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.BasicStroke
import java.awt.Color
import java.awt.RenderingHints
import java.awt.geom.AffineTransform
import java.awt.geom.Ellipse2D
import javax.swing.JFrame
import javax.swing.JPanel
class Canvas < JPanel
def paintComponent g
self.drawDonut g
end
def drawDonut g
rh = RenderingHints.new RenderingHints::KEY_ANTIALIASING,
RenderingHints::VALUE_ANTIALIAS_ON
rh.put RenderingHints::KEY_RENDERING,
RenderingHints::VALUE_RENDER_QUALITY
g.setRenderingHints rh
size = self.getSize
w = size.getWidth
h = size.getHeight
e = Ellipse2D::Double.new 0, 0, 80, 130
g.setStroke BasicStroke.new 1
g.setColor Color.gray
deg = 0
72.times do
at = AffineTransform.getTranslateInstance w / 2, h / 2
at.rotate deg/180.0 * Math::PI
g.draw at.createTransformedShape e
deg += 5
end
end
end
class Example < JFrame
def initialize
super "Donut"
self.initUI
end
def initUI
canvas = Canvas.new
self.getContentPane.add canvas
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 350, 320
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。 甜甜圈在窗口中居中。
size = self.getSize
w = size.getWidth
h = size.getHeight
在这里,我们确定窗口的宽度和高度。 我们需要这些值来使甜甜圈形状居中。
e = Ellipse2D::Double.new 0, 0, 80, 130
我们创建一个椭圆形。 我们将旋转此椭圆以创建甜甜圈形状。
g.setStroke BasicStroke.new 1
g.setColor Color.gray
我们为形状的轮廓设置笔触和颜色。
deg = 0
72.times do
我们绘制一个椭圆对象 72 次。 每次,我们再将椭圆旋转 5 度。 这将创建我们的甜甜圈形状。
at = AffineTransform.getTranslateInstance w / 2, h / 2
at.rotate deg/180.0 * Math::PI
g.draw at.createTransformedShape e
在AffineTransform
类的帮助下,我们将图形转换到窗口的中心。 然后我们进行旋转。 createTransformedShape
方法会将这些仿射变换应用于椭圆。 然后使用draw
方法绘制变换后的椭圆。 rotate
方法以弧度表示角度,因此我们以度为单位计算弧度。
绘制文字
在最后一个示例中,我们将在窗口上绘制文本。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this example we draw lyrics of a
# song on the window panel.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import java.awt.Font
import java.awt.RenderingHints
import java.awt.geom.Ellipse2D
import javax.swing.JFrame
import javax.swing.JPanel
class Canvas < JPanel
def paintComponent g
self.drawLyrics g
end
def drawLyrics g
rh = RenderingHints.new RenderingHints::KEY_ANTIALIASING,
RenderingHints::VALUE_ANTIALIAS_ON
rh.put RenderingHints::KEY_RENDERING,
RenderingHints::VALUE_RENDER_QUALITY
g.setRenderingHints rh
g.setFont Font.new "Purisa", Font::PLAIN, 13
g.drawString "Most relationships seem so transitory", 20, 30
g.drawString "They're all good but not the permanent one", 20, 60
g.drawString "Who doesn't long for someone to hold", 20, 90
g.drawString "Who knows how to love you without being told", 20, 120
g.drawString "Somebody tell me why I'm on my own", 20, 150
g.drawString "If there's a soulmate for everyone", 20, 180
end
end
class Example < JFrame
def initialize
super "Soulmate"
initUI
end
def initUI
canvas = Canvas.new
self.getContentPane.add canvas
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setSize 400, 250
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
我们在窗口上画一首歌的歌词。
rh = RenderingHints.new RenderingHints::KEY_TEXT_ANTIALIASING,
RenderingHints::VALUE_TEXT_ANTIALIAS_ON
g.setRenderingHints rh
我们在画上应用文字抗锯齿。
g.setFont Font.new "Purisa", Font::PLAIN, 13
我们指定字体名称,样式和磅值,并在其中绘制歌词。
g.drawString "Most relationships seem so transitory", 20, 30
drawString
方法绘制文本。
图:绘制文本
在 JRuby Swing 编程教程的这一部分中,我们做了一些绘图。
JRuby Swing 中的贪食蛇
在 JRuby Swing 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。
开发
蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"
消息。
#!/usr/local/bin/jruby
# ZetCode JRuby Swing tutorial
#
# In this program, we create
# a Nibbles game clone.
#
# author: Jan Bodnar
# website: www.zetcode.com
# last modified: December 2010
include Java
import java.awt.Color
import java.awt.Font
import java.awt.Dimension
import java.awt.Toolkit
import java.awt.event.ActionListener
import java.awt.event.KeyEvent
import java.awt.event.KeyListener
import javax.swing.JFrame
import javax.swing.ImageIcon
import javax.swing.JPanel
import javax.swing.Timer
NWIDTH = 300
NHEIGHT = 300
DOT_SIZE = 10
ALL_DOTS = NWIDTH * NHEIGHT / (DOT_SIZE * DOT_SIZE)
RAND_POS = 25
DELAY = 140
$x = [0] * ALL_DOTS
$y = [0] * ALL_DOTS
class Board < JPanel
include KeyListener, ActionListener
def initialize
super
self.setFocusable true
self.initGame
end
def initGame
@left = false
@right = true
@up = false
@down = false
@inGame = true
@dots = 3
begin
iid = ImageIcon.new "dot.png"
@ball = iid.getImage
iia = ImageIcon.new "apple.png"
@apple = iia.getImage
iih = ImageIcon.new "head.png"
@head = iih.getImage
rescue
puts "cannot load images"
end
for i in 0..@dots
$x[i] = 50 - i * 10
$y[i] = 50
end
self.locateApple
self.setBackground Color.black
self.addKeyListener self
@timer = Timer.new DELAY, self
@timer.start
end
def paint g
super g
if @inGame
self.drawObjects g
Toolkit.getDefaultToolkit.sync
g.dispose
else
self.gameOver g
end
end
def drawObjects g
g.drawImage @apple, @apple_x, @apple_y, self
for z in 0..@dots
if z == 0
g.drawImage @head, $x[z], $y[z], self
else
g.drawImage @ball, $x[z], $y[z], self
end
end
end
def gameOver g
msg = "Game Over"
small = Font.new "Helvetica", Font::BOLD, 14
metr = self.getFontMetrics small
g.setColor Color.white
g.setFont small
g.drawString msg, (NWIDTH - metr.stringWidth(msg)) / 2,
NHEIGHT / 2
@timer.stop
end
def checkApple
if $x[0] == @apple_x and $y[0] == @apple_y
@dots = @dots + 1
self.locateApple
end
end
def move
z = @dots
while z > 0
$x[z] = $x[(z - 1)]
$y[z] = $y[(z - 1)]
z = z - 1
end
if @left
$x[0] -= DOT_SIZE
end
if @right
$x[0] += DOT_SIZE
end
if @up
$y[0] -= DOT_SIZE
end
if @down
$y[0] += DOT_SIZE
end
end
def checkCollision
z = @dots
while z > 0
if z > 4 and $x[0] == $x[z] and $y[0] == $y[z]
@inGame = false
end
z = z - 1
end
if $y[0] > NHEIGHT - DOT_SIZE
@inGame = false
end
if $y[0] < 0
@inGame = false
end
if $x[0] > NWIDTH - DOT_SIZE
@inGame = false
end
if $x[0] < 0
@inGame = false
end
end
def locateApple
r = rand RAND_POS
@apple_x = r * DOT_SIZE
r = rand RAND_POS
@apple_y = r * DOT_SIZE
end
def actionPerformed e
if @inGame
self.checkApple
self.checkCollision
self.move
end
self.repaint
end
def keyReleased e
end
def keyPressed e
key = e.getKeyCode
if key == KeyEvent::VK_LEFT and not @right
@left = true
@up = false
@down = false
end
if key == KeyEvent::VK_RIGHT and not @left
@right = true
@up = false
@down = false
end
if key == KeyEvent::VK_UP and not @down
@up = true
@right = false
@left = false
end
if key == KeyEvent::VK_DOWN and not @up
@down = true
@right = false
@left = false
end
end
end
class Example < JFrame
def initialize
super "Nibbles"
self.initUI
end
def initUI
board = Board.new
board.setPreferredSize Dimension.new NWIDTH, NHEIGHT
self.add board
self.pack
self.setResizable false
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setLocationRelativeTo nil
self.setVisible true
end
end
Example.new
首先,我们将定义一些在游戏中使用的常量。
WIDTH
和HEIGHT
常数确定电路板的大小。 DOT_SIZE
是苹果的大小和蛇的点。 ALL_DOTS
常数定义了板上可能的最大点数。 RAND_POS
常数用于计算苹果的随机位置。 DELAY
常数确定游戏的速度。
$x = [0] * ALL_DOTS
$y = [0] * ALL_DOTS
这两个数组存储蛇的所有可能关节的 x,y 坐标。
initGame
方法初始化变量,加载图像并启动超时功能。
def paint g
super g
if @inGame
self.drawObjects g
Toolkit.getDefaultToolkit.sync
g.dispose
else
self.gameOver g
end
end
在paint
方法内部,我们检查@inGame
变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"
文本。 Toolkit.getDefaultToolkit.sync
方法可确保显示为最新。 这对于动画很有用。
def drawObjects g
g.drawImage @apple, @apple_x, @apple_y, self
for z in 0..@dots
if z == 0
g.drawImage @head, $x[z], $y[z], self
else
g.drawImage @ball, $x[z], $y[z], self
end
end
end
drawObjects
方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。
def gameOver g
msg = "Game Over"
small = Font.new "Helvetica", Font::BOLD, 14
metr = self.getFontMetrics small
g.setColor Color.white
g.setFont small
g.drawString msg, (NWIDTH - metr.stringWidth(msg)) / 2,
NHEIGHT / 2
@timer.stop
end
在gameOver
方法中,我们在窗口中心显示"Game Over"
消息。 我们也停止计时器。
def checkApple
if $x[0] == @apple_x and $y[0] == @apple_y
@dots = @dots + 1
self.locateApple
end
end
checkApple
方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locateApple
方法,该方法将随机放置一个新的Apple
对象。
在move
方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
while z > 0
$x[z] = $x[(z - 1)]
$y[z] = $y[(z - 1)]
z = z - 1
end
该代码将关节向上移动。
if @left
$x[0] -= DOT_SIZE
end
将头向左移动。
在checkCollision
方法中,我们确定蛇是否击中了自己或撞墙之一。
while z > 0
if z > 4 and $x[0] == $x[z] and $y[0] == $y[z]
@inGame = false
end
z = z - 1
end
如果蛇用头撞到关节之一,我们就结束游戏。
if $y[0] > NHEIGHT - DOT_SIZE
@inGame = false
end
如果蛇击中了棋盘的底部,我们就结束了游戏。
locateApple
方法在板上随机放置一个苹果。
r = rand RAND_POS
我们得到一个从 0 到RAND_POS-1
的随机数。
@apple_x = r * DOT_SIZE
...
@apple_y = r * DOT_SIZE
这些行设置了apple
对象的 x,y 坐标。
def actionPerformed e
if @inGame
self.checkApple
self.checkCollision
self.move
end
self.repaint
end
每隔DELAY
ms,将调用actionPerformed
方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。
在Board
类的keyPressed
方法中,我们确定按下的键。
if key == KeyEvent::VK_LEFT and not @right
@left = true
@up = false
@down = false
end
如果单击左光标键,则将@left
变量设置为true
。 在move
方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。
class Example < JFrame
def initialize
super "Nibbles"
self.initUI
end
def initUI
board = Board.new
board.setPreferredSize Dimension.new NWIDTH, NHEIGHT
self.add board
self.pack
self.setResizable false
self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE
self.setLocationRelativeTo nil
self.setVisible true
end
end
在这个类中,我们设置了贪食蛇游戏。
图:贪食蛇
这是使用 Swing 库和 JRuby 编程语言编写的贪食蛇电脑游戏。
Visual Basic Winforms 教程
这是 Mono Visual Basic Winforms 教程。 本教程处理 Winforms 库和 Visual Basic 语言。 它是通过 Mono 项目在 Linux 上创建的。 大多数材料也应在 Windows .NET 上运行。 本教程适合初学者和中级程序员。
目录
Winforms
Windows Forms 是图形用户界面应用编程接口(API),包含在 Microsoft .NET Framework 中。 截至 2008 年 5 月 13 日,Mono 的 System.Windows.Forms 2.0 已完成 API。 简而言之,Winforms 是一个用于创建 GUI 应用的库。
在 ZetCode 上有 Visual Basic 教程。 Mono C# Winforms 教程是使用姐妹语言的 Winforms 库的教程。 涵盖其他 Visual Basic GUI 库的教程包括 Visual Basic Qyoto 教程和 Visual Basic GTK# 教程。
Visual Basic Winforms 简介
在 Visual Basic Winforms 编程教程的这一部分中,我们将介绍 Winforms 库并使用 Visual Basic 编程语言创建第一个程序。
本教程的目的是使您开始使用 Winforms 和 Visual Basic。 可以在此处下载本教程中使用的图像。 我使用了 Gnome 项目的探戈图标包中的一些图标。
关于
Windows 窗体是图形用户界面应用编程接口(API),包含在 Microsoft .NET Framework 中。 截至 2008 年 5 月 13 日,Mono 的System.Windows.Forms
2.0 已完成 API。 简而言之,Winforms 是一个用于创建 GUI 应用的库。
Mono 是一个跨平台的开源.NET 开发框架。 它是.NET 兼容的工具集,其中包括 C# 编译器,Visual Basic 编译器,公共语言运行时,ADO.NET,ASP.NET 和 Winforms 库。
vbnc -r:/usr/lib/mono/2.0/System.Windows.Forms.dll quitbutton.vb
上面的命令显示了如何编译quitbutton
示例。 mono VB 编译器的-r
参数加载 Winforms 程序集。 这是一个动态库。 该命令显示了 Ubuntu 系统上 DLL 库的路径。
使窗口居中
在第二个示例中,我们将窗口置于屏幕中央。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program centers a window
' on the screen
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Center"
Me.Size = New Size(250, 200)
Me.CenterToScreen
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
此代码示例在屏幕中央显示一个小窗口。
Imports System.Windows.Forms
Imports System.Drawing
Imports
关键字导入我们将在应用中使用的必需品类型。
Public Class WinVBApp
Inherits Form
在 Winforms 中,任何窗口或对话框都是窗体。 该控件是一个基本容器,其目的是显示其他子控件。 我们的类继承于一种形式。 这样,它本身就成为一种形式。
Public Sub New
...
End Sub
在构造器中,我们设置了应用。
Me.Text = "Center"
在这里,我们为表格设置标题。
Me.Size = New Size(250, 200)
我们为表单设置大小。
Me.CenterToScreen
此代码行使屏幕上的窗口居中。
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
我们运行示例。
图标
Mono 在西班牙语中意为猴子。 如果我们不为应用提供图标,则默认情况下,我们的头是猴子。 下一个示例显示如何更改此设置。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows an icon in the
' title bar
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Icon"
Me.Size = New Size(250, 200)
Try
Icon = New Icon("web.ico")
Catch e As Exception
Console.WriteLine(e.Message)
Environment.Exit(1)
End Try
Me.CenterToScreen
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
该代码示例在窗体的左上角显示一个图标。 表单的图标是代表任务栏中表单的图片以及为表单的控制框显示的图标。
Try
Icon = New Icon("web.ico")
Catch e As Exception
Console.WriteLine(e.Message)
Environment.Exit(1)
End Try
最好将所有输入输出工作放在Try/Catch
关键字之间。 web.ico
文件必须在当前工作目录中可用。 这是我们执行(./icon.exe
)应用的目录。
图:图标
退出按钮
在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a quit
' button. When we press the button,
' the application terminates.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Quit button"
Me.Size = New Size(250, 200)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim button As New Button
button.Location = New Point(30, 20)
button.Text = "Quit"
button.Parent = Me
AddHandler button.Click, AddressOf Me.OnClick
Me.CenterToScreen
End Sub
Private Sub OnClick(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们使用Button
。 这是一个非常常见的小部件。 它是一个矩形,通常显示一个文本标签。
Me.InitUI
我们将用户界面的创建委托给InitUI
方法。
Dim button As New Button
button.Location = New Point(30, 20)
button.Text = "Quit"
button.Parent = Me
我们创建按钮小部件。 我们将其放置在表单上。 为其提供标签,并将其放入表单容器中。
AddHandler button.Click, AddressOf Me.OnClick
当我们单击按钮时,将触发Click
事件。 我们使用OnClick
方法对此事件做出反应。
Private Sub OnClick(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
OnClick
方法终止应用。
图:退出按钮
本节介绍了使用 Visual Basic 语言的 Winforms 库。
布局管理
Mono Winforms 教程继续进行控件的布局管理。 在将控件放置在其父容器上之后,我们必须确保它们的布局正确。
Anchor
控件的Anchor
属性确定如何使用其父控件调整其大小。 锚是海洋世界中的一个术语。 当船锚掉入水中时,船就固定在某个地方。 Winforms 控件也是如此。
Winforms 中的每个控件都可以具有以下AnchorStyles
值之一:
TOP
LEFT
RIGHT
BOTTOM
注意,控件不限于一个值。 他们可以使用|
取这些值的任何组合。 运算符。
基本Anchor
示例
下面的示例显示一个非常基本的示例,演示Anchor
属性。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program demonstrates the Anchor property
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Check menu item"
Me.Size = New Size(380, 220)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Me.Text = "Anchor"
Size = New Size(210, 210)
Dim btn1 As New Button
btn1.Text = "Button"
btn1.Parent = Me
btn1.Location = New Point(30, 30)
Dim btn2 As New Button
btn2.Text = "Button"
btn2.Parent = Me
btn2.Location = New Point(30, 80)
btn2.Anchor = AnchorStyles.Right
Me.CenterToScreen
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
这是一个非常基本的代码示例,清楚地显示了Anchor
属性的含义。 我们在表单上有两个按钮。 第一个按钮具有默认的AnchorStyles
值,即AnchorStyles.Left
。 第二个按钮已显式设置AnchorStyles.Right
。
btn2.Anchor = AnchorStyles.Right
我们将第二个按钮的Anchor
属性明确设置为AnchorStyles
。 正确的值。
现在看看以下两个图像。 左边的是开始时显示的应用。 调整大小后,右侧显示相同的应用。 第一个按钮与表单的左边界和上边界保持距离。 第二个按钮与表单的右边框保持距离。 但是它在垂直方向上没有保持任何距离。
图:调整大小前后
Dock
Dock
属性允许我们将控件粘贴到父窗体或控件的特定边缘。
以下是可能的DockStyle
值。
TOP
LEFT
RIGHT
BOTTOM
FILL
NONE
编辑器骨架
以下代码示例演示了正在使用的Dock
属性。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program demonstrates the Dock property
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Editor"
Me.Size = New Size(220, 170)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim mainMenu As New MainMenu
Dim file As MenuItem = mainMenu.MenuItems.Add("&File")
file.MenuItems.Add(New MenuItem("E&xit", _
New EventHandler(AddressOf Me.OnExit), Shortcut.CtrlX))
Menu = mainMenu
Dim tb As New TextBox
tb.Parent = Me
tb.Dock = DockStyle.Fill
tb.Multiline = True
Dim sb As New StatusBar
sb.Parent = Me
sb.Text = "Ready"
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们显示一个菜单栏和一个状态栏。 其余区域由TextBox
控件占用。
Dim tb As New TextBox
tb.Parent = Me
在这里,我们创建TextBox
控件。 Form
容器被设置为文本框的父级。
tb.Dock = DockStyle.Fill
此代码行使TextBox
控件占用了表单容器内的剩余空间。
图:编辑器骨架
固定按钮
下一个示例显示了位于窗体右下角的两个按钮。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program positions two buttons
' in the bottom right corner of
' the window
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private Const WIDTH = 250
Private Const HEIGHT = 150
Private Const BUTTONS_SPACE = 15
Private Const PANEL_SPACE = 8
Private Const CLOSE_SPACE = 10
Public Sub New
Me.Text = "Buttons"
Me.Size = New Size(WIDTH, HEIGHT)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim ok As New Button
Dim panelHeight As New Integer = ok.Height + PANEL_SPACE
Dim panel As New Panel
panel.Height = panelHeight
panel.Dock = DockStyle.Bottom
panel.Parent = Me
Dim x As Integer = ok.Width * 2 + BUTTONS_SPACE
Dim y As Integer = (panelHeight - ok.Height) / 2
ok.Text = "Ok"
ok.Parent = panel
ok.Location = New Point(WIDTH-x, y)
ok.Anchor = AnchorStyles.Right
Dim close As New Button
x = close.Width
close.Text = "Close"
close.Parent = panel
close.Location = New Point(WIDTH-x-CLOSE_SPACE, y)
close.Anchor = AnchorStyles.Right
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
该示例在对话框的右下角显示确定,关闭按钮,这在对话框窗口中很常见。
Private Const WIDTH = 250
Private Const HEIGHT = 150
WIDTH
和HEIGHT
变量确定应用窗口的宽度和高度。
Private Const BUTTONS_SPACE = 15
Private Const PANEL_SPACE = 8
Private Const CLOSE_SPACE = 10
BUTTONS_SPACE
是“确定”和“关闭”按钮之间的空间。 PANEL_SPACE
是面板和表单底部之间的空间。 最后,CLOSE_SPACE
变量设置“关闭”按钮和表单右边框之间的间隔。
Dim panelHeight As New Integer = ok.Height + PANEL_SPACE
在这里,我们计算面板的高度。 面板的高度基于“确定”按钮的高度。 并且我们添加了一些额外的空间,以使按钮不会太靠近边框。
Dim panel As New Panel
panel.Height = panelHeight
panel.Dock = DockStyle.Bottom
panel.Parent = Me
在这里,我们创建和管理Panel
控件。 在此示例中,它用作按钮的容器。 它被粘贴到表单的底部边框。 然后将按钮放置在面板内。
ok.Text = "Ok"
ok.Parent = panel
ok.Location = New Point(WIDTH-x, y)
ok.Anchor = AnchorStyles.Right
“确定”按钮的父级设置为面板控件。 计算位置。 并且Anchor
属性设置为右侧。 另一个按钮的创建类似。
图:固定按钮
播放器骨架
本部分最后的示例显示了一个更复杂的示例。 它是音乐播放器的骨架。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a skeleton of
' a music player.
'
' author jan bodnar
' last modified June 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Player"
Me.Size = New Size(350, 280)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim mainMenu As New MainMenu
Dim file As MenuItem = mainMenu.MenuItems.Add("&File")
Dim playm As MenuItem = mainMenu.MenuItems.Add("&Play")
Dim view As MenuItem = mainMenu.MenuItems.Add("&View")
Dim tools As MenuItem = mainMenu.MenuItems.Add("&Tools")
Dim favourites As MenuItem = mainMenu.MenuItems.Add("&Favourites")
Dim help As MenuItem = mainMenu.MenuItems.Add("&Help")
file.MenuItems.Add(New MenuItem("E&xit", _
New EventHandler(AddressOf Me.OnExit), Shortcut.CtrlX))
Menu = mainMenu
Dim panel As New Panel
panel.Parent = Me
panel.BackColor = Color.Black
panel.Dock = DockStyle.Fill
Dim buttonPanel As New Panel
buttonPanel.Parent = Me
buttonPanel.Height = 40
buttonPanel.Dock = DockStyle.Bottom
Dim pause As 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")
Dim play As 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")
Dim forward As 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")
Dim backward As 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")
Dim tb As = 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
Dim audio As 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
Dim sb As New StatusBar
sb.Parent = Me
sb.Text = "Ready"
Me.CenterToScreen
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
这是一个更复杂的示例,它同时显示了Dock
和Anchor
属性。
Dim mainMenu As New MainMenu
Dim file As MenuItem = mainMenu.MenuItems.Add("&File")
...
Menu = mainMenu
在这里,我们创建菜单栏。
Dim panel As New Panel
panel.Parent = Me
panel.BackColor = Color.Black
panel.Dock = DockStyle.Fill
这是黑色的面板,占据了菜单栏,状态栏和控制面板剩余的所有剩余空间。
Dim buttonPanel As New Panel
buttonPanel.Parent = Me
buttonPanel.Height = 40
buttonPanel.Dock = DockStyle.Bottom
这是控制面板。 它的父级是表单容器。 它被粘贴到表格的底部。 高度为 40 像素。 在此控制面板内部,我们放置了所有按钮和轨迹仪。
Dim pause As 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 Visual Basic Winforms 教程的这一部分是关于控件的布局管理的。 我们实践了 Winforms 库提供的各种可能性。
Qt5 中的字符串
在本章中,我们将使用字符串。 Qt5 具有用于处理字符串的QString
类。 它非常强大,并且具有多种方法。
QString
类提供 Unicode 字符串。 它将字符串存储为 16 位QChars
。 每个QChar
对应一个 Unicode 4.0 字符。 与许多其他编程语言中的字符串不同,可以修改QString
。
在本章的示例中,我们将不需要 Qt GUI 模块。 我们将创建命令行程序。 由于默认情况下包括 Qt GUI,因此我们可以通过在项目文件中添加QT -= gui
声明来禁用它。
第一个例子
在第一个示例中,我们将使用QString
类的一些基本方法。
basic.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString a = "love";
a.append(" chess");
a.prepend("I ");
out << a << endl;
out << "The a string has " << a.count()
<< " characters" << endl;
out << a.toUpper() << endl;
out << a.toLower() << endl;
return 0;
}
在代码示例中,我们启动QString
。 我们附加并添加一些其他文字。 我们打印字符串的长度。 最后,我们以大写和小写形式显示修改后的字符串。
QString a = "love";
启动QString
。
a.append(" chess");
a.prepend("I ");
我们将文本附加和添加到初始字符串之前。 该字符串就地修改。
out << a << endl;
终端上印有I love chess
。
out << "The a string has " << a.count()
<< " characters" << endl;
count()
方法返回字符串中的字符数。 length()
和size()
方法是等效的。
out << a.toUpper() << endl;
out << a.toLower() << endl;
这两个方法返回字符串的大写和小写副本。 他们不修改字符串,而是返回新的修改后的字符串副本。
输出:
$ ./basic
I love chess
The a string has 12 characters
I LOVE CHESS
i love chess
初始化字符串
QString
可以通过多种方式初始化。
init.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString str1 = "The night train";
out << str1 << endl;
QString str2("A yellow rose");
out << str2 << endl;
std::string s1 = "A blue sky";
QString str3 = s1.c_str();
out << str3 << endl;
std::string s2 = "A thick fog";
QString str4 = QString::fromLatin1(s2.data(), s2.size());
out << str4 << endl;
char s3[] = "A deep forest";
QString str5(s3);
out << str5 << endl;
return 0;
}
我们介绍了五种初始化QString
的方法。
QString str1 = "The night train";
这是在计算机语言中初始化字符串的传统方式。
QString str2("A yellow rose");
这是启动QString
的对象方式。
std::string s1 = "A blue sky";
QString str3 = s1.c_str();
我们有一个来自 C++ 标准库的字符串对象。 我们使用其c_str()
方法来生成以空字符结尾的字符序列。 该字符数组(字符串的经典 C 表示形式)可以分配给QString
变量。
std::string s2 = "A thick fog";
QString str4 = QString::fromLatin1(s2.data(), s2.size());
在这些代码行中,我们将标准 C++ 字符串转换为QString
。 我们利用fromLatin1()
方法。 它需要一个指向字符数组的指针。 指针通过std::string
的data()
方法返回。 第二个参数是std::string
的大小。
char s3[] = "A deep forest";
QString str5(s3);
这是一个 C 字符串; 它是一个字符数组。 QString
构造器之一可以将char
数组作为参数。
输出:
$ ./init
The night train
A yellow rose
A blue sky
A thick fog
A deep forest
访问字符串元素
QString
是QChars
的序列。 可以使用[]
运算符或at()
方法访问字符串的元素。
access.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString a = "Eagle";
out << a[0] << endl;
out << a[4] << endl;
out << a.at(0) << endl;
if (a.at(5).isNull()) {
out << "Outside the range of the string" << endl;
}
return 0;
}
我们从特定的QString
打印一些单独的字符。
out << a[0] << endl;
out << a[4] << endl;
我们打印字符串的第一个和第五个元素。
out << a.at(0) << endl;
使用at()
方法,我们检索字符串的第一个字符。
if (a.at(5).isNull()) {
out << "Outside the range of the string" << endl;
}
如果我们尝试访问字符串字符范围之外的字符,则at()
方法返回null
。
输出:
$ ./access
E
e
E
Outside the range of the string
字符串长度
有三种获取字符串长度的方法。 size()
,count()
和length()
方法。 所有人都做同样的事情。 它们返回指定字符串中的字符数。
length.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString s1 = "Eagle";
QString s2 = "Eagle\n";
QString s3 = "Eagle ";
QString s4 = "орел";
out << s1.length() << endl;
out << s2.length() << endl;
out << s3.length() << endl;
out << s4.length() << endl;
return 0;
}
我们得到四个字符串的大小。
QString s2 = "Eagle\n";
QString s3 = "Eagle ";
这两个字符串中的每个字符串都有一个白色字符。
QString s4 = "орел";
此字符串由俄语字母组成。
输出:
$ ./length
5
6
6
4
从输出中我们可以看到length()
方法也计算了白色字符。
字符串构建
动态字符串构建使我们可以用实际值替换特定的控制字符。 我们使用arg()
方法进行插值。
building.cpp
#include <QTextStream>
int main() {
QTextStream out(stdout);
QString s1 = "There are %1 white roses";
int n = 12;
out << s1.arg(n) << endl;
QString s2 = "The tree is %1 m high";
double h = 5.65;
out << s2.arg(h) << endl;
QString s3 = "We have %1 lemons and %2 oranges";
int ln = 12;
int on = 4;
out << s3.arg(ln).arg(on) << endl;
return 0;
}
将要替换的标记以%
字符开头。 下一个字符是指定自变量的数字。 一个字符串可以有多个参数。 arg()
方法已重载,它可以接受整数,长数字,字符和QChar
等。
QString s1 = "There are %1 white roses";
int n = 12;
%1
是我们计划替换的标记。 我们定义了一个整数。
out << s1.arg(n) << endl;
arg()
方法采用整数。 %1
标记被n
变量的值替换。
QString s2 = "The tree is %1 m high";
double h = 5.65;
out << s2.arg(h) << endl;
这三行对于一个双数执行相同的操作。 正确的arg()
方法将被自动调用。
QString s3 = "We have %1 lemons and %2 oranges";
int ln = 12;
int on = 4;
out << s3.arg(ln).arg(on) << endl;
我们可以有多个控制字符。 %1
引用第一个参数,%2
引用第二个参数。 arg()
方法在连续的链中调用。
输出:
$ ./building
There are 12 white roses
The tree is 5.65 m high
We have 12 lemons and 4 oranges
子串
在进行文本处理时,我们需要找到普通字符串的子字符串。 我们有left()
,mid()
和right()
方法可供我们使用。
substrings.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString str = "The night train";
out << str.right(5) << endl;
out << str.left(9) << endl;
out << str.mid(4, 5) << endl;
QString str2("The big apple");
QStringRef sub(&str2, 0, 7);
out << sub.toString() << endl;
return 0;
}
我们将使用这三种方法查找给定字符串的一些子字符串。
out << str.right(5) << endl;
通过right()
方法,我们获得str
字符串的最右边五个字符。 将打印train
。
out << str.left(9) << endl;
使用left()
方法,我们获得str
字符串的最左边九个字符。 将打印night
。
out << str.mid(4, 5) << endl;
使用mid()
方法,我们从第 4 个位置开始得到 5 个字符。 打印night
。
QString str2("The big apple");
QStringRef sub(&str2, 0, 7);
QStringRef
类是QString
的只读版本。 在这里,我们创建str2
字符串一部分的QStringRef
。 第二个参数是位置,第三个参数是子字符串的长度。
输出:
$ ./substrings
train
The night
night
The big
遍历字符串
QString
由QChars
组成。 我们可以遍历QString
以访问字符串的每个元素。
looping.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString str = "There are many stars.";
foreach (QChar qc, str) {
out << qc << " ";
}
out << endl;
for (QChar *it=str.begin(); it!=str.end(); ++it) {
out << *it << " " ;
}
out << endl;
for (int i = 0; i < str.size(); ++i) {
out << str.at(i) << " ";
}
out << endl;
return 0;
}
我们展示了通过QString
的三种方法。 在将字母打印到终端时,我们在字母之间添加空格字符。
foreach (QChar qc, str) {
out << qc << " ";
}
foreach
关键字是 C++ 语言的 Qt 扩展。 关键字的第一个参数是字符串元素,第二个参数是字符串。
for (QChar *it=str.begin(); it!=str.end(); ++it) {
out << *it << " " ;
}
在此代码中,我们使用迭代器遍历字符串。
for (int i = 0; i < str.size(); ++i) {
out << str.at(i) << " ";
}
我们计算字符串的大小,并使用at()
方法访问字符串元素。
输出:
$ ./looping
T h e r e a r e m a n y s t a r s .
T h e r e a r e m a n y s t a r s .
T h e r e a r e m a n y s t a r s .
字符串比较
QString::compare()
静态方法用于比较两个字符串。 该方法返回一个整数。 如果返回的值小于零,则第一个字符串小于第二个字符串。 如果返回零,则两个字符串相等。 最后,如果返回的值大于零,则第一个字符串大于第二个字符串。 “较少”是指字符串的特定字符在字符表中位于另一个字符之前。 比较字符串的方法如下:比较两个字符串的第一个字符; 如果它们相等,则比较以下两个字符,直到找到一些不同的字符或发现所有字符都匹配为止。
comparing.cpp
#include <QTextStream>
#define STR_EQUAL 0
int main(void) {
QTextStream out(stdout);
QString a = "Rain";
QString b = "rain";
QString c = "rain\n";
if (QString::compare(a, b) == STR_EQUAL) {
out << "a, b are equal" << endl;
} else {
out << "a, b are not equal" << endl;
}
out << "In case insensitive comparison:" << endl;
if (QString::compare(a, b, Qt::CaseInsensitive) == STR_EQUAL) {
out << "a, b are equal" << endl;
} else {
out << "a, b are not equal" << endl;
}
if (QString::compare(b, c) == STR_EQUAL) {
out << "b, c are equal" << endl;
} else {
out << "b, c are not equal" << endl;
}
c.chop(1);
out << "After removing the new line character" << endl;
if (QString::compare(b, c) == STR_EQUAL) {
out << "b, c are equal" << endl;
} else {
out << "b, c are not equal" << endl;
}
return 0;
}
我们将使用compare()
方法进行区分大小写和不区分大小写的比较。
#define STR_EQUAL 0
为了更好的代码清晰度,我们定义STR_EQUAL
常量。
QString a = "Rain";
QString b = "rain";
QString c = "rain\n";
我们将比较这三个字符串。
if (QString::compare(a, b) == STR_EQUAL) {
out << "a, b are equal" << endl;
} else {
out << "a, b are not equal" << endl;
}
我们比较a
和b
字符串,它们不相等。 它们的第一个字符不同。
if (QString::compare(a, b, Qt::CaseInsensitive) == STR_EQUAL) {
out << "a, b are equal" << endl;
} else {
out << "a, b are not equal" << endl;
}
如果不区分大小写,则字符串相等。 Qt::CaseInsensitive
使比较大小写不敏感。
c.chop(1);
chop()
方法从c
字符串中删除最后一个字符。 现在b
和c
字符串相等。
输出:
$ ./comparing
a, b are not equal
In case insensitive comparison:
a, b are equal
b, c are not equal
After removing the new line character
b, c are equal
转换字符串
字符串通常需要转换为其他数据类型,反之亦然。 toInt()
,toFloat()
和toLong()
是将字符串转换为整数,浮点数和长整数的三种QString
方法。 (还有更多这样的方法。)setNum()
方法将各种数字数据类型转换为字符串。 该方法已重载,并且自动调用了正确的方法。
Output
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString s1 = "12";
QString s2 = "15";
QString s3, s4;
out << s1.toInt() + s2.toInt() << endl;
int n1 = 30;
int n2 = 40;
out << s3.setNum(n1) + s4.setNum(n2) << endl;
return 0;
}
在示例中,我们将两个字符串转换为整数并将其相加。 然后,我们将两个整数转换为字符串并将其连接起来。
out << s1.toInt() + s2.toInt() << endl;
toInt()
方法将字符串转换为整数。 我们添加两个转换为字符串的数字。
out << s3.setNum(n1) + s4.setNum(n2) << endl;
在这种情况下,setNum()
方法将整数转换为字符串。 我们连接两个字符串。
输出:
$ ./converts
27
3040
字符
字符分为各种类别:数字,字母,空格和标点符号。 每个QString
都由QChar
组成。 QChar
具有isDigit()
,isLetter()
,isSpace()
和isPunct()
方法来执行作业。
letters.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
int digits = 0;
int letters = 0;
int spaces = 0;
int puncts = 0;
QString str = "7 white, 3 red roses.";
foreach(QChar s, str) {
if (s.isDigit()) {
digits++;
} else if (s.isLetter()) {
letters++;
} else if (s.isSpace()) {
spaces++;
} else if (s.isPunct()) {
puncts++;
}
}
out << QString("There are %1 characters").arg(str.count()) << endl;
out << QString("There are %1 letters").arg(letters) << endl;
out << QString("There are %1 digits").arg(digits) << endl;
out << QString("There are %1 spaces").arg(spaces) << endl;
out << QString("There are %1 punctuation characters").arg(puncts) << endl;
return 0;
}
在示例中,我们定义了一个简单的句子。 我们将计算句子中的数字,字母,空格和标点符号的数量。
int digits = 0;
int letters = 0;
int spaces = 0;
int puncts = 0;
我们为每个字符类别定义一个整数变量。
QString str = "7 white, 3 red roses.";
这是要检查的句子。
foreach(QChar s, str) {
if (s.isDigit()) {
digits++;
} else if (s.isLetter()) {
letters++;
} else if (s.isSpace()) {
spaces++;
} else if (s.isPunct()) {
puncts++;
}
}
我们使用foreach
关键字来浏览QString
。 每个元素都是QChar
。 我们使用QChar
类的方法来确定字符的类别。
out << QString("There are %1 characters").arg(str.count()) << endl;
out << QString("There are %1 letters").arg(letters) << endl;
out << QString("There are %1 digits").arg(digits) << endl;
out << QString("There are %1 spaces").arg(spaces) << endl;
out << QString("There are %1 punctuation characters").arg(puncts) << endl;
使用字符串插值,我们将数字打印到终端。
输出:
$ ./letters
There are 21 characters
There are 13 letters
There are 2 digits
There are 4 spaces
There are 2 punctuation characters
修改字符串
某些方法(例如toLower()
方法)返回原始字符串的新修改副本。 其他方法可就地修改字符串。 我们将介绍其中的一些。
modify.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString str = "Lovely";
str.append(" season");
out << str << endl;
str.remove(10, 3);
out << str << endl;
str.replace(7, 3, "girl");
out << str << endl;
str.clear();
if (str.isEmpty()) {
out << "The string is empty" << endl;
}
return 0;
}
我们描述了四种就地修改字符串的方法。
str.append(" season");
append()
方法在字符串的末尾添加一个新字符串。
str.remove(10, 3);
remove()
方法从位置 10 开始从字符串中删除 3 个字符。
str.replace(7, 3, "girl");
replace()
方法用指定的字符串替换从7
位置开始的3
字符。
str.clear();
clear()
方法清除字符串。
输出:
$ ./modify
Lovely season
Lovely sea
Lovely girl
The string is empty
在本章中,我们使用了 Qt5 中的字符串。
对齐字符串
拥有整洁的输出是常见的要求。 我们可以使用leftJustified()
和rightJustified()
方法来对齐字符串。
right_align.cpp
#include <QTextStream>
int main(void) {
QTextStream out(stdout);
QString field1 = "Name: ";
QString field2 = "Occupation: ";
QString field3 = "Residence: ";
QString field4 = "Marital status: ";
int width = field4.size();
out << field1.rightJustified(width, ' ') << "Robert\n";
out << field2.rightJustified(width, ' ') << "programmer\n";
out << field3.rightJustified(width, ' ') << "New York\n";
out << field4.rightJustified(width, ' ') << "single\n";
return 0;
}
该示例将字段字符串向右对齐。
int width = field4.size();
我们计算最宽字符串的大小。
out << field1.rightJustified(width, ' ') << "Robert\n";
rightJustified()
方法返回具有width
字符的字符串。 如果字符串较短,则其余部分将使用提供的字符填充。 在我们的情况下,它是一个空格字符。
输出:
$ ./right_align
Name: Robert
Occupation: programmer
Residence: New York
Marital status: single
转义字符
Qt5 具有toHtmlEscaped()
方法,该方法将纯文本字符串转换为将 HTML 元字符<
,>
,&
和"
替换为 HTML 命名实体的 HTML 字符串。
$ cat cprog.c
#include <stdio.h>
int main(void) {
for (int i=1; i<=10; i++) {
printf("Bottle %d\n", i);
}
}
此 C 包含 HTML 元字符。
html_escape.cpp
#include <QTextStream>
#include <QFile>
int main(void) {
QTextStream out(stdout);
QFile file("cprog.c");
if (!file.open(QIODevice::ReadOnly)) {
qWarning("Cannot open file for reading");
return 1;
}
QTextStream in(&file);
QString allText = in.readAll();
out << allText.toHtmlEscaped() << endl;
file.close();
return 0;
}
该示例读取一个 C 程序,并将元字符替换为其命名的实体。
输出:
$ ./html_escape
#include <stdio.h>
int main(void) {
for (int i=1; i<=10; i++) {
printf("Bottle %d\n", i);
}
}
在本章中,我们使用了 Qt5 中的字符串。
基本控制
Visual Basic Winforms 编程教程的这一部分将介绍基本控件。
Winforms 控件是应用的基本构建块。 Winforms 具有广泛的各种控件。 按钮,复选框,滑块,列表框等。程序员完成工作所需的一切。 在本教程的这一部分中,我们将描述几个有用的控件。
Label
Label
是用于显示文本或图像的简单控件。 它没有得到关注。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows lyrics of a song
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Dim lyrics As String = "Meet you downstairs in the bar and heard" & vbNewLine & _
"your rolled up sleeves and your skull t-shirt" & vbNewLine & _
"You say why did you do it with him today?" & vbNewLine & _
"and sniff me out like I was Tanqueray" & vbNewLine & _
"" & vbNewLine & _
"cause you're my fella, my guy" & vbNewLine & _
"hand me your stella and fly" & vbNewLine & _
"by the time I'm out the door" & vbNewLine & _
"you tear men down like Roger Moore" & vbNewLine & _
"" & vbNewLine & _
"I cheated myself" & vbNewLine & _
"like I knew I would" & vbNewLine & _
"I told ya, I was trouble" & vbNewLine & _
"you know that I'm no good"
Public Sub New()
Me.Text = "You know I'm no Good"
Me.Size = New Size(300, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim font As New Font("Serif", 10)
Dim label As New Label
label.Parent = Me
label.Text = lyrics
label.Font = font
label.Location = New Point(10, 10)
label.Size = New Size (290, 290)
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在我们的示例中,我们在标签控件中显示歌曲的歌词。
Dim lyrics As String = "Meet you downstairs in the bar and heard" & vbNewLine & _
"your rolled up sleeves and your skull t-shirt" & vbNewLine & _
...
我们定义了多行文字。 与 C# ,Python 或 Ruby 不同,没有简单的结构可以用 Visual Basic 语言创建多行文本。 若要在 Visual Basic 中创建多行文本,我们使用vbNewLine
打印常量,+
连接字符和_
行终止字符。
Dim label As New Label
Label
控件已创建。
label.Text = lyrics
我们为标签设置文本。
Dim font As New Font("Serif", 10)
...
label.Font = font
标签文本的字体设置为 Serif,10px。
图:Label
CheckBox
CheckBox
是具有两个状态的控件:开和关。 它是带有标签或图像的盒子。 如果选中了CheckBox
,则在方框中用勾号表示。 CheckBox
可用于在启动时显示/隐藏启动屏幕,切换工具栏的可见性等。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program toggles the title of the
' window with the CheckBox control
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "CheckBox"
Me.Size = New Size(220, 170)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim cb As New CheckBox
cb.Parent = Me
cb.Location = New Point(30, 30)
cb.Text = "Show Title"
cb.Checked = True
AddHandler cb.CheckedChanged, AddressOf Me.OnChanged
End Sub
Private Sub OnChanged(ByVal sender As Object, ByVal e As EventArgs)
If sender.Checked
Text = "CheckBox"
Else
Text = ""
End If
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们的代码示例根据窗口的状态显示或隐藏窗口的标题。
Dim cb As New CheckBox
CheckBox
控件已创建。
cb.Text = "Show Title"
cb.Checked = True
当应用启动时,我们显示标题。 然后将CheckBox
控件设置为选中状态。
AddHandler cb.CheckedChanged, AddressOf Me.OnChanged
当我们单击CheckBox
控件时,将触发CheckedChanged
事件。 我们用OnChanged
方法对这个特定事件做出反应。
If sender.Checked
Text = "CheckBox"
Else
Text = ""
End If
在这里,我们切换窗口的标题。
图:CheckBox
ComboBox
ComboBox
是一个组合了按钮或可编辑字段和下拉列表的控件。 用户可以从下拉列表中选择一个值,该列表应用户的要求出现。 如果使组合框可编辑,则组合框将包含一个可编辑字段,用户可以在其中输入值。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program, we use the ComboBox
' control to select an option.
' The selected option is shown in the
' Label component.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private Dim label As Label
Public Sub New
Me.Text = "ComboBox"
Me.Size = New Size(240, 240)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim cb As New ComboBox
cb.Parent = Me
cb.Location = New Point(50, 30)
cb.Items.AddRange(New Object() {"Ubuntu", _
"Mandriva", _
"Red Hat", _
"Fedora", _
"Gentoo"})
label = New Label
label.Location = New Point(50, 140)
label.Parent = Me
label.Text = "..."
AddHandler cb.SelectionChangeCommitted, AddressOf Me.OnChanged
End Sub
Private Sub OnChanged(ByVal sender As Object, ByVal e As EventArgs)
label.Text = sender.Text
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们的代码编程示例显示了一个包含五个项目的组合框。 所选项目显示在标签控件中。
Dim cb As New ComboBox
ComboBox
控件已创建。
cb.Items.AddRange(New Object() {"Ubuntu", _
"Mandriva", _
"Red Hat", _
"Fedora", _
"Gentoo"})
ComboBox
控件中充满了项目。
AddHandler cb.SelectionChangeCommitted, AddressOf Me.OnChanged
如果我们从组合框中选择一个项目,则会触发SelectionChangeCommitted
事件。
Private Sub OnChanged(ByVal sender As Object, ByVal e As EventArgs)
label.Text = sender.Text
End Sub
在这里,将从组合框中选择的文本复制到标签。
图:ComboBox
MonthCalendar
在下一个示例中,我们将显示MonthCalendar
控件。 MonthCalendar 控件允许用户使用视觉显示选择日期。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program, we use the MonthCalendar
' control to select a date.
' The selected date is shown in the
' Label control.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private Dim label As Label
Public Sub New
Me.Text = "MonthCalendar"
Me.Size = New Size(240, 240)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim calendar As New MonthCalendar
calendar.Parent = Me
calendar.Location = New Point(20, 20)
label = New Label
label.Location = New Point(40, 170)
label.Parent = Me
Dim dt As DateTime = calendar.SelectionStart
label.Text = dt.Month & "/" & dt.Day & "/" & dt.Year
AddHandler calendar.DateSelected, AddressOf Me.OnSel
End Sub
Private Sub OnSel(ByVal sender As Object, ByVal e As DateRangeEventArgs)
Dim dt As DateTime = sender.SelectionStart
label.Text = dt.Month & "/" & dt.Day & "/" & dt.Year
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在示例中,我们显示了MonthCalendar
和Label
。 后者显示当前选择的日期。
Private Sub OnSel(ByVal sender As Object, ByVal e As DateRangeEventArgs)
Dim dt As DateTime = sender.SelectionStart
label.Text = dt.Month & "/" & dt.Day & "/" & dt.Year
End Sub
当我们从MonthCalendar
中选择一个日期时,就会调用OnSel
方法。 SelectionStart
属性获取所选日期范围的开始日期。
图:MonthCalendar
TextBox
TextBox
控件用于显示或接受某些文本。 文本可以是单行或多行。 此控件还可以进行密码屏蔽。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program demonstrates the
' TextBox control. Text entered in the TextBox
' control is shown in a Label control.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private Dim label As Label
Public Sub New
Me.Text = "TextBox"
Me.Size = New Size(250, 200)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
label = New Label
label.Parent = Me
label.Text = "..."
label.Location = New Point(60, 40)
label.AutoSize = True
Dim tbox As New TextBox
tbox.Parent = Me
tbox.Location = New Point(60, 100)
AddHandler tbox.KeyUp, AddressOf Me.OnKeyUp
End Sub
Private Sub OnKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs)
Me.label.Text = sender.Text
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
本示例显示一个文本框和一个标签。 我们在文本框中键入的文本将立即显示在标签控件中。
label = New Label
...
label.AutoSize = True
Label
控件已创建。 AutoSize
属性确保Label
增长以显示文本。
Dim tbox As New TextBox
...
AddHandler tbox.KeyUp, AddressOf Me.OnKeyUp
我们插入KeyUp
事件。 释放按键时,将调用OnKeyUp
方法。
Private Sub OnKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs)
Me.label.Text = sender.Text
End Sub
在OnKeyUp
方法中,我们使用文本框控件中的文本更新了标签控件。
图:TextBox
我们已经完成了 Visual Basic Winforms 教程的这一章,专门讨论基本控件。
进阶控件
在 Visual Basic Winforms 教程的这一部分中,我们介绍一些更高级的控件。 即ListBox
,ListView
和TreeView
控件。
ListBox
ListBox
控件用于显示项目列表。 用户可以通过单击选择一个或多个项目。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows how to use
' the ListBox control. Item selected from
' the ListBox is shown in the statusbar.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private sb As StatusBar
Public Sub New
Me.Text = "ListBox"
Me.Size = New Size(210, 210)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim lb As New ListBox
lb.Parent = Me
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
sb = New StatusBar
sb.Parent = Me
AddHandler lb.SelectedIndexChanged, AddressOf Me.OnChanged
End Sub
Private Sub OnChanged(ByVal sender As Object, ByVal e As EventArgs)
sb.Text = sender.SelectedItem
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们的示例显示了一个具有六个名称的列表框。 所选项目显示在状态栏中。
Dim lb As New ListBox
lb.Parent = Me
ListBox
控件已创建。
lb.Items.Add("Jessica")
这就是我们向ListBox
控件添加新项目的方式。 该控件具有Items
属性。 该属性是对列表框中项目列表的引用。 使用此引用,我们可以添加,删除或获取列表框中的项目数。
AddHandler lb.SelectedIndexChanged, AddressOf Me.OnChanged
当我们选择一个项目时,会触发SelectedIndexChanged
事件。
sb.Text = sender.SelectedItem
在OnChanged
方法内部,我们将所选文本设置为状态栏。
图:ListBox
ListView
ListView
控件用于显示项目集合。 它是比ListBox
控件更复杂的控件。 它可以在各种视图中显示数据,主要用于在多列视图中显示数据。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows how to use
' the ListView control. Item selected from
' the ListView is shown in the statusbar.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Collections.Generic
Public Class Actress
Public Dim m_name As String
Public Dim m_year As Integer
Public Sub New(ByVal name As String, ByVal year As Integer)
Me.m_name = name
Me.m_year = year
End Sub
End Class
Public Class WinVBApp
Inherits Form
Private Dim sb As StatusBar
Private Dim lv As ListView
Public Sub New
Me.Text = "ListView"
Me.Size = New Size(350, 300)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim actresses As New List(Of 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))
Dim name As New ColumnHeader
name.Text = "Name"
name.Width = -1
Dim year As New ColumnHeader
year.Text = "Year"
Me.SuspendLayout
lv = New ListView
lv.Parent = Me
lv.FullRowSelect = True
lv.GridLines = True
lv.AllowColumnReorder = True
lv.Sorting = SortOrder.Ascending
lv.Columns.AddRange(New ColumnHeader() {name, year})
For Each act As Actress In actresses
Dim item As New ListViewItem
item.Text = act.m_name
item.SubItems.Add(act.m_year.ToString())
lv.Items.Add(item)
Next
lv.Dock = DockStyle.Fill
sb = New StatusBar
sb.Parent = Me
lv.View = View.Details
Me.ResumeLayout
AddHandler lv.Click, AddressOf Me.OnChanged
AddHandler lv.ColumnClick, AddressOf Me.OnColumnClick
End Sub
Private Sub OnChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim name As String = lv.SelectedItems(0).SubItems(0).Text
Dim born As String = lv.SelectedItems(0).SubItems(1).Text
sb.Text = name & ", " & born
End Sub
Private Sub OnColumnClick(ByVal sender As Object, _
ByVal e As ColumnClickEventArgs)
If sender.Sorting = SortOrder.Ascending
sender.Sorting = SortOrder.Descending
Else
sender.Sorting = SortOrder.Ascending
End If
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在我们的示例中,我们有一个包含两列的列表视图。 在第一列中,我们显示女演员的名字。 在第二个他们的出生日期。 数据存储在List
集合中。 通过选择一行,一行中的数据将显示在状态栏中。 另外,通过单击列标题,可以对数据进行排序。
Public Class Actress
...
End Class
我们使用Actress
类存储数据。
Dim actresses As New List(Of Actress)
actresses.Add(New Actress("Jessica Alba", 1981))
actresses.Add(New Actress("Angelina Jolie", 1975))
...
我们创建项目并在项目中填充项目。
Dim name As New ColumnHeader
name.Text = "Name"
name.Width = -1
对于列表视图中的每一列,我们创建一个ColumnHeader
。 通过将Width
设置为 -1,列的宽度等于列中最长的项目。
lv = New ListView
lv.Parent = Me
创建一个ListView
控件。
lv.FullRowSelect = True
lv.GridLines = True
lv.AllowColumnReorder = True
lv.Sorting = SortOrder.Ascending
在这里,我们设置控件的四个属性。 该代码行支持全行选择,显示网格线,通过拖动列对列进行重新排序并以升序对数据进行排序。
lv.Columns.AddRange(New ColumnHeader() {name, year})
在这里,我们将两个ColumnHeader
添加到ListView
控件中。
For Each act As Actress In actresses
Dim item As New ListViewItem
item.Text = act.m_name
item.SubItems.Add(act.m_year.ToString())
lv.Items.Add(item)
Next
此循环填充listview
控件。 每行都作为ListViewItem
类添加到列表视图。
lv.View = View.Details
ListView
控件可以具有不同的视图。 不同的视图以不同的方式显示数据。
Dim name As String = lv.SelectedItems(0).SubItems(0).Text
Dim born As String = lv.SelectedItems(0).SubItems(1).Text
sb.Text = name & ", " & born
在OnChanged
方法内部,我们从选定的行中获取数据并将其显示在状态栏上。
If sender.Sorting = SortOrder.Ascending
sender.Sorting = SortOrder.Descending
Else
sender.Sorting = SortOrder.Ascending
End If
在这里,我们切换列的排序顺序。
图:ListView
TreeView
TreeView
控件显示项目的分层集合。 此控件中的每个项目都由TreeNode
对象表示。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows how to use
' the ListBox control. Item selected from
' the ListBox is shown in the statusbar.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private Dim sb As StatusBar
Private Dim WithEvents tv As TreeView
Public Sub New
Me.Text = "TreeView"
Me.Size = New Size(250, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
tv = New TreeView
Dim root As New TreeNode
root.Text = "Languages"
Dim child1 As New TreeNode
child1.Text = "Python"
Dim child2 As New TreeNode
child2.Text = "Ruby"
Dim child3 As New TreeNode
child3.Text = "Java"
root.Nodes.AddRange(New TreeNode() {child1, child2, child3})
tv.Parent = Me
tv.Nodes.Add(root)
tv.Dock = DockStyle.Fill
sb = New StatusBar
sb.Parent = Me
End Sub
Private Sub OnSelected(ByVal sender As Object, _
ByVal e As TreeViewEventArgs) Handles tv.AfterSelect
sb.Text = e.Node.Text
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
这是TreeView
控件的非常简单的演示。 我们有一个根项目和三个子项。
tv = New TreeView
我们创建TreeView
控件。
Dim root As New TreeNode
root.Text = "Languages"
...
tv.Nodes.Add(root)
在这里,我们创建一个根节点。
Dim child1 As New TreeNode
child1.Text = "Python"
子节点以类似的方式创建。
root.Nodes.AddRange(New TreeNode() {child1, child2, child3})
子节点插入到根节点的Nodes
属性中。
Private Sub OnSelected(ByVal sender As Object, _
ByVal e As TreeViewEventArgs) Handles tv.AfterSelect
sb.Text = e.Node.Text
End Sub
处理事件的另一种方法是使用Handles
关键字。
图:TreeView
在 Visual Basic Winforms 教程的这一部分中,我们介绍了 Winforms 库中提供的三个高级控件。
菜单和工具栏
在 Visual Basic Winforms 教程的这一部分中,我们将讨论菜单和工具栏。
菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。
简单菜单
在第一个示例中,我们创建一个简单的菜单。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows a simple
' menu. It has one action, which
' will terminate the program, when
' selected.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Simple menu"
Me.Size = New Size(220, 170)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim ms As New MenuStrip
ms.Parent = Me
Dim fileItem As New ToolStripMenuItem("&File")
Dim exitItem As New ToolStripMenuItem("&Exit", Nothing, _
New EventHandler(AddressOf OnExit))
exitItem.ShortcutKeys = Keys.Control Or Keys.X
fileItem.DropDownItems.Add(exitItem)
ms.Items.Add(fileItem)
MainMenuStrip = ms
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在我们的示例中,我们有一个菜单栏和一个菜单。 菜单内有一个菜单项。 如果选择菜单项,则应用关闭。
注意如何关闭应用。 我们可以使用 Ctrl + X
快捷方式或按 Alt
, F
, E
键关闭它 。
Dim ms As New MenuStrip
MenuStrip
为我们的表单创建一个菜单系统。 我们将ToolStripMenuItem
对象添加到MenuStrip
中,这些对象代表菜单结构中的各个菜单命令。 每个ToolStripMenuItem
可以是您的应用的命令,也可以是其他子菜单项的父菜单。
Dim fileItem As New ToolStripMenuItem("&File")
在这里,我们创建一个文件菜单。
Dim exitItem As New ToolStripMenuItem("&Exit", Nothing, _
New EventHandler(AddressOf OnExit))
此行创建退出菜单项。
exitItem.ShortcutKeys = Keys.Control Or Keys.X
我们提供了退出菜单项的快捷方式。
fileItem.DropDownItems.Add(exitItem)
退出菜单项将添加到菜单对象的下拉项中。
ms.Items.Add(fileItem)
在这里,我们将菜单对象添加到菜单栏中。
MainMenuStrip = ms
MenuStrip
已插入表格。
图:简单菜单
子菜单
每个菜单项也可以有一个子菜单。 这样,我们可以将类似的命令分组。 例如,我们可以将隐藏/显示各种工具栏(例如个人栏,地址栏,状态栏或导航栏)的命令放置在称为工具栏的子菜单中。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a submenu
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Submenu"
Me.Size = New Size(380, 220)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim ms As New MenuStrip
ms.Parent = Me
Dim fileItem As New ToolStripMenuItem("&File")
Dim exitItem As New ToolStripMenuItem("&Exit", Nothing, _
New EventHandler(AddressOf OnExit))
exitItem.ShortcutKeys = Keys.Control Or Keys.X
Dim import As New ToolStripMenuItem
import.Text = "Import"
Dim temp As 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)
fileItem.DropDownItems.Add(import)
fileItem.DropDownItems.Add(exitItem)
ms.Items.Add(fileItem)
Me.MainMenuStrip = ms
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在此示例中,我们创建一个子菜单。 子菜单导入具有三个菜单项。
Dim import As New ToolStripMenuItem
import.Text = "Import"
ToolStripMenuItem
可以是菜单或菜单项。 在这里它将作为子菜单。
Dim temp As New ToolStripMenuItem
temp.Text = "Import newsfeed list..."
import.DropDownItems.Add(temp)
在这里,我们创建一个菜单项并将其添加到“导入”子菜单。
图:子菜单
检查菜单项
下一个代码示例演示如何创建选中的菜单项。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a checked
' menu
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Dim sb As Statusbar
Public Sub New
Me.Text = "Check menu item"
Me.Size = New Size(380, 220)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
sb = New StatusBar
sb.Parent = Me
sb.Text = "Ready"
Dim mainMenu As New MainMenu
Dim file As MenuItem = mainMenu.MenuItems.Add("&File")
file.MenuItems.Add(New MenuItem("E&xit", _
New EventHandler(AddressOf OnExit), Shortcut.CtrlX))
Dim view As MenuItem = mainMenu.MenuItems.Add("&View")
Dim viewStatusBar As New MenuItem("View StatusBar")
viewStatusBar.Checked = True
view.MenuItems.Add(viewStatusBar)
Me.Menu = mainMenu
AddHandler viewStatusBar.Click, AddressOf Me.ToggleStatusBar
End Sub
Private Sub ToggleStatusBar(ByVal sender As Object, ByVal e As EventArgs)
Dim check As Boolean = sender.Checked
If check
sb.Visible = False
sender.Checked = False
Else
sb.Visible = True
sender.Checked = True
End If
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们有两个菜单。 文件和查看。 “查看”菜单具有一个菜单项,用于切换状态栏的可见性。
Dim mainMenu As New MainMenu
在此示例中,我们使用MainMenu
控件。 要创建菜单栏,我们可以使用MainMenu
或MenuStrip
控件。 后者具有一些附加功能。
viewStatusBar.Checked = True
默认情况下会选中此菜单项,因为状态栏从应用的开始就可见。
Dim check As Boolean = sender.Checked
If check
sb.Visible = False
sender.Checked = False
Else
sb.Visible = True
sender.Checked = True
End If
我们确定菜单项是否被选中。 我们根据check
值显示和隐藏状态栏和复选标记。
图:选中菜单项
图像,分隔符
我们将进一步增强对MenuStrip
控件的了解。 我们将创建一个带有图像的菜单项,并显示如何使用分隔符将其分开。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program shows how to add images and
' separators to menu items
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "MenuStrip"
Me.Size = New Size(250, 200)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim menuStrip As New MenuStrip
Dim titem1 As New ToolStripMenuItem("File")
menuStrip.Items.Add(titem1)
Dim titem2 As New ToolStripMenuItem("Tools")
menuStrip.Items.Add(titem2)
Dim subm1 As New ToolStripMenuItem("New")
subm1.Image = Image.FromFile("new.png")
titem1.DropDownItems.Add(subm1)
Dim subm2 As New ToolStripMenuItem("Open")
subm2.Image = Image.FromFile("open.png")
titem1.DropDownItems.Add(subm2)
titem1.DropDownItems.Add(New ToolStripSeparator)
Dim subm3 As New ToolStripMenuItem("Exit")
subm3.Image = Image.FromFile("exit.png")
titem1.DropDownItems.Add(subm3)
AddHandler subm3.Click, AddressOf Me.OnExit
Controls.Add(menuStrip)
MainMenuStrip = menuStrip
End Sub
Private Sub OnExit(ByVal sender As Object, ByVal e As EventArgs)
Me.Close
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们的代码示例中有两个菜单。 文件和工具。 在文件中,我们有三个带有图像的菜单项。 我们还有一个分隔符。 在此示例中,PNG 图像必须位于当前工作目录中。
Dim subm1 As 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
属性。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a toolbar
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Dim sb As Statusbar
Public Sub New
Me.Text = "Toolbar"
Me.Size = New Size(250, 220)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim toolBar As New ToolBar
toolBar.Parent = Me
Dim toolBarIcons As New ImageList
Dim saveb As New ToolBarButton
Dim exitb As New ToolBarButton
saveb.ImageIndex = 0
saveb.Tag = "Save"
exitb.ImageIndex = 1
exitb.Tag = "Exit"
toolBar.ImageList = toolBarIcons
toolBar.ShowToolTips = True
toolBar.Buttons.AddRange(New ToolBarButton() {saveb, exitb})
toolBarIcons.Images.Add(New Icon("new.ico"))
toolBarIcons.Images.Add(New Icon("exit.ico"))
AddHandler toolBar.ButtonClick, AddressOf Me.OnClicked
End Sub
Private Sub OnClicked(ByVal sender As Object, _
ByVal e As ToolBarButtonClickEventArgs)
If e.Button.Tag.Equals("Exit")
Me.Close
End If
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在我们的示例中,我们在工具栏上显示了两个按钮。
Dim toolBar As New ToolBar
在这里,我们创建ToolBar
控件。
toolBar.ImageList = toolBarIcons
创建图像列表。
Dim saveb As New ToolBarButton
Dim exitb As New ToolBarButton
这是两个工具栏按钮。
saveb.ImageIndex = 0
我们确定图像列表中的哪个图标将用于保存工具栏按钮。
toolBar.Buttons.AddRange(New ToolBarButton() {saveb, exitb})
ToolBarButton
控件已添加到工具栏。
toolBarIcons.Images.Add(New Icon("new.ico"))
toolBarIcons.Images.Add(New Icon("exit.ico"))
图标将添加到图像列表。
If e.Button.Tag.Equals("Exit")
Me.Close
End If
如果按钮的标签等于"Exit"
,我们将关闭该应用。
图:工具栏
Visual Basic Winforms 教程的这一部分是关于菜单和工具栏的。
对话框
在 Visual Basic Winforms 教程的这一部分中,我们将讨论对话框。
对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。
基本上有两种类型的对话框。 预定义对话框和自定义对话框。
FolderBrowserDialog
此对话框提示用户选择一个文件夹。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we select a directory with a
' FolderBrowser dialog. The selected directory's
' name is shown in the statusbar.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Dim statusbar As StatusBar
Public Sub New
Me.Text = "FolderBrowserDialog"
Me.Size = New Size(300, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim toolbar As New ToolBar
Dim open As New ToolBarButton
statusbar = New StatusBar
statusbar.Parent = Me
toolbar.Buttons.Add(open)
Me.Controls.Add(toolbar)
AddHandler toolbar.ButtonClick, AddressOf Me.OnClicked
End Sub
Private Sub OnClicked(ByVal sender As Object, _
ByVal e As ToolBarButtonClickEventArgs)
Dim dialog As New FolderBrowserDialog
If dialog.ShowDialog(Me) = DialogResult.OK
statusbar.Text = dialog.SelectedPath
End If
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们有一个工具栏和一个工具栏按钮。 点击按钮,FolderBrowserDialog
出现在屏幕上。 所选文件夹的名称显示在状态栏中。
Dim dialog As New FolderBrowserDialog
FolderBrowserDialog
已创建。
If dialog.ShowDialog(Me) = DialogResult.OK
statusbar.Text = dialog.SelectedPath
End If
ShowDialog
方法在屏幕上显示对话框。 如果单击对话框的“确定”按钮,则所选的目录路径将显示在状态栏上。
图:FolderBrowserDialog
ColorDialog
此对话框显示可用的颜色以及使用户能够定义自定义颜色的控件。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we use the ColorDialog
' to change a color of a rectangle
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private col As Color
Private Const rectWidth As Integer = 100
Private Const rectHeight As Integer = 100
Private Dim r As Rectangle
Public Sub New
Me.Text = "ColorDialog"
Me.Size = New Size(300, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim tbar As New ToolBar
Dim open As New ToolBarButton
col = Color.Blue
tbar.Buttons.Add(open)
Me.LocateRect
Me.SetStyle(ControlStyles.ResizeRedraw, True)
Controls.Add(tbar)
AddHandler Me.Paint, AddressOf Me.OnPaint
AddHandler tbar.ButtonClick, AddressOf Me.OnClicked
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
Me.LocateRect
Dim brsh As New SolidBrush(col)
g.FillRectangle(brsh, r)
End Sub
Private Sub OnClicked(ByVal sender As Object, _
ByVal e As ToolBarButtonClickEventArgs)
Dim dialog As New ColorDialog
If dialog.ShowDialog(Me) = DialogResult.OK
col = dialog.Color
Me.Invalidate
End If
End Sub
Private Sub LocateRect
Dim x As Integer = (Me.ClientSize.Width - rectWidth) / 2
Dim y As Integer = (Me.ClientSize.Height - rectHeight) / 2
r = New Rectangle(x, y, rectWidth, rectHeight)
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在此代码示例中,我们使用ColorDialog
为位于窗体控件中间的矩形选择颜色。
col = Color.Blue
开始时,矩形的颜色是蓝色。 我们使用col
变量来确定矩形的颜色。
Dim dialog As New ColorDialog
ColorDialog
已创建。
If dialog.ShowDialog(Me) = DialogResult.OK
col = dialog.Color
Me.Invalidate
End If
该代码显示颜色对话框。 如果单击“确定”按钮,则将获得选定的颜色并调用Invalidate
方法。 该方法会使控件的整个表面无效,并使控件重画。 结果是用新的颜色值绘制了矩形。
图:ColorDialog
FontDialog
FontDialog
用于选择字体。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we use the FontDialog
' to change a font of a label
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private Dim txt As Label
Public Sub New
Me.Text = "FontDialog"
Me.Size = New Size(300, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim tbar As New ToolBar
tbar.Parent = Me
Dim open As New ToolBarButton
tbar.Buttons.Add(open)
txt = New Label
txt.Parent = Me
txt.Text = "Winforms tutorial"
Me.LocateText
txt.AutoSize = True
AddHandler Me.Resize, AddressOf Me.OnResize
AddHandler tbar.ButtonClick, AddressOf Me.OnClicked
End Sub
Private Sub OnClicked(ByVal sender As Object, _
ByVal e As ToolBarButtonClickEventArgs)
Dim dialog As New FontDialog
If dialog.ShowDialog(Me) = DialogResult.OK
txt.Font = dialog.Font
Me.LocateText
End If
End Sub
Private Sub LocateText
txt.Top = (Me.ClientSize.Height - txt.Height) / 2
txt.Left = (Me.ClientSize.Width - txt.Width) / 2
End Sub
Private Sub OnResize(ByVal sender As Object, ByVal e As EventArgs)
Me.LocateText
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们在表单控件的中间绘制一些文本。 我们使用字体对话框更改此文本的字体。
Dim dialog As New FontDialog
FontDialog
已创建。
If dialog.ShowDialog(Me) = DialogResult.OK
txt.Font = dialog.Font
Me.LocateText
End If
单击“确定”按钮时,将为Label
控件设置新选择的字体。 由于文本的大小会随着字体的变化而变化,因此我们必须调用LocateText
方法,该方法将文本定位在表单控件的中间。
图:FontDialog
OpenDialog
此对话框用于打开文件。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we use the OpenDialog to
' open a file and show its contents in
' a TextBox control
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.IO
Public Class WinVBApp
Inherits Form
Private txtBox As TextBox
Public Sub New
Me.Text = "OpenDialog"
Me.Size = New Size(300, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
Dim tbar As New ToolBar
tbar.Parent = Me
Dim open As New ToolBarButton
tbar.Buttons.Add(open)
txtBox = New TextBox
txtBox.Parent = Me
txtBox.Multiline = True
txtBox.ScrollBars = ScrollBars.Both
txtBox.WordWrap = False
txtBox.Parent = Me
txtBox.Dock = DockStyle.Fill
AddHandler tbar.ButtonClick, AddressOf Me.OnClicked
End Sub
Private Sub OnClicked(ByVal sender As Object, _
ByVal e As ToolBarButtonClickEventArgs)
Dim dia As New OpenFileDialog
dia.Filter = "VB files (*.vb)|*.vb"
If dia.ShowDialog(Me) = DialogResult.OK
Dim reader As New StreamReader(dia.FileName)
Dim data As String = reader.ReadToEnd
reader.Close
txtBox.Text = data
End If
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们使用OpenDialog
控件打开 VB 源文件。 我们有一个TextBox
控件,用于显示文件。
Dim dia As New OpenFileDialog
OpenDialog
已创建。
dia.Filter = "VB files (*.vb)|*.vb"
我们将Filter
属性设置为 VB 源文件。 此对话框实例只能选择 VB 文件。
If dia.ShowDialog(Me) = DialogResult.OK
Dim reader As New StreamReader(dia.FileName)
Dim data As String = reader.ReadToEnd
reader.Close
txtBox.Text = data
End If
单击确定后,我们读取所选文件的内容并将其放入TextBox
控件。
图:OpenDialog
在 Visual Basic Winforms 教程的这一部分中,我们显示了各种对话框。
绘图
在 Visual Basic Winforms 教程的这一部分中,我们将进行绘图。 当我们想要更改或增强现有控件时,将使用绘图。 或者,如果我们要从头开始创建自定义控件。 要进行绘图,我们使用 Winforms 库提供的绘图 API。 绘图是在一种方法中完成的,我们将其插入Paint
事件。
System.Drawing
名称空间提供对GDI+
基本图形功能的访问。 System.Drawing.Drawing2D
,System.Drawing.Imaging
和System.Drawing.Text
命名空间中提供了更高级的功能。 Graphics
类提供了在表单上绘图的方法。
HatchBrush
HatchBrush
对象用于填充形状的内部。 我们可以使用几种内置模式。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws nine rectangles.
' The interiors are filled with
' different built-in patterns.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Hatches"
Me.Size = New Size(360, 300)
AddHandler Me.Paint AddressOf Me.OnPaint
Me.CenterToScreen
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
Dim hb As HatchBrush = New HatchBrush(HatchStyle.Cross, Color.Black, Me.BackColor)
g.FillRectangle(hb, 10, 15, 90, 60)
hb = New HatchBrush(HatchStyle.Percent05, Color.Black, Me.BackColor)
g.FillRectangle(hb, 130, 15, 90, 60)
hb = New HatchBrush(HatchStyle.SolidDiamond, Color.Black, Me.BackColor)
g.FillRectangle(hb, 250, 15, 90, 60)
hb = New HatchBrush(HatchStyle.DiagonalBrick, Color.Black, Me.BackColor)
g.FillRectangle(hb, 10, 105, 90, 60)
hb = New HatchBrush(HatchStyle.Divot, Color.Black, Me.BackColor)
g.FillRectangle(hb, 130, 105, 90, 60)
hb = New HatchBrush(HatchStyle.Wave, Color.Black, Me.BackColor)
g.FillRectangle(hb, 250, 105, 90, 60)
hb = New HatchBrush(HatchStyle.ZigZag, Color.Black, Me.BackColor)
g.FillRectangle(hb, 10, 195, 90, 60)
hb = New HatchBrush(HatchStyle.Sphere, Color.Black, Me.BackColor)
g.FillRectangle(hb, 130, 195, 90, 60)
hb = New HatchBrush(HatchStyle.Shingle, Color.Black, Me.BackColor)
g.FillRectangle(hb, 250, 195, 90, 60)
g.Dispose
hb.Dispose
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
这次,我们用九种不同的图案(称为剖面线)填充了九个矩形。
Dim hb As HatchBrush = New HatchBrush(HatchStyle.Cross, Color.Black, Me.BackColor)
在这里,我们创建一个HatchBrush
对象。 参数是图案填充样式以及前景色和背景色。 背景颜色设置为表单的颜色,因此看起来就像我们在表单上绘制的一样。
g.FillRectangle(hb, 10, 15, 90, 60)
我们使用指定的阴影刷填充矩形。
图:通口
基本形状
下面的示例在窗体控件上绘制一些基本形状。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws basic shapes available
' in Winforms
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Basic Shapes"
Me.Size = New Size(420, 280)
AddHandler Me.Paint AddressOf Me.OnPaint
Me.CenterToScreen
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)
g.FillRectangle(Brushes.Gray, 180, 20, 80, 80)
g.FillEllipse(Brushes.Gray, 30, 120, 100, 100)
g.FillEllipse(Brushes.Gray, 160, 130, 100, 70)
Dim points(5) As Point
points(0) = New Point(300, 40)
points(1) = New Point(340, 15)
points(2) = New Point(380, 40)
points(3) = New Point(380, 80)
points(4) = New Point(340, 105)
points(5) = New Point(300, 80)
g.FillPolygon(Brushes.Gray, points)
g.FillPie(Brushes.Gray, New Rectangle(290, 130, 90, 90), 0, 315)
g.Dispose
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
该代码示例在表单上绘制六个形状。 矩形,正方形,圆形,椭圆形,多边形和扇形。
g.SmoothingMode = SmoothingMode.AntiAlias
这使绘图更平滑。
g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)
这条线用灰色填充矩形。 参数是画笔颜色,矩形左上角的 x,y 坐标以及矩形的宽度和高度。
Dim points(5) As Point
points(0) = New Point(300, 40)
points(1) = New Point(340, 15)
...
我们创建五个点的数组。
g.FillPolygon(Brushes.Gray, points)
这条线绘制了一个包含六个单点的多边形。
g.FillPie(Brushes.Gray, New Rectangle(290, 130, 90, 90), 0, 315)
这条线画了一个馅饼。 最后两个参数是起始角度和后掠角度。 以度为单位。
图:基本形状
透明矩形
透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws ten
' rectangles with different
' levels of transparency
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Transparent rectangles"
Me.Size = New Size(590, 110)
AddHandler Me.Paint AddressOf Me.OnPaint
Me.CenterToScreen
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
For i As Integer = 1 to 10
Dim color As Color = Color.FromArgb(i*25, 0, 0, 255)
Dim brush As Brush = New SolidBrush(color)
g.FillRectangle(brush, 50*i, 20, 40, 40)
Next
g.Dispose
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在示例中,我们将绘制十个具有不同透明度级别的矩形。
Dim color As Color = Color.FromArgb(i*25, 0, 0, 255)
该行创建一个颜色对象。 第一个值是 Alpha 透明度。
Dim brush As Brush = New SolidBrush(color)
我们用颜色创建画笔。
g.FillRectangle(brush, 50*i, 20, 40, 40)
我们用颜色填充矩形。
图:透明矩形
灰度图像
下面的示例创建一个灰度图像。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws creates a grayscale
' clone of a bitmap image
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Public Class WinVBApp
Inherits Form
Private rotunda As Bitmap
Private gs As Bitmap
Public Sub New
Me.Text = "Grayscale"
Me.Size = New Size(290, 150)
rotunda = Me.LoadImage
gs = GrayScale(rotunda.Clone)
AddHandler Me.Paint AddressOf Me.OnPaint
Me.CenterToScreen
End Sub
Private Function LoadImage As Bitmap
Try
rotunda = New Bitmap("rotunda.jpg")
Return rotunda
Catch
Console.WriteLine("Image not found")
Environment.Exit(1)
End Try
End Function
Private Function GrayScale(ByVal image As Bitmap) As Bitmap
Dim w As Integer = image.Width
Dim h As Integer = image.Height
For i as Integer = 0 To w-1
For j As Integer = 0 To h-1
Dim c As Color = image.GetPixel(i, j)
Dim lum As Double = 0.299*c.R + 0.587*c.G + 0.114*c.B
image.SetPixel(i, j, Color.FromArgb(lum, lum, lum))
Next
Next
Return image
End Function
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
Dim r1 As New Rectangle(15, 15, rotunda.Width, rotunda.Height)
g.DrawImage(rotunda, r1)
Dim r2 As New Rectangle(150, 15, gs.Width, gs.Height)
g.DrawImage(gs, r2)
g.Dispose
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们的示例中有两个图像。 一种颜色和一种灰度。
rotunda = Me.LoadImage
LoadImage
方法从磁盘的当前工作目录加载位图。
gs = GrayScale(rotunda.Clone)
GrayScale
方法从彩色图像制作灰度图像。 我们将圆形大厅图像的副本作为此方法的参数。
Dim c As Color = image.GetPixel(i, j)
我们得到一个像素的颜色。
Dim lum As Double = 0.299*c.R + 0.587*c.G + 0.114*c.B
该方程式计算灰度图像的亮度。 如果我们使用这些因素来缩放颜色的红色,绿色和蓝色部分,则人眼会将图像视为灰色。
image.SetPixel(i, j, Color.FromArgb(lum, lum, lum))
我们修改像素。
图:灰度图像
绘制文字
要在 Winforms Form
上绘制文本,我们使用DrawString
方法。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws lyrics of
' a song
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "You know I'm no Good"
Me.Size = New Size(380, 450)
AddHandler Me.Paint AddressOf Me.OnPaint
Me.CenterToScreen
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
Dim ft As New Font("Purisa", 10)
Dim br As New SolidBrush(Color.Black)
Dim pt As PointF = 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)
ft.Dispose
br.Dispose
g.Dispose
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在我们的示例中,我们在 Winforms 窗体上绘制歌曲的歌词。
Dim ft As New Font("Purisa", 10)
我们使用 10 磅高的 Purisa 字体。
Dim pt As PointF = New PointF(20.0f, 20.0f)
要在表单上绘制字符串,我们必须使用浮点值。
g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)
DrawString
方法采用以下参数:要绘制的文本,字体,笔刷和PointF
对象。
图:歌词
等待
在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一种错觉,即一条线在移动。 这种效果通常用于通知用户,一项艰巨的任务正在幕后进行。 一个示例是通过互联网流式传输视频。
' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a waiting
' demo
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic
Public Class WinVBApp
Inherits Form
Dim trs(,) As Integer = New Integer(,) { _
{ 0, 35, 70, 100, 150, 180, 210, 250 }, _
{ 250, 0, 35, 70, 100, 150, 180, 210 }, _
{ 210, 250, 0, 35, 70, 100, 150, 180 }, _
{ 180, 210, 250, 0, 35, 70, 100, 150 }, _
{ 150, 180, 210, 250, 0, 35, 70, 100 }, _
{ 100, 150, 180, 210, 250, 0, 35, 70 }, _
{ 70, 100, 150, 180, 210, 250, 0, 35 }, _
{ 35, 70, 100, 150, 180, 210, 250, 0 } _
}
Dim count As Integer = 0
Dim timer As Timer
Public Sub New
Me.Text = "Waiting"
Me.Size = New Size(250, 150)
timer = New Timer
timer.Enabled = True
timer.Interval = 80
AddHandler timer.Tick, AddressOf Me.OnTick
AddHandler Me.Paint, AddressOf Me.OnPaint
Me.CenterToScreen
End Sub
Private Sub OnTick(ByVal sender As Object, ByVal e As EventArgs)
count = count + 1
Me.Refresh
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
g.SmoothingMode = SmoothingMode.AntiAlias
Dim si As Size = Me.ClientSize
g.TranslateTransform(si.Width/2, si.Height/2)
For i As Integer = 0 To 7
Dim color As Color = Color.FromArgb(trs(count Mod 8, i), 30, 30, 30)
Dim pen As New Pen(color, 3)
pen.StartCap = LineCap.Round
pen.EndCap = LineCap.Round
g.DrawLine(pen, 0, -10, 0, -40)
g.RotateTransform(45)
pen.Dispose
Next
g.Dispose
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们用八个不同的 alpha 值绘制八条线。
timer = New Timer
timer.Enabled = True
timer.Interval = 80
AddHandler timer.Tick, AddressOf Me.OnTick
我们使用Timer
制作动画。
Dim trs(,) As Integer = New Integer(,) { _
{ 0, 35, 70, 100, 150, 180, 210, 250 }, _
...
}
这是此演示中使用的透明度值的二维集合。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。
Dim pen As New Pen(color, 3)
pen.StartCap = LineCap.Round
pen.EndCap = LineCap.Round
我们使线条更粗一些,以便更好地显示它们。 我们用带帽的线画线。
Dim color As Color = Color.FromArgb(trs(count Mod 8, i), 30, 30, 30)
在这里,我们定义了一条线的透明度值。
g.DrawLine(pen, 0, -10, 0, -40)
g.RotateTransform(45)
我们画了 8 条线。 它们是顺时针旋转的。
图:等待
在本章中,我们使用 Visual Basic 在 Winforms 中进行了一些绘图。
拖放
Mono Visual Basic Winforms 教程的这一部分将专门用于拖放操作。
在计算机图形用户界面中,拖放是单击虚拟对象并将其拖动到其他位置或另一个虚拟对象上的动作(或支持以下动作)。 通常,它可用于调用多种动作,或在两个抽象对象之间创建各种类型的关联。 (维基百科)
拖放功能是图形用户界面最明显的方面之一。 拖放操作使您可以直观地完成复杂的事情。
拖动按钮
在第一个示例中,我们将在按钮控件上执行拖放操作。 该示例在拖放&放置协议之外执行作业。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we drag & drop
' a button control
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private isDragging As Boolean = False
Private oldX As Integer
Private oldY As Integer
Private button As Button
Public Sub New
Me.Text = "Drag & Drop button"
Me.Size = New Size(270, 180)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
button = New Button
button.Parent = Me
button.Cursor = Cursors.Hand
button.Text = "Button"
button.Location = New Point(20, 20)
AddHandler button.MouseDown, AddressOf Me.OnMouseDown
AddHandler button.MouseUp, AddressOf Me.OnMouseUp
AddHandler button.MouseMove, AddressOf Me.OnMouseMove
End Sub
Private Sub OnMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
isDragging = True
oldX = e.X
oldY = e.Y
End Sub
Private Sub OnMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
If isDragging
button.Top = button.Top + (e.Y - oldY)
button.Left = button.Left + (e.X - oldX)
End If
End Sub
Private Sub OnMouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
isDragging = False
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
该代码示例将一个常规按钮控件放在表单容器上。 通过单击按钮表面并同时用鼠标拖动它,我们可以重新放置按钮。
我们的示例中有一些支持变量。 isDragging
变量告诉我们是否正在拖动对象。 oldX
和oldY
变量在拖动过程开始之前存储 x,y 坐标。
AddHandler button.MouseDown, AddressOf Me.OnMouseDown
AddHandler button.MouseUp, AddressOf Me.OnMouseUp
AddHandler button.MouseMove, AddressOf Me.OnMouseMove
我们为按钮插入了三种不同的鼠标处理器。 它们实现了拖放过程的三个不同阶段。 当我们单击按钮时,过程开始。 这由OnMouseDown
方法处理。 第二部分是机芯。 这是当我们将对象移动到新位置时。 它以OnMouseMove
方法处理。 最后一部分是过程停止的时间。 当我们释放鼠标按钮时会发生这种情况。 适当的任务委托给OnMouseUp
方法。
Private Sub OnMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
isDragging = True
oldX = e.X
oldY = e.Y
End Sub
OnMouseDown
方法实现了过程的第一部分。 它设置了三个必要的变量。
Private Sub OnMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
If isDragging
button.Top = button.Top + (e.Y - oldY)
button.Left = button.Left + (e.X - oldX)
End If
End Sub
在OnMouseMove
方法中,我们重新定位按钮。 我们计算存储的 x,y 坐标与鼠标指针的新坐标之间的差。 差异将添加到按钮的Top
和Left
属性中,从而将其移动到新位置。
图:拖动按钮
拖动文字
在前面的示例中,我们确实拖动了控件上的&拖放。 接下来,我们将对文本数据进行拖放操作。 在这里,我们将使用 Winforms 库提供的拖放协议。
拖放操作是 Winforms 中的标准通信协议。 我们有两个基本对象。 drag source
和drop target
。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we drag & drop
' text
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private txtBox As TextBox
Private btn As Button
Public Sub New
Me.Text = "Drag & Drop text"
Me.Size = New Size(250, 200)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
btn = New Button
txtBox = New TextBox
Me.SuspendLayout
btn.AllowDrop = True
btn.Location = New Point(150, 50)
txtBox.Location = New Point(15, 50)
Me.Controls.Add(btn)
Me.Controls.Add(txtBox)
Me.ResumeLayout
AddHandler btn.DragEnter, AddressOf Me.OnDragEnter
AddHandler btn.DragDrop, AddressOf Me.OnDragDrop
AddHandler txtBox.MouseDown, AddressOf Me.OnMouseDown
End Sub
Private Sub OnDragEnter(ByVal sender As Object, ByVal e As DragEventArgs)
e.Effect = DragDropEffects.Copy
End Sub
Private Sub OnDragDrop(ByVal sender As Object, ByVal e As DragEventArgs)
sender.Text = e.Data.GetData(DataFormats.Text)
End Sub
Private Sub OnMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
sender.DoDragDrop(sender.Text, DragDropEffects.Copy)
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
我们在表单上有两个控件。 一个按钮和一个文本框。 我们将文本从文本框中拖放到按钮上。
btn.AllowDrop = True
我们将AllowDrop
属性设置为true
。 默认情况下不启用删除。
AddHandler btn.DragEnter, AddressOf Me.OnDragEnter
AddHandler btn.DragDrop, AddressOf Me.OnDragDrop
AddHandler txtBox.MouseDown, AddressOf Me.OnMouseDown
同样,拖放过程分为三个步骤。 对于每个特定步骤,我们有三种方法。
Private Sub OnDragEnter(ByVal sender As Object, ByVal e As DragEventArgs)
e.Effect = DragDropEffects.Copy
End Sub
当鼠标指针进入放置目标控件的区域时,将启动DragEnter
事件。 必须设置Effect
属性。 拖动源和放置目标的DragDropEffects
必须相等。 否则,该操作将无法进行。
Private Sub OnDragDrop(ByVal sender As Object, ByVal e As DragEventArgs)
sender.Text = e.Data.GetData(DataFormats.Text)
End Sub
最后,我们有OnDragDrop
方法。 在这里,我们从事件对象获取数据并将其设置为按钮Text
属性。
Private Sub OnMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
sender.DoDragDrop(sender.Text, DragDropEffects.Copy)
End Sub
在OnMouseDown
方法中,我们初始化了拖放过程。 我们使用DoDragDrop
方法启动该过程。 DragDropEffects.Copy
参数指定操作的类型。 实质上,我们可以在拖放操作期间复制文本或移动文本。
图:文本拖放
拖动图像
在最后一个示例中,我们将&拖放图像拖到窗体上。
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program we drag & drop
' an image
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System.Windows.Forms
Imports System.Drawing
Public Class WinVBApp
Inherits Form
Private IsDragging As Boolean = False
Private oldX As Integer
Private oldY As Integer
Private dropRect As Rectangle
Private picBox As PictureBox
Private image As Bitmap
Private brsh As Brush
Public Sub New
Me.Text = "Drag & Drop image"
Me.Size = New Size(350, 250)
Me.InitUI
Me.CenterToScreen
End Sub
Private Sub InitUI
isDragging = False
dropRect = New Rectangle(10, 10, 200, 160)
brsh = Brushes.Gray
picBox = New PictureBox
Me.LoadImage
picBox.Parent = Me
picBox.Location = New Point(100, 50)
picBox.Size = New Size(image.Width, image.Height)
picBox.Image = image
picBox.Cursor = Cursors.Hand
AddHandler Me.Paint, AddressOf Me.OnPaint
AddHandler picBox.MouseDown, AddressOf Me.OnMouseDown
AddHandler picBox.MouseMove, AddressOf Me.OnMouseMove
AddHandler picBox.MouseUp, AddressOf Me.OnMouseUp
End Sub
Private Sub LoadImage
Try
image = New Bitmap("image.jpg")
Catch
Console.WriteLine("Error reading image")
Environment.Exit(1)
End Try
End Sub
Private Sub OnMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
isDragging = True
oldX = e.X
oldY = e.Y
End Sub
Private Sub OnMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
If isDragging
picBox.Top = picBox.Top + (e.Y - oldY)
picBox.Left = picBox.Left + (e.X - oldX)
End If
End Sub
Private Sub OnMouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
isDragging = False
If dropRect.Contains(picBox.Bounds)
brsh = Brushes.Gold
Else
brsh = Brushes.Gray
End If
Me.Refresh
End Sub
Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
g.FillRectangle(brsh, dropRect)
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
在我们的示例中,我们有一个PictureBox
,并绘制了一个灰色矩形。 如果将图片放在矩形内,则矩形的颜色变为金色。
brsh = Brushes.Gray
brsh
变量保存矩形的笔刷。 默认情况下为灰色。
Private Sub LoadImage
Try
image = New Bitmap("image.jpg")
Catch
Console.WriteLine("Error reading image")
Environment.Exit(1)
End Try
End Sub
LoadImage
方法为PictureBox
控件加载位图。
If dropRect.Contains(picBox.Bounds)
brsh = Brushes.Gold
Else
brsh = Brushes.Gray
End If
在OnMouseUp
方法中,我们确定矩形的笔刷。 如果图片框的边界在矩形内,则画笔为金色;否则,画笔为金色。 否则为灰色。
Me.Refresh
我们必须调用Refresh
方法来激活新的画笔颜色。
图:拖放图像
本章致力于使用带有 Visual Basic 语言的 Mono Winforms 库拖放操作。
贪食蛇
在 Mono Winforms 编程教程的这一部分中,我们将创建贪食蛇游戏克隆。
贪食蛇游戏
贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蠕虫。 目的是尽可能多地吃苹果。 蠕虫每次吃一个苹果,它的身体就会长大。 它必须避开墙壁和自己的身体。
开发
蠕虫每个关节的大小为 10 像素。 蠕虫由光标键控制。 最初,蠕虫具有三个关节。 通过按下光标键之一开始游戏。 如果游戏结束,我们将在棋盘中间显示Game Over
消息。
board.vb
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing
Imports System.Data
Imports System.Windows.Forms
NameSpace BoardSpace
public class Board
Inherits UserControl
Const WIDTH As Integer = 300
Const HEIGHT As Integer = 300
Const DOT_SIZE As Integer = 10
Const ALL_DOTS As Integer = 900
Const RAND_POS As Integer = 27
Const DELAY As Integer = 140
Dim x(ALL_DOTS) As Integer
Dim y(ALL_DOTS) As Integer
Dim dots As Integer
Dim apple_x As Integer
Dim apple_y As Integer
Dim left As Boolean = False
Dim right As Boolean = True
Dim up As Boolean = False
Dim down As Boolean = False
Dim inGame As Boolean = True
Private Dim timer As Timer
Private Dim dot As Bitmap
Private Dim apple As Bitmap
Private Dim head As Bitmap
Private Dim components As IContainer
Public Dim BORDER_WIDTH As Integer
Public Dim TITLEBAR_HEIGHT As Integer
Public Sub New
components = New Container
Me.BackColor = Color.Black
Me.DoubleBuffered = True
Me.ClientSize = New Size(WIDTH, HEIGHT)
Try
dot = New Bitmap("dot.png")
apple = New Bitmap("apple.png")
head = New Bitmap("head.png")
Catch e As Exception
Console.WriteLine(e.Message)
Environment.Exit(1)
End Try
Me.InitGame
End Sub
Private Sub InitGame
dots = 3
For z As Integer = 0 To dots-1
x(z) = 50 - z*10
y(z) = 50
Next
Me.LocateApple
AddHandler Me.KeyUp, AddressOf Me.OnKeyUp
timer = New Timer(Me.components)
timer.Enabled = True
timer.Interval = DELAY
AddHandler timer.Tick, AddressOf Me.OnTick
AddHandler Me.Paint, AddressOf Me.OnPaint
End Sub
Private Sub OnPaint(ByVal sender As Object, _
ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
If inGame
Me.DrawObjects(g)
Else
Me.GameOver(g)
End If
End Sub
Private Sub DrawObjects(ByVal g As Graphics)
g.DrawImage(apple, apple_x, apple_y)
For z As Integer = 0 To dots-1
If z = 0
g.DrawImage(head, x(z), y(z))
Else
g.DrawImage(dot, x(z), y(z))
End If
Next
End Sub
Private Sub GameOver(ByVal g As Graphics)
Dim msg As String = "Game Over"
Dim rectF As RectangleF = RectangleF.op_Implicit(Me.ClientRectangle)
Dim format As New StringFormat
format.Alignment = StringAlignment.Center
format.LineAlignment = StringAlignment.Center
g.DrawString(msg, Font, Brushes.White, rectF , format)
timer.Stop
End Sub
Private Sub CheckApple
If x(0) = apple_x And y(0) = apple_y
dots += 1
Me.LocateApple
End If
End Sub
Private Sub Move
For z As Integer = dots To 1 Step -1
x(z) = x(z - 1)
y(z) = y(z - 1)
Next
If left
x(0) -= DOT_SIZE
End If
If right
x(0) += DOT_SIZE
End If
If up
y(0) -= DOT_SIZE
End If
If down
y(0) += DOT_SIZE
End If
End Sub
Private Sub CheckCollision
For z As Integer = dots To 1 Step -1
If z > 4 And x(0) = x(z) And y(0) = y(z)
inGame = False
End If
Next
If y(0) >= HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT
inGame = False
End If
If y(0) < 0
inGame = False
End If
If x(0) >= WIDTH - DOT_SIZE - BORDER_WIDTH:
inGame = False
End If
If x(0) < 0
inGame = False
End If
End Sub
Private Sub LocateApple
Dim rand As New Random
Dim r As Integer = rand.Next(RAND_POS)
apple_x = r * DOT_SIZE
r = rand.Next(RAND_POS)
apple_y = r * DOT_SIZE
End Sub
Private Sub OnTick(ByVal sender As Object, ByVal e As EventArgs)
If inGame
Me.CheckApple
Me.CheckCollision
Me.Move
End If
Me.Refresh
End Sub
Private Sub OnKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs)
Dim key As Integer = e.KeyCode
If key = Keys.Left And Not right
left = True
up = False
down = False
End If
If key = Keys.Right And Not left
right = True
up = False
down = False
End If
If key = Keys.Up And Not down
up = True
right = False
left = False
End if
If key = Keys.Down And Not up
down = True
right = False
left = False
End If
End Sub
End Class
End Namespace
首先,我们将定义游戏中使用的常量。
WIDTH
和HEIGHT
常数确定电路板的大小。 DOT_SIZE
是苹果的大小和蠕虫的点。 ALL_DOTS
常数定义了板上可能的最大点数。 (900 = 300 * 300 / 10 * 10
)RAND_POS
常数用于计算苹果的随机位置。 DELAY
常数确定游戏的速度。
Dim x(ALL_DOTS) As Integer
Dim y(ALL_DOTS) As Integer
这两个数组存储蠕虫的所有关节的 x,y 坐标。
在Move
方法中,我们有游戏的关键算法。 要了解它,请查看蠕虫如何移动。 您控制蠕虫的头部。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。
For z As Integer = dots To 1 Step -1
x(z) = x(z - 1)
y(z) = y(z - 1)
Next
该代码将关节向上移动。
If left
x(0) -= DOT_SIZE
End If
将头向左移动。
在CheckCollision
方法中,我们确定蠕虫是否已经击中自己或撞墙之一。
For z As Integer = dots To 1 Step -1
If z > 4 And x(0) = x(z) And y(0) = y(z)
inGame = False
End If
Next
如果蠕虫用头撞到关节之一,就结束游戏。
If y(0) >= HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT
inGame = False
End If
如果蠕虫到达了棋盘的底部,我们就结束了游戏。
下图有助于了解蠕虫对象与板子底部的碰撞。
图:碰撞
locateApple
方法在表格上随机定位一个苹果。
Dim rand As New Random
Dim r As Integer = rand.Next(RAND_POS)
我们得到一个从 0 到RAND_POS-1
的随机数。
apple_x = r * DOT_SIZE
...
apple_y = r * DOT_SIZE
这些行设置了apple
对象的 x,y 坐标。
在OnKeyUp
方法中,我们确定了键击玩家击键的时间。
If key = Keys.Left And Not right
left = True
up = False
down = False
End If
如果我们按左光标键,则将left
变量设置为True
。 在Move
方法中使用此变量来更改蠕虫对象的坐标。 还要注意,当蠕虫向右移动时,我们不能立即向左转。
nibbles.vb
' ZetCode Mono Visual Basic Winforms tutorial
'
' In this program, we create
' a Nibbles game clone
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Public Class WinVBApp
Inherits Form
Public Sub New
Me.Text = "Nibbles"
Me.FormBorderStyle = FormBorderStyle.FixedSingle
Dim borderWidth As Integer = (Me.Width - Me.ClientSize.Width) / 2
Dim titleBarHeight As Integer = Me.Height - Me.ClientSize.Height - borderWidth
Dim board As New BoardSpace.Board
board.BORDER_WIDTH = borderWidth
board.TITLEBAR_HEIGHT = titleBarHeight
Me.Controls.Add(board)
Me.CenterToScreen
End Sub
Public Shared Sub Main
Application.Run(New WinVBApp)
End Sub
End Class
这是主要的类。
图:贪食蛇
这是使用 Mono Winforms 库和 Visual Basic 语言编写的贪食蛇游戏。
JavaScript GTK 教程
这是 JavaScript GTK 教程。 在本教程中,您将学习使用 JavaScript 在 GTK 中进行 GUI 编程的基础。 本教程适合初学者和中级程序员。
目录
GTK
GTK 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK 库也称为 GIMP 工具包。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK 中创建的。 语言绑定适用于 C++ ,Python,Perl,Java,C# ,JavaScript,PHP 和其他编程语言。
相关教程
GTK+ 教程以其母语介绍了 GTK。 在 ZetCode 上有其他绑定的教程。 Ruby GTK 教程, C# GTK# 教程, Java Gnome 教程, PyGTK 教程或 Visual Basic GTK# 教程。
JavaScript GTK 简介
原文: http://zetcode.com/gui/javascriptgtktutorial/introduction/
在 JavaScript GTK 编程教程的这一部分中,我们将介绍 GTK 库并使用 JavaScript 编程语言创建第一个程序。
本教程的目的是帮助您开始使用 GTK 和 JavaScript。 GTK 是用于创建图形用户界面的领先工具包之一。 JavaScript 是一种流行的脚本语言,主要用于浏览器中。 近年来,JavaScript 已进入其他领域,包括台式机上的用户界面编程和使用诸如 JavaScript++ 之类的语言来创建 JavaScript 类的面向对象编程(OOP)。
Seed
Seed 是一个 JavaScript 解释器,也是 GNOME 项目的库,用于在 JavaScript 中创建独立的应用。 它使用 WebKit 项目的 JavaScript 引擎 JavaScriptCore。
种子使用GObject
内省来访问 Gnome 库。 GTK 是 Gnome 库之一。
简单的例子
在第一个示例中,我们创建一个简单的窗口。 窗口在屏幕上居中。 该代码以过程样式编写。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
This program centers a window on
the screen.
author: Jan Bodnar
website: www.zetcode.com
last modified: August 2011
*/
Gtk = imports.gi.Gtk;
Gtk.init(null, null);
window = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL });
window.signal.hide.connect(Gtk.main_quit);
window.set_default_size(250, 200);
window.set_title("Center");
window.set_position(Gtk.WindowPosition.CENTER);
window.show();
Gtk.main();
本示例在屏幕中央显示一个250x200
像素的窗口。
Gtk = imports.gi.Gtk;
我们导入 Gtk 库。
Gtk.init(null, null);
此行初始化 GTK 库以供使用。
window = new Gtk.Window({ type: Gtk.WindowType.TOPLEVEL });
该行创建一个顶层Window
。 窗口是一个容器。 在大多数情况下(但并非总是如此),主应用窗口是顶级窗口。
window.signal.hide.connect(Gtk.main_quit);
在这里,我们将hide
信号连接到回调。 main_quit()
方法永久退出该应用。 当我们单击标题栏中的关闭按钮或按 Alt + F4
组合键时,会发出隐藏信号。
window.set_default_size(250, 200);
我们为应用窗口设置默认大小。
window.set_title("Center");
我们使用set_title()
方法为窗口设置标题。
window.set_position(Gtk.WindowPosition.CENTER);
这条线使窗口在屏幕上居中。
window.show();
一切准备就绪后,我们在屏幕上显示窗口。
Gtk.main();
我们设置了应用。 创建无限循环。 从这一点开始,应用就坐下来,等待用户或系统的外部事件。 循环一直运行到终止为止。
我们有以面向对象样式编写的相同代码示例。 JavaScript 在某种程度上支持面向对象的编程。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
This program centers a window on
the screen.
author: Jan Bodnar
website: www.zetcode.com
last modified: August 2011
*/
Gtk = imports.gi.Gtk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function()
{
init_ui(this);
function init_ui(w) {
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(250, 200);
w.set_title("Center");
w.set_position(Gtk.WindowPosition.CENTER);
w.show();
}
}
});
var window = new Example();
Gtk.main();
前面的示例以面向对象的样式重写。
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function()
{
我们创建一个新类型。 它继承自 GTK Window 小部件。 我们将代码放入该类型的 init 方法中。
创建工具提示
第二个示例将显示一个工具提示。 工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
This code shows a tooltip on
a window and a button.
author: Jan Bodnar
website: www.zetcode.com
last modified: August 2011
*/
Gtk = imports.gi.Gtk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function()
{
init_ui(this);
function init_ui(w) {
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(250, 200);
w.set_title("Tooltips");
w.set_position(Gtk.WindowPosition.CENTER);
var fix = new Gtk.Fixed();
var button = new Gtk.Button({ label: "Button" });
button.set_size_request(80, 35);
button.set_tooltip_text("Button widget");
fix.put(button, 50, 50);
w.add(fix);
w.set_tooltip_text("Window widget");
w.show_all();
}
}
});
var window = new Example();
Gtk.main();
该示例创建一个窗口。 如果将鼠标指针悬停在窗口区域上方,则会弹出一个工具提示。
button.set_tooltip_text("Button widget");
我们使用set_tooltip_text()
方法设置工具提示。
图:工具提示
退出按钮
在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
This program creates a quit
button. When we press the button,
the application terminates.
author: Jan Bodnar
website: www.zetcode.com
last modified: August 2011
*/
Gtk = imports.gi.Gtk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function()
{
init_ui(this);
function init_ui(w) {
var fix = new Gtk.Fixed();
var btn = new Gtk.Button({ label: "Quit" });
btn.signal.clicked.connect(Gtk.main_quit);
btn.set_size_request(80, 35);
fix.put(btn, 50, 50);
w.add(fix);
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(250, 200);
w.set_title("Quit button");
w.set_position(Gtk.WindowPosition.CENTER);
w.show_all();
}
}
});
var window = new Example();
Gtk.main();
我们使用Button
小部件。 这是一个非常常见的小部件。 它显示文本标签,图像或两者。
init_ui(this);
我们将用户界面的创建委托给init_ui()
方法。
var btn = new Gtk.Button({ label: "Quit" });
在这里,我们创建一个按钮小部件。
btn.signal.clicked.connect(Gtk.main_quit);
我们将main_quit()
方法插入按钮clicked
信号。
btn.set_size_request(80, 35);
我们为按钮设置大小。
fix.put(btn, 50, 50);
我们将退出按钮放入x = 50
和y = 50
的固定容器中。
w.show_all();
我们有两个选择。 在所有小部件上调用show()
,或调用show_all()
(显示容器及其所有子代)。
图:退出按钮
本节介绍了使用 JavaScript 语言的 GTK 库。
布局管理
原文: http://zetcode.com/gui/javascriptgtktutorial/layoutmanagement/
在本章中,我们将展示如何在窗口或对话框中布置窗口小部件。
在设计应用的 GUI 时,我们决定要使用哪些小部件以及如何在应用中组织这些小部件。 为了组织窗口小部件,我们使用专门的不可见窗口小部件,称为布局容器。 在本章中,我们将提到Alignment
,Fixed
,VBox
和Table
。
Fixed
Fixed
容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 在大多数应用中,我们不使用此容器。 我们在某些专业领域使用它。 例如游戏,使用图表的专用应用,可以移动的可调整大小的组件(如电子表格应用中的图表),小型教育示例。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
In this program, we lay out widgets
using absolute positioning
author: Jan Bodnar
website: www.zetcode.com
last modified: July 2011
*/
Gtk = imports.gi.Gtk;
Gdk = imports.gi.Gdk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function ()
{
init_ui(this);
function init_ui(w) {
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(300, 280);
w.set_title("Fixed");
w.set_position(Gtk.WindowPosition.CENTER);
w.modify_bg(Gtk.StateType.NORMAL,
new Gdk.Color({red:6400, green:6400, blue:6440}));
var image1 = new Gtk.Image.from_file("bardejov.jpg");
var image2 = new Gtk.Image.from_file("rotunda.jpg");
var image3 = new Gtk.Image.from_file("mincol.jpg");
var fixed = new Gtk.Fixed();
fixed.put(image1, 20, 20);
fixed.put(image2, 40, 160);
fixed.put(image3, 170, 50);
w.add(fixed);
w.show_all();
}
}
});
var window = new Example();
Gtk.main();
在我们的示例中,我们在窗口上显示了三个小图像。 我们明确指定放置这些图像的 x,y 坐标。
w.modify_bg(Gtk.StateType.NORMAL,
new Gdk.Color({red:6400, green:6400, blue:6440}));
为了获得更好的视觉体验,我们将背景色更改为深灰色。
var image1 = new Gtk.Image.from_file("bardejov.jpg");
Image
是用于显示图像的小部件。 图片是从磁盘上的文件加载的。
var fixed = new Gtk.Fixed();
我们创建Fixed
容器。
fixed.put(image1, 20, 20);
我们将第一个图像放置在x = 20
,y = 20
坐标处。
w.add(fixed);
最后,我们将Fixed
容器添加到窗口中。
图:固定
按钮
在此代码示例中,我们将使用垂直框,水平框和对齐小部件。 水平框将小部件排列为一行。 同样,垂直框将其小部件放在一列中。 Alignment
容器控制其子窗口小部件的对齐方式和大小。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
In this program, we position two buttons
in the bottom right corner of the window.
We use horizontal and vertical boxes.
author: Jan Bodnar
website: www.zetcode.com
last modified: July 2011
*/
Gtk = imports.gi.Gtk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function ()
{
init_ui(this);
function init_ui(w) {
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(260, 150);
w.set_title("Buttons");
w.set_position(Gtk.WindowPosition.CENTER);
var vbox = new Gtk.VBox({homogeneous: false, spacing:5});
var hbox = new Gtk.HBox({homogeneous: true, spacing:3});
var space = new Gtk.Frame();
vbox.pack_start(space, true, true, 0);
var okButton = new Gtk.Button({label:"OK"});
okButton.set_size_request(70, 30);
var closeButton = new Gtk.Button({label:"Close"});
hbox.add(okButton);
hbox.add(closeButton);
var halign = new Gtk.Alignment({xalign: 1.0, yalign: 0.0,
xscale: 0.0, yscale: 0.0});
halign.add(hbox);
vbox.pack_start(halign, false, false, 3);
w.add(vbox);
w.show_all();
}
}
});
var window = new Example();
Gtk.main();
在代码示例中,我们在窗口的右下角放置了两个按钮。 为此,我们使用一个水平框,一个垂直框和一个对齐容器。
var vbox = new Gtk.VBox({homogeneous: false, spacing:5});
将创建一个垂直框容器。 我们将homogeneous
成员设置为 false。 这意味着放在垂直框中的窗口小部件将具有相同的大小。 小部件之间的垂直间距设置为 5 像素。
var space = new Gtk.Frame();
在这里,我们创建一个Frame
小部件。 该小部件的目的是占用两个按钮上方的空间。
vbox.pack_start(space, true, true, 0);
在这里,我们将框架小部件放入垂直框中。 该方法的第一个参数是小部件,它被放置在框中。 以下三个参数是expand
,fill
和padding
。 expand
参数设置为true
,这意味着将在小部件周围分配可用空间。 当fill
参数设置为true
时,小部件实际上会占用其周围的所有可用空间。 子窗口小部件周围没有填充。
var hbox = new Gtk.HBox({homogeneous: true, spacing:3});
此代码行创建一个水平框。 框内的所有小部件都将具有相同的大小。 小部件之间的水平间隔为 3px。
var okButton = new Gtk.Button({label:"OK"});
okButton.set_size_request(70, 30);
var closeButton = new Gtk.Button({label:"Close"});
hbox.add(okButton);
hbox.add(closeButton);
我们创建两个按钮,并将它们放在水平框中。
var halign = new Gtk.Alignment({xalign: 1.0, yalign: 0.0,
xscale: 0.0, yscale: 0.0});
halign.add(hbox);
vbox.pack_start(halign, false, false, 3);
这将创建一个对齐容器,它将其子窗口小部件放在右侧。 设置为 1.0 的xalign
成员会将所有可用空间放在水平框的左侧。 这将向右推两个按钮。 我们将水平框添加到对齐容器中,然后将对齐容器包装到垂直框中。 我们必须记住,对齐容器仅包含一个子窗口小部件。 这就是为什么我们必须使用水平框。
图:按钮
计算器骨架
Table
小部件按行和列排列小部件。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
In this program we create a skeleton of
a calculator. We use the Table widget.
author: Jan Bodnar
website: www.zetcode.com
last modified: July 2011
*/
Gtk = imports.gi.Gtk;
Gdk = imports.gi.Gdk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function ()
{
init_ui(this);
function init_ui(w) {
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(300, 250);
w.set_title("Calculator");
w.set_position(Gtk.WindowPosition.CENTER);
var vbox = new Gtk.VBox({homogeneous: false, spacing:2});
var mb = new Gtk.MenuBar();
var filemenu = new Gtk.Menu();
var file = new Gtk.MenuItem({label:"File"});
file.set_submenu(filemenu);
mb.append(file);
vbox.pack_start(mb, false, false, 0);
var table = new Gtk.Table.c_new(5, 4, true);
table.attach_defaults(new Gtk.Button.with_label("Cls"), 0, 1, 0, 1);
table.attach_defaults(new Gtk.Button.with_label("Bck"), 1, 2, 0, 1);
table.attach_defaults(new Gtk.Label(), 2, 3, 0, 1);
table.attach_defaults(new Gtk.Button.with_label("Close"), 3, 4, 0, 1);
table.attach_defaults(new Gtk.Button.with_label("7"), 0, 1, 1, 2);
table.attach_defaults(new Gtk.Button.with_label("8"), 1, 2, 1, 2);
table.attach_defaults(new Gtk.Button.with_label("9"), 2, 3, 1, 2);
table.attach_defaults(new Gtk.Button.with_label("/"), 3, 4, 1, 2);
table.attach_defaults(new Gtk.Button.with_label("4"), 0, 1, 2, 3);
table.attach_defaults(new Gtk.Button.with_label("5"), 1, 2, 2, 3);
table.attach_defaults(new Gtk.Button.with_label("6"), 2, 3, 2, 3);
table.attach_defaults(new Gtk.Button.with_label("*"), 3, 4, 2, 3);
table.attach_defaults(new Gtk.Button.with_label("1"), 0, 1, 3, 4);
table.attach_defaults(new Gtk.Button.with_label("2"), 1, 2, 3, 4);
table.attach_defaults(new Gtk.Button.with_label("3"), 2, 3, 3, 4);
table.attach_defaults(new Gtk.Button.with_label("-"), 3, 4, 3, 4);
table.attach_defaults(new Gtk.Button.with_label("0"), 0, 1, 4, 5);
table.attach_defaults(new Gtk.Button.with_label("."), 1, 2, 4, 5);
table.attach_defaults(new Gtk.Button.with_label("="), 2, 3, 4, 5);
table.attach_defaults(new Gtk.Button.with_label("+"), 3, 4, 4, 5);
vbox.pack_start(new Gtk.Entry(), false, false, 0);
vbox.pack_end(table, true, true, 0);
w.add(vbox);
w.show_all();
}
}
});
var window = new Example();
Gtk.main();
我们使用Table
小部件创建一个计算器框架。
var table = new Gtk.Table.c_new(5, 4, true);
我们创建一个具有 5 行 4 列的表小部件。 第三个参数是同质参数。 如果设置为true
,则表中的所有小部件都具有相同的大小。 所有窗口小部件的大小等于表容器中最大的窗口小部件。
table.attach_defaults(new Gtk.Button.with_label("Cls"), 0, 1, 0, 1);
我们在表格容器上附加一个按钮。 到表格的左上方单元格。 前两个参数是单元格的左侧和右侧,后两个参数是单元格的顶部和左侧。
vbox.pack_end(table, true, true, 0);
我们将表格小部件打包到垂直框中。
图:计算机骨架
窗口
接下来,我们将创建一个更高级的示例。 我们显示一个窗口,可以在 JDeveloper IDE 中找到它。
#!/usr/bin/seed
/*
ZetCode JavaScript GTK tutorial
This is a more complicated layout example.
We use Alignment and Table widgets.
author: Jan Bodnar
website: www.zetcode.com
last modified: July 2011
*/
Gtk = imports.gi.Gtk;
Gdk = imports.gi.Gdk;
Gtk.init(null, null);
Example = new GType({
parent: Gtk.Window.type,
name: "Example",
init: function ()
{
init_ui(this);
function init_ui(w) {
w.signal.hide.connect(Gtk.main_quit);
w.set_default_size(300, 280);
w.set_title("Windows");
w.set_position(Gtk.WindowPosition.CENTER);
w.set_border_width(15);
var table = new Gtk.Table.c_new(8, 4, false);
table.set_col_spacings(3);
var title = new Gtk.Label.c_new("Windows");
var halign = new Gtk.Alignment.c_new(0.0, 0.0, 0.0, 0.0);
halign.add(title);
table.attach(halign, 0, 1, 0, 1, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL, 0, 0);
var frame = new Gtk.Frame();
table.attach(frame, 0, 2, 1, 3, Gtk.AttachOptions.FILL |
Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.FILL |
Gtk.AttachOptions.EXPAND, 1, 1);
var activate = new Gtk.Button.with_label("Activate");
activate.set_size_request(50, 30);
table.attach(activate, 3, 4, 1, 2, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.SHRINK, 1, 1)
var valign = new Gtk.Alignment.c_new(0.0, 0.0, 0.0, 0.0);
var closeButton = new Gtk.Button.with_label("Close");
closeButton.set_size_request(70, 30);
valign.add(closeButton);
table.set_row_spacing(1, 3);
table.attach(valign, 3, 4, 2, 3, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, 1, 1)
halign2 = new Gtk.Alignment.c_new(0.0, 1.0, 0.0, 0.0);
help = new Gtk.Button.with_label("Help");
help.set_size_request(70, 30);
halign2.add(help);
table.set_row_spacing(3, 6);
table.attach(halign2, 0, 1, 4, 5, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL, 0, 0);
var okButton = new Gtk.Button.with_label("OK");
okButton.set_size_request(70, 30);
table.attach(okButton, 3, 4, 4, 5, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL, 0, 0);
w.add(table);
w.show_all();
}
}
});
var window = new Example();
Gtk.main();
该代码示例显示了如何在 JavaScript GTK 中创建类似的窗口。
var table = new Gtk.Table.c_new(8, 4, false);
table.set_col_spacings(3);
该示例基于Table
容器。 列之间将有 3px 的间隔。
var title = new Gtk.Label.c_new("Windows");
var halign = new Gtk.Alignment.c_new(0.0, 0.0, 0.0, 0.0);
halign.add(title);
table.attach(halign, 0, 1, 0, 1, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL, 0, 0);
这段代码创建了一个向左对齐的标签。 标签放置在Table
容器的第一列的第一行中。
var frame = new Gtk.Frame();
table.attach(frame, 0, 2, 1, 3, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND,
Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, 1, 1);
框架小部件跨越两行两列。 它将消耗其周围的所有可用空间。 因此,占用了窗口的大部分区域。
var valign = new Gtk.Alignment.c_new(0.0, 0.0, 0.0, 0.0);
var closeButton = new Gtk.Button.with_label("Close");
closeButton.set_size_request(70, 30);
valign.add(closeButton);
table.set_row_spacing(1, 3);
table.attach(valign, 3, 4, 2, 3, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, 1, 1)
我们将关闭按钮放在框架小部件旁边,进入第四列。 (我们从零开始计数)将按钮添加到对齐小部件中,以便可以将其对齐到顶部。
halign2 = new Gtk.Alignment.c_new(0.0, 1.0, 0.0, 0.0);
help = new Gtk.Button.with_label("Help");
help.set_size_request(70, 30);
halign2.add(help);
table.set_row_spacing(3, 6);
table.attach(halign2, 0, 1, 4, 5, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL, 0, 0);
将帮助按钮放置在对齐小部件中,以便可以在其表格单元格中使其对齐。 它位于第一列第五行。
var okButton = new Gtk.Button.with_label("OK");
okButton.set_size_request(70, 30);
table.attach(okButton, 3, 4, 4, 5, Gtk.AttachOptions.FILL,
Gtk.AttachOptions.FILL, 0, 0);
最后,单击确定按钮。 它位于第四列第五行。
图:窗口
在 JavaScript GTK 教程的这一部分中,我们提到了小部件的布局管理。