ZetCode-GUI-教程-六-

ZetCode GUI 教程(六)

原文:ZetCode

协议:CC BY-NC-SA 4.0

贪食蛇

原文: http://zetcode.com/gui/vbqyoto/nibbles/

在 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

首先,我们将定义一些在游戏中使用的全局变量。

WIDTHHEIGHT常数确定电路板的大小。 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

在这个类中,我们设置了贪食蛇游戏。

Nibbles

图:贪食蛇

这是用 Qyo​​to 库和 Visual Basic 编程语言编写的贪食蛇电脑游戏。

Mono IronPython Winforms 教程

原文: http://zetcode.com/tutorials/ironpythontutorial/

这是 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

TextWidthHeight是表单的属性。 更改这些属性,我们将修改表单控件。 第一行在表单控件的标题栏中显示文本"Simple"。 其他两行将表单的大小设置为250x200像素。

self.CenterToScreen()

这种方法将我们的应用集中在屏幕上。

Application.Run(IForm())

此行运行示例。

Simple

图:简单

图标

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)  

最好将所有输入输出工作放在tryexcept关键字之间。 web.ico文件必须在当前工作目录中可用。 这是我们执行应用的目录(ipy icon.py)。

Icon

图:图标

工具提示

工具提示是一个小的矩形弹出窗口,当用户将指针放在控件上时,它会显示控件目的的简短说明。

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 = 30y = 70px坐标处。

Tooltips

图:工具提示 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 编程都是事件驱动的编程。 在我们的示例中,我们在表单容器上显示了一个按钮控件。 该按钮将收听两个事件:ClickMouseEnter事件。

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 教程的这一部分显示了一些入门代码示例。

布局管理

原文: http://zetcode.com/tutorials/ironpythontutorial/layout/

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。 正确的价值。

现在看看以下两个图像。 左边的是开始时显示的应用。 调整大小后,右侧显示相同的应用。 第一个按钮与表单的左边界和上边界保持距离。 第二个按钮与表单的右边框保持距离。 但是它在垂直方向上没有保持任何距离。

Before resizing

After resizing

图:调整大小前后

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控件占用了表单容器内的剩余空间。

Editor skeleton

图:编辑器骨架

固定按钮

下一个示例显示了位于窗体右下角的两个按钮。

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

WIDTHHEIGHT变量确定应用窗口的宽度和高度。

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属性设置为右侧。 另一个按钮的创建类似。

Anchored buttons

图:固定按钮

播放器骨架

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())

这是一个更复杂的示例,它同时显示了DockAnchor属性。

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

最后两个控件固定在右侧。

Player skeleton

图:播放器骨架

IronPython Mono Winforms 教程的这一部分是关于控件的布局管理的。 我们实践了 Winforms 库提供的各种可能性。

PyQt5 中的俄罗斯方块

原文: http://zetcode.com/gui/pyqt5/tetris/

在本章中,我们将创建一个俄罗斯方块游戏克隆。

俄罗斯方块

俄罗斯方块游戏是有史以来最受欢迎的计算机游戏之一。 原始游戏是由俄罗斯程序员 Alexey Pajitnov 于 1985 年设计和编程的。此后,几乎所有版本的几乎所有计算机平台上都可以使用俄罗斯方块。

俄罗斯方块被称为下降块益智游戏。 在这个游戏中,我们有七个不同的形状,称为 tetrominoes:S 形,Z 形,T 形,L 形,线形,MirroredL 形和正方形。 这些形状中的每一个都形成有四个正方形。 形状从板上掉下来。 俄罗斯方块游戏的目的是移动和旋转形状,以使其尽可能地适合。 如果我们设法形成一行,则该行将被破坏并得分。 我们玩俄罗斯方块游戏,直到达到顶峰。

Tetrominoes

图:Tetrominoes

PyQt5 是旨在创建应用的工具包。 还有其他一些旨在创建计算机游戏的库。 尽管如此,PyQt5 和其他应用工具包仍可用于创建简单的游戏。

创建计算机游戏是增强编程技能的好方法。

开发

我们的俄罗斯方块游戏没有图像,我们使用 PyQt5 编程工具包中提供的绘图 API 绘制四面体。 每个计算机游戏的背后都有一个数学模型。 俄罗斯方块也是如此。

游戏背后的一些想法:

  • 我们使用QtCore.QBasicTimer()创建游戏周期。
  • 绘制四方块。
  • 形状以正方形为单位移动(而不是逐个像素移动)。
  • 从数学上讲,棋盘是一个简单的数字列表。

该代码包括四个类别:TetrisBoardTetrominoeShapeTetris类设置游戏。 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类变量。 BoardWidthBoardHeight以块为单位定义电路板的大小。 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)]

创建后,我们将创建一个空坐标列表。 该列表将保存俄罗斯方块的坐标。

Coordinates

图:坐标

上面的图片将帮助您更多地了解坐标值。 例如,元组(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()方法将一块向左旋转。 正方形不必旋转。 这就是为什么我们只是将引用返回到当前对象。 将创建一个新零件,并将其坐标设置为旋转零件的坐标。

Tetris

图:俄罗斯方块

这是 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已插入表格。

Simple menu

图:简单菜单

子菜单

每个菜单项也可以有一个子菜单。 这样,我们可以将类似的命令分组。 例如,我们可以将隐藏或显示各种工具栏(例如个人栏,地址栏,状态栏或导航栏)的命令放入称为工具栏的子菜单中。

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)

在这里,我们创建一个菜单项并将其添加到“导入”子菜单。

Submenu

图:子菜单

检查菜单项

下一个代码示例演示如何创建选中的菜单项。

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控件。 要创建菜单栏,我们可以使用MainMenuMenuStrip控件。 后者具有一些附加功能。

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值显示和隐藏状态栏和复选标记。

Check menu item

图:选中菜单项

图像,分隔符

我们将进一步增强对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())

在这里,我们向“文件”菜单添加分隔符。

MenuStrip

图:图像 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",我们将关闭该应用。

ToolBar

图:工具栏

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

图: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

图: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())

在代码示例中,我们显示了TrackBarPictureBox。 通过拖动轨迹栏,我们可以在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

图: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

图: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

图: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())

在示例中,我们显示了MonthCalendarLabel

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

图: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

图: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

图:PictureBox

我们已经完成了 Mono Winforms 编程教程的这一章,专门讨论基本控件。

Mono Winforms 中的高级控件

原文: http://zetcode.com/tutorials/ironpythontutorial/advancedcontrols/

在 IronPython Mono Winforms 教程的这一部分中,我们介绍一些更高级的控件。 即ListBoxListViewTreeView控件。

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

图: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

图: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

目录

下面的代码示例将更深入地研究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

图: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

图: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

图: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

图: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变量告诉我们是否正在拖动对象。 oldXoldY变量在拖动过程开始之前存储 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 坐标与鼠标指针的新坐标之间的差。 差异将添加到按钮的TopLeft属性中,从而将其移动到新位置。

Dragging a button

图:拖动按钮

拖动文字

在前面的示例中,我们确实拖动了控件上的&拖放。 接下来,我们将对文本数据进行拖放操作。 在这里,我们将使用 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属性。

Drag & drop of 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()方法来激活新的画笔颜色。

Drag & drop image

图:拖放图像

本章专门使用 Mono Winforms 库拖放操作。

绘图

原文: http://zetcode.com/tutorials/ironpythontutorial/painting/

在 IronPython Mono Winforms 教程的这一部分中,我们将进行绘图。 当我们想要更改或增强现有控件时,将使用绘图。 或者,如果我们要从头开始创建自定义控件。 要进行绘图,我们使用 Winforms 库提供的绘图 API。 绘图是在一种方法中完成的,我们将其插入Paint事件。

System.Drawing名称空间提供对GDI+基本图形功能的访问。 System.Drawing.Drawing2DSystem.Drawing.ImagingSystem.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()

我们释放资源。

lines

图:直线

色彩

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 值以及矩形的宽度和高度。

Colors

图:颜色

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)

我们使用指定的阴影刷填充矩形。

Hatches

图:通口

基本对象

下面的示例在窗体控件上绘制一些基本形状。

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)

这条线画了一个馅饼。 最后两个参数是起始角度和后掠角度。 以度为单位。

Basic shapes

图:基本形状

绘制直线

要在 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对象。

Lyrics

图:歌词

绘制图像

在最后一个示例中,我们将在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)

这条线实际上绘制图像。

Image

图:图像

在本章中,我们在 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 度。 从而得到甜甜圈的对象。

Donut

图:多纳圈

透明矩形

透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用 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)

我们画一个矩形。

Transparent rectangles

图:透明矩形

灰度图像

下面的示例创建一个灰度图像。

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对象。 我们使用两个控制点和两种混合颜色。

Gradients

图:渐变

等待

在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 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 条线。 它们顺时针旋转。

Waiting

图:等待

在 IronPython Winforms 教程的这一章中,我们在 Mono Winforms 库中做了一些更高级的绘图。

IronPython Mono Winforms 中的贪食蛇

原文: http://zetcode.com/tutorials/ironpythontutorial/snake/

在 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

首先,我们将定义游戏中使用的常量。

WIDTHHEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 (900 = 300 * 300 / 10 * 10RAND_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

如果蛇击中了棋盘的底部,则游戏结束。

下图有助于了解蛇形物体与棋盘底部的碰撞。

Collision

图:碰撞

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

我们将它们提供给董事会。

Snake

图:贪食蛇

这是使用 Iron Win 编程语言的 Mono Winforms 库编程的贪食蛇游戏。

IronPython Mono Winforms 中的俄罗斯方块游戏

http://zetcode.com/tutorials/ironpythontutorial/tetris/

俄罗斯方块游戏是有史以来最受欢迎的计算机游戏之一。 原始游戏是由俄罗斯程序员 Alexey Pajitnov 于 1985 年设计和编程的。此后,几乎所有版本的几乎所有计算机平台上都可以使用俄罗斯方块。 甚至我的手机都有俄罗斯方块游戏的修改版。

俄罗斯方块被称为下降块益智游戏。 在这个游戏中,我们有七个不同的形状,称为 tetrominoes 。 S 形,Z 形,T 形,L 形,线形,镜像 L 形和正方形。 这些形状中的每一个都形成有四个正方形。 形状从板上掉下来。 俄罗斯方块游戏的目的是移动和旋转形状,以便它们尽可能地适合。 如果我们设法形成一行,则该行将被破坏并得分。 我们玩俄罗斯方块游戏,直到达到顶峰。

Tetrominoes

图: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列表存储正方形填充的颜色值。 七块每个都有其自己的颜色。 lightdark存储线条的颜色,使正方形看起来像 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 形。 下图说明了形状。

Coordinates

图:坐标

当绘制当前下降片时,将其绘制在self.curXself.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

在其他情况下,我们更改作品的坐标。 要了解此代码,请查看上图。

Tetris

图:俄罗斯方块

这是 IronPython Winforms 中的俄罗斯方块游戏。

Qt5 教程

原文: http://zetcode.com/gui/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 教程

原文: http://zetcode.com/gui/fbgtk/

这是 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

我们编译并运行该示例。

Simple example

图:简单 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 = 50y = 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(),该窗口小部件一步显示所有三个小部件。

Close button

图:关闭按钮

显示图像

在以下示例中,我们在窗口上显示图像。

' 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)

通过选择退出菜单项,我们终止了该应用。

Simple menu

图:简单菜单 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关键字来进行转换。

Check button

图:复选按钮 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信号中。 我们再次进行铸造。

A combo box widget

图:组合框小部件

这是 FreeBASIC GTK 教程。 我们有几个示例,这些示例说明了如何使用 FreeBASIC 语言和 GTK 库。 这些示例清楚地表明,我们混合使用 FreeBASIC 和 C 语言。 一旦知道如何将它们放在一起,就可以进行 GTK 编程。 有关 GTK 库的更多信息,请参见。

Tweet

Jython Swing 教程

原文: http://zetcode.com/gui/jythonswing/

这是 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 简介

原文: http://zetcode.com/gui/jythonswing/introduction/

在 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()方法。

Tooltip

图:工具提示

退出按钮

在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。

#!/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退出应用。

Quit button

图:退出按钮

本节介绍了使用 Jython 语言的 Swing 工具包。

Jython Swing 中的布局管理

原文: http://zetcode.com/gui/jythonswing/layout/

在 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)

我们将标签添加到面板容器中。

Absolute

图:绝对定位

按钮示例

在下面的示例中,我们将在窗口的右下角放置两个按钮。

#!/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)))

我们在按钮和窗口的边框之间留出一些空间。

Buttons example

图:按钮示例

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])

此行使所有按钮的大小相同。 我们只需要设置它们的宽度,因为默认情况下它们的高度已经相同。

Windows example

图:窗口示例

查看示例的屏幕截图。 注意,可以将组件分为垂直和水平组件集。 例如,标签区域和“帮助”按钮组件可以形成垂直的组件组。 这正是GroupLayout管理器所做的。 它通过形成组件的垂直和水平组来布局组件。

在 Jython Swing 教程的这一部分中,我们提到了小部件的布局管理。

Jython Swing 中的组件

原文: http://zetcode.com/gui/jythonswing/components/

在 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

图: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 component

图: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()

在代码示例中,我们显示了JSliderJLabel。 通过拖动滑块,我们可以更改标签组件上的图标。 我们有四个代表声音各种状态的图像。

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 component

图: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 component

图: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()

在我们的示例中,我们将显示JListJLabel组件。 列表组件包含我们系统上所有可用字体系列名称的列表。 如果我们从列表中选择一项,则标签将以我们选择的字体显示。

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 component

图:JList组件

在 Jython Swing 教程的这一部分中,我们介绍了几个 Swing 组件。

Jython Swing 中的菜单和工具栏

原文: http://zetcode.com/gui/jythonswing/menustoolbars/

在 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)

菜单添加到菜单栏。

Simple menu

图:简单菜单

子菜单

子菜单是插入另一个菜单对象的菜单。 下一个示例对此进行了演示。

#!/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()

分隔符是一条水平线,用于在视觉上分隔菜单项。 这样,我们可以将项目分组到一些合理的位置。

Submenu

图:子菜单

弹出菜单

在下一个示例中,我们创建一个弹出菜单。

#!/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 坐标处显示弹出菜单窗口。

Popup menu`

图:弹出菜单

工具栏

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 在 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管理器的北部。

Toolbar

图:工具栏

在 Jython Swing 教程的这一部分中,我们提到了菜单和工具栏。

Jython Swing 中的对话框

原文: http://zetcode.com/gui/jythonswing/dialogs/

在 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

Error message dialog

图:错误消息 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

图: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

图:JFileChooser

在 Jython Swing 教程的这一部分中,我们使用了对话框窗口。

Jython Swing 中的绘图

原文: http://zetcode.com/gui/jythonswing/painting/

在 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 = 10y = 15且宽度= 90和高度= 60的矩形。

Colors

图:颜色

形状

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)

这三条线绘制一个椭圆,一个弧形和一个椭圆形。

Shapes

图:形状

透明矩形

透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用 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 合成规则。

Transparent rectangles

图:透明矩形

甜甜圈形状

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。 仿射变换由零个或多个线性变换(旋转,缩放或剪切)和平移(移位)组成。 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()方法绘制文本。

Drawing text

图:绘制文本

在 Jython Swing 编程教程的这一部分中,我们做了一些绘图。

Jython Swing 中的贪食蛇

原文: http://zetcode.com/gui/jythonswing/nibbles/

在 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

首先,我们将定义一些在游戏中使用的常量。

WIDTHHEIGHT常数确定电路板的大小。 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()

在这个类中,我们设置了贪食蛇游戏。

Nibbles

图:贪食蛇

这是使用 Swing 库和 Jython 编程语言编写的贪食蛇电脑游戏。

JRuby Swing 教程

原文: http://zetcode.com/gui/jrubyswing/

这是 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 工具包简介

原文: http://zetcode.com/gui/qt5/introduction/

在 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创建一个Makefilemake命令生成该程序。

如果 Qt5 安装目录不是PATH变量的一部分,我们可以提供qmake工具的完整路径。

$ /usr/local/qt5/bin/qmake -project
$ /usr/local/qt5/bin/qmake
$ make

Simple example

图:简单 example

本章是 Qt5 库的简介。

JRuby Swing 简介

原文: http://zetcode.com/gui/jrubyswing/introduction/

在 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方法。

Tooltip

图:工具提示

退出按钮

在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。

#!/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

我们向按钮添加一个动作监听器。 监听器终止应用。

Quit button

图:退出按钮

本节介绍了使用 JRuby 语言的 Swing 工具包。

JRuby Swing 中的布局管理

原文: http://zetcode.com/gui/jrubyswing/layout/

在 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

我们将标签添加到面板容器中。

Absolute

图:绝对定位

按钮示例

在下面的示例中,我们将在窗口的右下角放置两个按钮。

#!/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

我们在按钮和窗口的边框之间留出一些空间。

Buttons example

图:按钮示例

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

此代码使所有按钮的大小相同。 我们只需要设置它们的宽度,因为默认情况下它们的高度已经相同。

Windows example

图:窗口示例

查看示例的屏幕截图。 注意,可以将组件分为垂直和水平组件集。 例如,标签,区域和“帮助”按钮组件可以形成垂直的组件组。 这正是GroupLayout管理器所做的。 它通过形成组件的垂直和水平组来布局组件。

在 JRuby Swing 教程的这一部分中,我们提到了组件的布局管理。

JRuby Swing 中的组件

原文: http://zetcode.com/gui/jrubyswing/components/

在 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

图: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 component

图: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

在代码示例中,我们显示了JSliderJLabel。 通过拖动滑块,我们可以更改标签组件上的图标。 我们有四个代表声音各种状态的图像。

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

图: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

图: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

在我们的示例中,我们将显示JListJLabel组件。 列表组件包含我们系统上所有可用字体系列名称的列表。 如果我们从列表中选择一项,则标签将以我们选择的字体显示。

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 component

图:JList组件

在 JRuby Swing 教程的这一部分中,我们介绍了几个 Swing 组件。

菜单和工具栏

原文: http://zetcode.com/gui/jrubyswing/menustoolbars/

在 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

菜单添加到菜单栏。

Simple menu

图:简单菜单

子菜单

子菜单是插入另一个菜单对象的菜单。 下一个示例对此进行了演示。

#!/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

分隔符是一条水平线,用于在视觉上分隔菜单项。 这样,我们可以将项目分组到一些合理的位置。

Submenu

图:子菜单

弹出菜单

在下一个示例中,我们创建一个弹出菜单。

#!/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 坐标处显示弹出菜单窗口。

Popup menu`

图:弹出菜单

工具栏

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 在 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容器的默认布局管理器。

Toolbar

图:工具栏

在 JRuby Swing 教程的这一部分中,我们提到了菜单和工具栏。

JRuby Swing 中的对话框

原文: http://zetcode.com/gui/jrubyswing/dialogs/

在 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

Error message dialog

图:错误消息 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

图:JFileChooser

在 JRuby Swing 教程的这一部分中,我们使用了对话框窗口。

在 JRuby Swing 中绘图

原文: http://zetcode.com/gui/jrubyswing/painting/

在 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 = 10y = 15且宽度= 90和高度= 60的矩形。

Colors

图:颜色

形状

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

这三条线绘制一个椭圆,一个弧形和一个椭圆形。

Shapes

图:形状

透明矩形

透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用 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 合成规则。

Transparent rectangles

图:透明矩形

甜甜圈形状

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。 仿射变换由零个或多个线性变换(旋转,缩放或剪切)和平移(移位)组成。 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方法绘制文本。

Drawing text

图:绘制文本

在 JRuby Swing 编程教程的这一部分中,我们做了一些绘图。

JRuby Swing 中的贪食蛇

原文: http://zetcode.com/gui/jrubyswing/nibbles/

在 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

首先,我们将定义一些在游戏中使用的常量。

WIDTHHEIGHT常数确定电路板的大小。 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

在这个类中,我们设置了贪食蛇游戏。

Nibbles

图:贪食蛇

这是使用 Swing 库和 JRuby 编程语言编写的贪食蛇电脑游戏。

Visual Basic Winforms 教程

原文: http://zetcode.com/gui/vbwinforms/

这是 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 简介

原文: http://zetcode.com/gui/vbwinforms/introduction/

在 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)应用的目录。

Icon

图:图标

退出按钮

在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。

' 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方法终止应用。

Quit button

图:退出按钮

本节介绍了使用 Visual Basic 语言的 Winforms 库。

布局管理

原文: http://zetcode.com/gui/vbwinforms/layout/

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。 正确的值。

现在看看以下两个图像。 左边的是开始时显示的应用。 调整大小后,右侧显示相同的应用。 第一个按钮与表单的左边界和上边界保持距离。 第二个按钮与表单的右边框保持距离。 但是它在垂直方向上没有保持任何距离。

Before resizing

After resizing

图:调整大小前后

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控件占用了表单容器内的剩余空间。

Editor skeleton

图:编辑器骨架

固定按钮

下一个示例显示了位于窗体右下角的两个按钮。

' 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

WIDTHHEIGHT变量确定应用窗口的宽度和高度。

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属性设置为右侧。 另一个按钮的创建类似。

Anchored buttons

图:固定按钮

播放器骨架

本部分最后的示例显示了一个更复杂的示例。 它是音乐播放器的骨架。

' 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

这是一个更复杂的示例,它同时显示了DockAnchor属性。

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

最后两个控件固定在右侧。

Player skeleton

图:播放器骨架

Mono Visual Basic Winforms 教程的这一部分是关于控件的布局管理的。 我们实践了 Winforms 库提供的各种可能性。

Qt5 中的字符串

原文: http://zetcode.com/gui/qt5/strings/

在本章中,我们将使用字符串。 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::stringdata()方法返回。 第二个参数是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

访问字符串元素

QStringQChars的序列。 可以使用[]运算符或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

遍历字符串

QStringQChars组成。 我们可以遍历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;
}

我们比较ab字符串,它们不相等。 它们的第一个字符不同。

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字符串中删除最后一个字符。 现在bc字符串相等。

输出:

$ ./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(&quot;Bottle %d\n&quot;, i);
    }
}

在本章中,我们使用了 Qt5 中的字符串。

基本控制

原文: http://zetcode.com/gui/vbwinforms/controls/

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

图: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

图: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

图: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

在示例中,我们显示了MonthCalendarLabel。 后者显示当前选择的日期。

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

图: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

图:TextBox

我们已经完成了 Visual Basic Winforms 教程的这一章,专门讨论基本控件。

进阶控件

原文: http://zetcode.com/gui/vbwinforms/advanced/

在 Visual Basic Winforms 教程的这一部分中,我们介绍一些更高级的控件。 即ListBoxListViewTreeView控件。

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

图: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

图: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

图:TreeView

在 Visual Basic Winforms 教程的这一部分中,我们介绍了 Winforms 库中提供的三个高级控件。

菜单和工具栏

原文: http://zetcode.com/gui/vbwinforms/menustoolbars/

在 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 快捷方式或按 AltFE 键关闭它 。

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已插入表格。

Simple menu

图:简单菜单

子菜单

每个菜单项也可以有一个子菜单。 这样,我们可以将类似的命令分组。 例如,我们可以将隐藏/显示各种工具栏(例如个人栏,地址栏,状态栏或导航栏)的命令放置在称为工具栏的子菜单中。

' 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)

在这里,我们创建一个菜单项并将其添加到“导入”子菜单。

Submenu

图:子菜单

检查菜单项

下一个代码示例演示如何创建选中的菜单项。

' 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控件。 要创建菜单栏,我们可以使用MainMenuMenuStrip控件。 后者具有一些附加功能。

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值显示和隐藏状态栏和复选标记。

Check menu item

图:选中菜单项

图像,分隔符

我们将进一步增强对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)

在这里,我们向“文件”菜单添加分隔符。

MenuStrip

图:图像 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",我们将关闭该应用。

ToolBar

图:工具栏

Visual Basic Winforms 教程的这一部分是关于菜单和工具栏的。

对话框

原文: http://zetcode.com/gui/vbwinforms/dialogs/

在 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

图: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

图: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

图: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

图:OpenDialog

在 Visual Basic Winforms 教程的这一部分中,我们显示了各种对话框。

绘图

原文: http://zetcode.com/gui/vbwinforms/painting/

在 Visual Basic Winforms 教程的这一部分中,我们将进行绘图。 当我们想要更改或增强现有控件时,将使用绘图。 或者,如果我们要从头开始创建自定义控件。 要进行绘图,我们使用 Winforms 库提供的绘图 API。 绘图是在一种方法中完成的,我们将其插入Paint事件。

System.Drawing名称空间提供对GDI+基本图形功能的访问。 System.Drawing.Drawing2DSystem.Drawing.ImagingSystem.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)

我们使用指定的阴影刷填充矩形。

Hatches

图:通口

基本形状

下面的示例在窗体控件上绘制一些基本形状。

' 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)

这条线画了一个馅饼。 最后两个参数是起始角度和后掠角度。 以度为单位。

Basic shapes

图:基本形状

透明矩形

透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用 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)

我们用颜色填充矩形。

Transparent rectangles

图:透明矩形

灰度图像

下面的示例创建一个灰度图像。

' 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))

我们修改像素。

Grayscale image

图:灰度图像

绘制文字

要在 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对象。

Lyrics

图:歌词

等待

在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 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 条线。 它们是顺时针旋转的。

Waiting

图:等待

在本章中,我们使用 Visual Basic 在 Winforms 中进行了一些绘图。

拖放

原文: http://zetcode.com/gui/vbwinforms/dragdrop/

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变量告诉我们是否正在拖动对象。 oldXoldY变量在拖动过程开始之前存储 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 坐标与鼠标指针的新坐标之间的差。 差异将添加到按钮的TopLeft属性中,从而将其移动到新位置。

Dragging a button

图:拖动按钮

拖动文字

在前面的示例中,我们确实拖动了控件上的&拖放。 接下来,我们将对文本数据进行拖放操作。 在这里,我们将使用 Winforms 库提供的拖放协议。

拖放操作是 Winforms 中的标准通信协议。 我们有两个基本对象。 drag sourcedrop 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参数指定操作的类型。 实质上,我们可以在拖放操作期间复制文本或移动文本。

Drag & drop of text

图:文本拖放

拖动图像

在最后一个示例中,我们将&拖放图像拖到窗体上。

' 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方法来激活新的画笔颜色。

Drag & drop image

图:拖放图像

本章致力于使用带有 Visual Basic 语言的 Mono Winforms 库拖放操作。

贪食蛇

原文: http://zetcode.com/gui/vbwinforms/nibbles/

在 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

首先,我们将定义游戏中使用的常量。

WIDTHHEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蠕虫的点。 ALL_DOTS常数定义了板上可能的最大点数。 (900 = 300 * 300 / 10 * 10RAND_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

如果蠕虫到达了棋盘的底部,我们就结束了游戏。

下图有助于了解蠕虫对象与板子底部的碰撞。

Collision

图:碰撞

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

这是主要的类。

Nibbles

图:贪食蛇

这是使用 Mono Winforms 库和 Visual Basic 语言编写的贪食蛇游戏。

JavaScript GTK 教程

原文: http://zetcode.com/gui/javascriptgtktutorial/

这是 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 和其他编程语言。

Tweet

相关教程

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()方法设置工具提示。

Tooltip

图:工具提示

退出按钮

在本节的最后一个示例中,我们将创建一个退出按钮。 当我们按下此按钮时,应用终止。

#!/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 = 50y = 50的固定容器中。

w.show_all(); 

我们有两个选择。 在所有小部件上调用show(),或调用show_all()(显示容器及其所有子代)。

Quit button

图:退出按钮

本节介绍了使用 JavaScript 语言的 GTK 库。

布局管理

原文: http://zetcode.com/gui/javascriptgtktutorial/layoutmanagement/

在本章中,我们将展示如何在窗口或对话框中布置窗口小部件。

在设计应用的 GUI 时,我们决定要使用哪些小部件以及如何在应用中组织这些小部件。 为了组织窗口小部件,我们使用专门的不可见窗口小部件,称为布局容器。 在本章中,我们将提到AlignmentFixedVBoxTable

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 = 20y = 20坐标处。

w.add(fixed);

最后,我们将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);

在这里,我们将框架小部件放入垂直框中。 该方法的第一个参数是小部件,它被放置在框中。 以下三个参数是expandfillpaddingexpand参数设置为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成员会将所有可用空间放在水平框的左侧。 这将向右推两个按钮。 我们将水平框添加到对齐容器中,然后将对齐容器包装到垂直框中。 我们必须记住,对齐容器仅包含一个子窗口小部件。 这就是为什么我们必须使用水平框。

Buttons

图:按钮

计算器骨架

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);

我们将表格小部件打包到垂直框中。

Calculator skeleton

图:计算机骨架

窗口

接下来,我们将创建一个更高级的示例。 我们显示一个窗口,可以在 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);

最后,单击确定按钮。 它位于第四列第五行。

Windows

图:窗口

在 JavaScript GTK 教程的这一部分中,我们提到了小部件的布局管理。

posted @ 2024-10-24 18:15  绝不原创的飞龙  阅读(7)  评论(0编辑  收藏  举报