ZetCode-GUI-教程-四-

ZetCode GUI 教程(四)

原文:ZetCode

协议:CC BY-NC-SA 4.0

QtJambi 中的菜单和工具栏

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

在 QtJambi 编程教程的这一部分中,我们将使用菜单和工具栏。

菜单栏是 GUI 应用中最可见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 有公认的标准可以进一步减少学习新应用的时间。 菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。

简单菜单

第一个示例将显示一个简单的菜单。

package com.zetcode;

import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QMainWindow;
import com.trolltech.qt.gui.QMenu;

/**
 * ZetCode QtJambi tutorial
 *
 * This program shows a simple
 * menu. It has one action, which
 * will terminate the program, when
 * selected. 
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QMainWindow {

    public JambiApp() {

        setWindowTitle("Simple menu");

        initUI();

        resize(250, 200);
        move(300, 300);
        show();
    }

    private void initUI() {

        QAction quit = new QAction("&Quit", this);

        QMenu file = menuBar().addMenu("&File");
        file.addAction(quit);

        quit.triggered.connect(QApplication.instance(), "quit()");
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

我们有一个菜单栏,一个菜单和一个动作。 为了使用菜单,我们必须继承QMainWindow小部件。

QAction quit = new QAction("&Quit", this);

此代码行创建一个QAction。 每个QMenu具有一个或多个动作对象。 注意 AND 字符(&)。 它为以下项目创建快捷方式: Alt + Q 。 它还强调了Q字符。 下拉菜单中的文件时,该快捷方式处于活动状态。

QMenu file = menuBar().addMenu("&File");
file.addAction(quit);

我们创建一个QMenu对象。 &字符创建快捷方式: Alt + F 。 连续的快捷键 Alt + FAlt + Q 退出了应用。

quit.triggered.connect(QApplication.instance(), "quit()");

当我们从菜单中选择此选项时,应用退出。

Simple menu

图:简单菜单

创建一个子菜单

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

package com.zetcode;

import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QMainWindow;
import com.trolltech.qt.gui.QMenu;

/**
 * ZetCode QtJambi tutorial
 *
 * This program creates a
 * submenu
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QMainWindow {

    public JambiApp() {

        setWindowTitle("Submenu");

        initUI();

        resize(250, 200);
        move(300, 300);
        show();
    }

    private void initUI() {

        QAction quit = new QAction("&Quit", this);

        QMenu file = menuBar().addMenu("&File");
        QMenu impm = new QMenu("Import");

        QAction seeds = new QAction("Import news feed...", this);
        QAction marks = new QAction("Import bookmarks...", this);
        QAction mail = new QAction("Import mail...", this);
        impm.addAction(seeds);
        impm.addAction(marks);
        impm.addAction(mail);

        file.addMenu(impm);
        file.addAction(quit);

        quit.triggered.connect(QApplication.instance(), "quit()");
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在示例中,文件菜单的子菜单中有三个选项。

QMenu file = menuBar().addMenu("&File");
QMenu impm = new QMenu("Import");

我们有两个QMenu对象。 文件菜单和导入菜单。

QAction seeds = new QAction("Import news feed...", this);
QAction marks = new QAction("Import bookmarks...", this);
QAction mail = new QAction("Import mail...", this);

我们创建三个动作对象。

impm.addAction(seeds);
impm.addAction(marks);
impm.addAction(mail);

我们将动作对象添加到导入菜单中。

file.addMenu(impm);

最后,我们将导入菜单添加到文件菜单中。

Submenu

图:子菜单

图像,菜单,分隔符

在以下示例中,我们将进一步增强以前的应用。 我们将在菜单中添加图标,使用快捷方式和分隔符。

package com.zetcode;

import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QIcon;
import com.trolltech.qt.gui.QMainWindow;
import com.trolltech.qt.gui.QMenu;

/**
 * ZetCode QtJambi tutorial
 *
 * This program shows image
 * menus, shortcuts and a separator
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QMainWindow {

    public JambiApp() {

        setWindowTitle("Image menu");

        initUI();

        resize(250, 200);
        move(300, 300);
        show();
    }

    private void initUI() {

          QIcon newpix = new QIcon("new.png");
          QIcon openpix = new QIcon("open.png");
          QIcon quitpix = new QIcon("quit.png");

          QAction newa = new QAction(newpix, "&New", this);
          QAction open = new QAction(openpix, "&Open", this);
          QAction quit = new QAction(quitpix, "&Quit", this);
          quit.setShortcut("Ctrl+Q");

          QMenu file;
          file = menuBar().addMenu("&File");
          file.addAction(newa);
          file.addAction(open);
          file.addSeparator();
          file.addAction(quit);

          quit.triggered.connect(QApplication.instance(), "quit()");
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在我们的示例中,我们有一个包含三个动作的菜单。 如果我们选择退出操作,则实际上只有退出操作才可以执行某些操作。 我们还创建了分隔符和 Ctrl + Q 快捷方式,它们将终止应用。

QIcon newpix = new QIcon("new.png");
QIcon openpix = new QIcon("open.png");
QIcon quitpix = new QIcon("quit.png");

这些是我们将在应用中使用的 PNG 图像。

QAction newa = new QAction(newpix, "&New", this);
QAction open = new QAction(openpix, "&Open", this);
QAction quit = new QAction(quitpix, "&Quit", this);

在这里,我们创建三个动作对象。 第一个参数是QIcon

quit.setShortcut("Ctrl+Q");

这行创建一个快捷方式。 通过按下此快捷方式,我们将运行退出操作,这将退出应用。

file.addSeparator();

我们创建一个分隔符。 分隔符是一条水平线,它使我们能够将菜单操作分组为一些逻辑部分。

Images, shortcut and a separator

图:图像 s, shortcut and a separator

工具栏

QToolBar类提供了一个可移动面板,其中包含一组控件,这些控件提供对应用操作的快速访问。

package com.zetcode;

import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QIcon;
import com.trolltech.qt.gui.QMainWindow;
import com.trolltech.qt.gui.QToolBar;

/**
 * ZetCode QtJambi tutorial
 *
 * This program creates a 
 * toolbar
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified April 2009
 */

public class JambiApp extends QMainWindow {

    public JambiApp() {

        setWindowTitle("Toolbar");

        initUI();

        resize(250, 200);
        move(300, 300);
        show();
    }

    private void initUI() {

        QIcon newpi = new QIcon("new.png");
        QIcon openpi = new QIcon("open.png");
        QIcon quitpi = new QIcon("quit.png");

        QToolBar toolbar = addToolBar("main toolbar");
        toolbar.addAction(newpi, "New File");
        toolbar.addAction(openpi, "Open File");
        toolbar.addSeparator();
        QAction quit = toolbar.addAction(quitpi,
            "Quit Application");

        quit.triggered.connect(QApplication.instance(), "quit()");

    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

我们创建一个带有三个动作对象和一个分隔符的工具栏。

QIcon newpi = new QIcon("new.png");
QIcon openpi = new QIcon("open.png");
QIcon quitpi = new QIcon("quit.png");

工具栏动作对象将显示这些图标。

QToolBar toolbar = addToolBar("main toolbar");

QMainWindowaddToolBar()方法为应用创建一个工具栏。 文本字符串为工具栏命名。 此名称用于引用此工具栏,因为一个应用中可以有多个工具栏。 如果右键单击窗口区域,我们将看到一个可检查的选项,该选项显示/隐藏工具栏。

toolbar.addSeparator();

我们创建一个垂直分隔符。

quit.triggered.connect(QApplication.instance(), "quit()");

当我们单击退出操作对象时,应用终止。

Toolbar

图:工具栏

撤销重做

以下示例演示了如何停用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法。 例如,保存按钮。 如果我们将文档的所有更改都保存到磁盘上,则在大多数文本编辑器中,“保存”按钮将被停用。 这样,应用会向用户指示所有更改都已保存。

package com.zetcode;

import com.trolltech.qt.QSignalEmitter;
import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QIcon;
import com.trolltech.qt.gui.QMainWindow;
import com.trolltech.qt.gui.QToolBar;

/**
 * ZetCode QtJambi tutorial
 *
 * This program disables/enables
 * actions on a toolbar
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QMainWindow {

    private int count = 0;
    private QAction undo;
    private QAction redo;

    public JambiApp() {

        setWindowTitle("Undo redo");

        initUI();

        resize(300, 200);
        move(400, 300);
        show();
    }

    private void initUI() {

        QIcon undoi = new QIcon("undo.png");
        QIcon redoi = new QIcon("redo.png");
        QIcon quitpi = new QIcon("quit.png");

        QToolBar toolbar = addToolBar("first toolbar");

        undo = toolbar.addAction(undoi, "Undo");
        redo = toolbar.addAction(redoi, "Redo");

        undo.triggered.connect(this, "count()");
        redo.triggered.connect(this, "count()");

        toolbar.addSeparator();

        QAction quit = toolbar.addAction(quitpi, "Quit Application");
        quit.triggered.connect(QApplication.instance(), "quit()");

    }

    private void count() {

        QAction action = (QAction) QSignalEmitter.signalSender();

        if ("Undo".equals(action.text())) {
            count += -1;
        } else {
            count += 1;
        }

        if (count <= 0) {
            undo.setDisabled(true);
            redo.setDisabled(false);
        }

        if (count >= 5) {
            undo.setDisabled(false);
            redo.setDisabled(true);
        }
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在我们的示例中,我们有三个QAction对象和一个分隔符。 在撤消或重做按钮上单击几下后,它们将被停用。 外观上,按钮显示为灰色。

private int count = 0;

计数变量确定哪个按钮被激活和停用。

undo.triggered.connect(this, "count()");
redo.triggered.connect(this, "count()");

单击工具栏按钮,将发射triggered信号。 我们将此信号连接到count()方法。

QAction action = (QAction) QSignalEmitter.signalSender();

工具栏上的两个按钮都调用count()方法。 我们需要在它们之间说出来。 这条线确定哪个动作对象实际发出信号。

if ("Undo".equals(action.text())) {
    count += -1;
} else {
    count += 1;
}

撤消工具栏按钮从计数变量中减去 1。 重做添加 1.根据计数变量的值,我们启用/禁用工具栏按钮。

if (count <= 0) {
    undo.setDisabled(true);
    redo.setDisabled(false);
}

setDisabled()方法激活或停用工具栏按钮。

Undo redo

图:撤销和重做

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

QtJambi 对话框

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

在 QtJambi 编程教程的这一部分中,我们将使用对话框。

对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。

MessageDialog

消息框是方便的对话框,可向用户提供应用消息。 该消息由文本和图像数据组成。

package com.zetcode;

import com.trolltech.qt.QSignalEmitter;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QGridLayout;
import com.trolltech.qt.gui.QMessageBox;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * This program demonstrates
 * QMessageBox dialogs
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    public JambiApp() {

        setWindowTitle("Message Dialogs");

        initUI();

        resize(220, 90);
        move(400, 300);
        show();
    }

    private void initUI() {

        QGridLayout grid = new QGridLayout(this);
        grid.setSpacing(2);

        QPushButton error = new QPushButton("Error", this);
        QPushButton warning = new QPushButton("Warning", this);
        QPushButton question = new QPushButton("Question", this);
        QPushButton information = new QPushButton("Information", this);
        QPushButton about = new QPushButton("About", this);

        grid.addWidget(error, 0, 0);
        grid.addWidget(warning, 0, 1);
        grid.addWidget(question, 1, 0);
        grid.addWidget(information, 1, 1);
        grid.addWidget(about, 2, 0);

        error.clicked.connect(this, "showDialog()");
        warning.clicked.connect(this, "showDialog()");
        question.clicked.connect(this, "showDialog()");
        information.clicked.connect(this, "showDialog()");
        about.clicked.connect(this, "showDialog()");
    }

    private void showDialog() {

        QPushButton button = (QPushButton) QSignalEmitter.signalSender();

        if ("Error".equals(button.text())) {
            QMessageBox.critical(this, "Error", "Error loading file!");
        } else if ("Warning".equals(button.text())) {
            QMessageBox.warning(this, "Warning", "Operation not permitted!");
        } else if ("Question".equals(button.text())) {
            QMessageBox.question(this, "Question", "Are you sure to quit?");
        } else if ("Information".equals(button.text())) {
            QMessageBox.information(this, "Information", "Download completed.");
        } else if ("About".equals(button.text())) {
            QMessageBox.about(this, "About", "ZetCode QtJambi tutorial.");
        }
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

我们使用GridLayout管理器来设置五个按钮的网格。 每个按钮显示一个不同的消息框。

QPushButton button = (QPushButton) QSignalEmitter.signalSender();

在这里,我们确定哪个按钮称为showDialog()方法。

if ("Error".equals(button.text())) {
    QMessageBox.critical(this, "Error", "Error loading file!");
}

如果按下错误按钮,则会显示错误对话框。 我们使用QMessageBox类的静态方法来显示消息框。

Information message dialog

Warning message dialog

Question message dialog

Error message dialog

About message dialog

QInputDialog

QInputDialog类提供了一个简单的便捷对话框,可从用户那里获取单个值。 输入值可以是字符串,数字或列表中的项目。 必须设置标签以告知用户他们应该输入什么。

package com.zetcode;

import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QInputDialog;
import com.trolltech.qt.gui.QLineEdit;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 * 
 * This program shows an input
 * dialog
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    QLineEdit edit;

    public JambiApp() {

        setWindowTitle("Input Dialog");

        initUI();

        move(400, 300);
        show();
    }

    private void initUI() {

        setGeometry(300, 300, 350, 80);

        QPushButton show = new QPushButton("Dialog", this);

        show.clicked.connect(this, "showDialog()");
        show.setFocusPolicy(Qt.FocusPolicy.NoFocus);

        show.move(20, 20);

        edit = new QLineEdit(this);
        edit.move(130, 22);
    }

    private void showDialog() {

        String text = QInputDialog.getText(
                this, "Input Dialog", "Enter your name");

        if (text!=null && !text.trim().isEmpty()) {
            edit.setText(text);
        }
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在代码示例中,我们有一个按钮和一行编辑。 该按钮显示一个输入对话框。 我们得到一些文本,文本显示在行编辑小部件中。

String text = QInputDialog.getText(
        this, "Input Dialog", "Enter your name");

getText()静态方法创建输入对话框。 对话框中的文本存储在text变量中。

if (text!=null && !text.trim().isEmpty()) {
    edit.setText(text);
}

在更新行编辑之前,请确保text变量不为null且不为空,并且不仅由空格组成。

Input dialog

图:输入对话框

QColorDialog

QColorDialog类提供用于指定颜色的对话框小部件。 颜色对话框的功能是允许用户选择颜色。

package com.zetcode;

import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QColorDialog;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QMouseEvent;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;
import java.util.Formatter;

/**
 * ZetCode QtJambi tutorial
 *
 * In this program, we use the
 * QColorDialog to change the color
 * of a label text
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    QLabel label;

    public JambiApp() {

        setWindowTitle("Color Dialog");

        initUI();

        resize(250, 200);
        move(400, 300);
        show();
    }

    private void initUI() {

        label = new QLabel("ZetCode QtJambi tutorial", this);

        QVBoxLayout vbox = new QVBoxLayout(this);
        label.setAlignment(Qt.AlignmentFlag.AlignCenter);
        vbox.addWidget(label);

    }

    @Override
    public void mousePressEvent(QMouseEvent event) {

        QColor color = QColorDialog.getColor();

        if (!color.isValid()) return;

        Formatter fmt = new Formatter();
        fmt.format("QWidget { color: %s }", color.name());

        label.setStyleSheet(fmt.toString());
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

我们在窗口中心显示一些文本。 通过单击窗口区域,我们显示一个颜色对话框。 我们将文本前景色更改为从对话框中选择的颜色。

@Override
public void mousePressEvent(QMouseEvent event) {
    ...
}

为了接收我们窗口的鼠标按下事件,我们必须重新实现mousePressEvent()方法。

QColor color = QColorDialog.getColor();

正在创建QColorDialog。 所选颜色存储在color变量中。

Formatter fmt = new Formatter();
fmt.format("QWidget { color: %s }", color.name());

label.setStyleSheet(fmt.toString());

在这里,我们更新标签文本的前景色。

QColorDialog

图:QColorDialog

QFontDialog

QFontDialog类提供用于选择字体的对话框小部件。

package com.zetcode;

import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QFontDialog;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QMouseEvent;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * In this program, we use the
 * QFontDialog to change the font
 * of a label text
 * 
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    QLabel label;

    public JambiApp() {

        setWindowTitle("QFontColor dialog");

        initUI();

        resize(250, 200);
        move(300, 300);
        show();
    }

    private void initUI() {

        label = new QLabel("ZetCode QtJambi tutorial", this);

        QVBoxLayout vbox = new QVBoxLayout(this);
        label.setAlignment(Qt.AlignmentFlag.AlignCenter);
        vbox.addWidget(label);
    }

    @Override
    public void mousePressEvent(QMouseEvent event) {

        QFontDialog.Result result = QFontDialog.getFont();

        if (!result.ok) return;

        label.setFont(result.font);
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

此示例与上一个示例相似。 这次,我们更改文本的字体。

QFontDialog.Result result = QFontDialog.getFont();

正在创建QFontDialog。 该对话框返回QFontDialog.Result类。 此类有两个字段。 字体和确定字段。

if (!result.ok) return;

如果单击对话框的“确定”按钮,则布尔值oktrue。 如果按下了取消按钮,我们将从方法中返回。

label.setFont(result.font);

font字段存储所选字体。 我们将标签的字体更新为新选择的字体。

QFontDialog

图:QFontDialog

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

QtJambi 中的绘图

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

在 QtJambi 编程教程的这一部分中,我们将进行绘图。

我们什么时候需要油漆? 在某些情况下,当我们需要从头开始创建小部件时。 在这种情况下,我们需要绘图。 或者我们想创建图表,特殊装饰,效果或小部件增强。

当我们在 QtJambi 库中进行绘图时,QPainter类将发挥作用。 绘图事件通过paintEvent()方法接收。 若要进行自定义绘图,我们必须重新实现此方法。

图案

在 QtJambi 中,我们可以使用各种图案来填充形状的内部。

package com.zetcode;

import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QPen;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * This program draws nine rectangles.
 * The interiors are filled with
 * different built-in patterns.
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    public JambiApp() {

        setWindowTitle("Patterns");

        resize(350, 280);
        move(400, 300);
        show();
    }

    @Override
    protected void paintEvent(QPaintEvent event) {

        QPainter painter = new QPainter(this);
        drawPatterns(painter);
    }

    private void drawPatterns(QPainter painter) {

          painter.setPen(QPen.NoPen);

          painter.setBrush(Qt.BrushStyle.HorPattern);
          painter.drawRect(10, 15, 90, 60);

          painter.setBrush(Qt.BrushStyle.VerPattern);
          painter.drawRect(130, 15, 90, 60);

          painter.setBrush(Qt.BrushStyle.CrossPattern);
          painter.drawRect(250, 15, 90, 60);

          painter.setBrush(Qt.BrushStyle.Dense7Pattern);
          painter.drawRect(10, 105, 90, 60);

          painter.setBrush(Qt.BrushStyle.Dense6Pattern);
          painter.drawRect(130, 105, 90, 60);

          painter.setBrush(Qt.BrushStyle.Dense5Pattern);
          painter.drawRect(250, 105, 90, 60);

          painter.setBrush(Qt.BrushStyle.BDiagPattern);
          painter.drawRect(10, 195, 90, 60);

          painter.setBrush(Qt.BrushStyle.FDiagPattern);
          painter.drawRect(130, 195, 90, 60);

          painter.setBrush(Qt.BrushStyle.DiagCrossPattern);
          painter.drawRect(250, 195, 90, 60);

          painter.end();
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在代码示例中,我们将绘制九个矩形,并用不同的画笔图案填充它们。

@Override
protected void paintEvent(QPaintEvent event) {

    QPainter painter = new QPainter(this);
    drawPatterns(painter);
}

当需要重绘窗口区域时,将调用paintEvent()方法。 当我们调整窗口大小,最大化或最小化窗口时,就会发生这种情况。在此方法中,我们创建了QPainter对象。 此对象用于完成 QtJambi 中的所有绘图。 绘图本身被委托给drawPatterns()方法。

painter.setPen(QPen.NoPen);

笔对象用于绘制形状的轮廓。 在我们的示例中,我们将不使用笔。

painter.setBrush(Qt.BrushStyle.HorPattern);

我们将水平图案设置为画笔。

painter.drawRect(10, 15, 90, 60);

我们使用当前的笔和画笔绘制一个矩形。 该方法的前两个参数是 x,y 坐标。 最后两个参数是矩形的宽度和高度。

painter.end();

结束绘图。 释放绘图时使用的所有资源。 根据文档,在 QtJambi 中不需要此调用。 资源由析构器释放。

Patterns

图:图案

形状

QtJambi 绘图 API 可以绘制各种形状。 以下编程代码示例将显示其中的一些。

package com.zetcode;

import com.trolltech.qt.core.QPoint;
import com.trolltech.qt.core.QPointF;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QBrush;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QPainterPath;
import com.trolltech.qt.gui.QPen;
import com.trolltech.qt.gui.QPolygon;
import com.trolltech.qt.gui.QWidget;
import java.util.ArrayList;
import java.util.List;

/**
 * ZetCode QtJambi tutorial
 *
 * This program draws basic shapes
 * available in QtJambi
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    public JambiApp() {

        setWindowTitle("Shapes");

        resize(350, 280);
        move(400, 300);
        show();
    }

    @Override
    protected void paintEvent(QPaintEvent event) {

        QPainter painter = new QPainter(this);
        drawShapes(painter);
    }

    private void drawShapes(QPainter painter) {

        painter.setRenderHint(QPainter.RenderHint.Antialiasing);
        painter.setPen(new QPen(new QBrush(QColor.darkGray), 1));
        painter.setBrush(QColor.darkGray);

        QPainterPath path1 = new QPainterPath();

        path1.moveTo(5, 5);
        path1.cubicTo(40, 5,  50, 50,  99, 99);
        path1.cubicTo(5, 99,  50, 50,  5, 5);
        painter.drawPath(path1);

        painter.drawPie(130, 20, 90, 60, 30*16, 120*16);
        painter.drawChord(240, 30, 90, 60, 0, 16*180);
        painter.drawRoundRect(20, 120, 80, 50);

        List<QPoint> points = new ArrayList<QPoint>();
        points.add(new QPoint(130, 140));
        points.add(new QPoint(180, 170));
        points.add(new QPoint(180, 140));
        points.add(new QPoint(220, 110));
        points.add(new QPoint(140, 100));

        QPolygon polygon = new QPolygon(points);

        painter.drawPolygon(polygon);
        painter.drawRect(250, 110, 60, 60);

        QPointF baseline = new QPointF(20, 250);
        QFont font = new QFont("Georgia", 55);
        QPainterPath path2 = new QPainterPath();
        path2.addText(baseline, font, "Q");
        painter.drawPath(path2);

        painter.drawEllipse(140, 200, 60, 60);
        painter.drawEllipse(240, 200, 90, 60);
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在此代码示例中,我们在窗口上绘制了九种不同的形状。 复杂路径,饼图,和弦,圆角矩形,多边形,矩形,基于字符的形状,圆形和椭圆形。

painter.setRenderHint(QPainter.RenderHint.Antialiasing);

我们在示例中使用抗锯齿。 抗锯齿形状看起来更好,但是绘制它们需要更多时间。

painter.setPen(new QPen(new QBrush(QColor.darkGray), 1));
painter.setBrush(QColor.darkGray);

我们使用深灰色的笔和画笔绘制形状。

QPainterPath path1 = new QPainterPath();

path1.moveTo(5, 5);
path1.cubicTo(40, 5,  50, 50,  99, 99);
path1.cubicTo(5, 99,  50, 50,  5, 5);
painter.drawPath(path1);

使用QPainterPath对象创建第一个复杂形状。 QPainterPath类为绘图操作提供了一个容器。 画家路径是由许多图形构造块(例如矩形,椭圆形,直线和曲线)组成的对象。

painter.drawPie(130, 20, 90, 60, 30*16, 120*16);
painter.drawChord(240, 30, 90, 60, 0, 16*180);
painter.drawRoundRect(20, 120, 80, 50);

这三行画出一个饼图,一个和弦和一个圆角矩形。

List<QPoint> points = new ArrayList<QPoint>();
points.add(new QPoint(130, 140));
points.add(new QPoint(180, 170));
points.add(new QPoint(180, 140));
points.add(new QPoint(220, 110));
points.add(new QPoint(140, 100));

QPolygon polygon = new QPolygon(points);

painter.drawPolygon(polygon);

我们使用五个点的列表来创建多边形。

QPointF baseline = new QPointF(20, 250);
QFont font = new QFont("Georgia", 55);
QPainterPath path2 = new QPainterPath();
path2.addText(baseline, font, "Q");
painter.drawPath(path2);

这些线创建基于字符的形状。

painter.drawEllipse(140, 200, 60, 60);
painter.drawEllipse(240, 200, 90, 60);

这两条线分别创建一个圆和一个椭圆。

Shapes

图:形状

透明矩形

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

在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)

package com.zetcode;

import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QPen;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * This program draws ten
 * rectangles with different
 * levels of transparency
 * 
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    public JambiApp() {

        setWindowTitle("Transparent rectangles");

        resize(590, 90);
        move(400, 300);
        show();
    }

    @Override
    protected void paintEvent(QPaintEvent event) {

        QPainter painter = new QPainter(this);
        drawRectangles(painter);

    }

    private void drawRectangles(QPainter painter) {

        painter.setPen(QPen.NoPen);

        for (int i=1; i<11; i++) {
            painter.setBrush(new QColor(0, 0, 255, i*25));
            painter.drawRect(50*i, 20, 40, 40);
        }
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在示例中,我们将绘制十个具有不同透明度级别的矩形。

painter.setPen(QPen.NoPen);

我们不用笔。

for (int i=1; i<11; i++) {
    painter.setBrush(new QColor(0, 0, 255, i*25));
    painter.drawRect(50*i, 20, 40, 40);
}

QColor 对象的最后一个参数是 alpha 透明度值。

Transparent rectangles

图:透明矩形

甜甜圈形状

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。

package com.zetcode;

import com.trolltech.qt.core.QPoint;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QPen;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * This program draws a donut
 * shape
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    public JambiApp() {

        setWindowTitle("Donut");

        resize(350, 280);
        move(400, 300);
        show();
    }

    @Override
    protected void paintEvent(QPaintEvent event) {

        QPainter painter = new QPainter(this);
        drawDonut(painter);

    }

    private void drawDonut(QPainter painter) {

        QColor color = new QColor();
        color.setNamedColor("#333333");

        painter.setPen(new QPen(color, 0.5));

        painter.setRenderHint(QPainter.RenderHint.Antialiasing);

        int w = width();
        int h = height();

        painter.translate(new QPoint(w/2, h/2));

         for (double rot=0; rot < 360.0; rot+=5.0 ) {
             painter.drawEllipse(-125, -40, 250, 80);
             painter.rotate(5.0);
         }
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。

QColor color = new QColor();
color.setNamedColor("#333333");

我们可以使用十六进制表示法来创建颜色对象。

int w = width();
int h = height();

在这里,我们确定窗口的宽度和高度。

painter.translate(new QPoint(w/2, h/2));

我们将坐标系移到窗口的中间。 这样,我们使绘图在数学上更容易。

for (double rot=0; rot < 360.0; rot+=5.0 ) {
    painter.drawEllipse(-125, -40, 250, 80);
    painter.rotate(5.0);
}

我们绘制一个椭圆对象 72 次。 每次,我们将椭圆旋转 5 度。 这将创建我们的甜甜圈形状。

Donut

图:多纳圈

绘制文字

在最后一个示例中,我们将在窗口上绘制文本。

package com.zetcode;

import com.trolltech.qt.core.QPoint;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * This program draws text
 * on the window
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QWidget {

    public JambiApp() {

        setWindowTitle("Soulmate");

        resize(370, 240);
        move(400, 300);
        show();
    }

    @Override
    protected void paintEvent(QPaintEvent event) {

        QPainter painter = new QPainter(this);
        drawLyrics(painter);

    }

    private void drawLyrics(QPainter painter) {

        painter.setBrush(new QColor(25, 25, 25));
        painter.setFont(new QFont("Purisa", 10));

        painter.drawText(new QPoint(20, 30),
                "Most relationships seem so transitory");
        painter.drawText(new QPoint(20, 60),
                "They're good but not the permanent one");
        painter.drawText(new QPoint(20, 120),
                "Who doesn't long for someone to hold");
        painter.drawText(new QPoint(20, 150),
                "Who knows how to love without being told");
        painter.drawText(new QPoint(20, 180),
                "Somebody tell me why I'm on my own");
        painter.drawText(new QPoint(20, 210),
                "If there's a soulmate for everyone");
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

我们在窗口上画一首歌歌词。

painter.setFont(new QFont("Purisa", 10));

我们为文本设置了 Purisa 字体。

painter.drawText(new QPoint(20, 30),
        "Most relationships seem so transitory");

drawText()方法用于绘制文本。

Drawing text

图:绘制文本

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

QtJambi 中的自定义小部件

原文: http://zetcode.com/gui/qtjambi/customwidget/

在 QtJambi 编程教程的这一部分中,我们将创建一个自定义小部件。

工具箱通常仅提供最常见的窗口小部件,例如按钮,文本窗口小部件,滑块等。没有工具箱可以提供所有可能的窗口小部件。 程序员必须自己创建此类小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能性。 程序员可以修改或增强现有的小部件。 或者,他可以从头开始创建自定义窗口小部件。

刻录小部件

在下一个示例中,我们将创建一个自定义刻录小部件。 可以在 Nero 或 K3B 之类的应用中看到此小部件。 该小部件将从头开始创建。

Burning.java

package com.zetcode;

import com.trolltech.qt.core.QPointF;
import com.trolltech.qt.core.QRectF;
import com.trolltech.qt.gui.QBrush;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QFontMetrics;
import com.trolltech.qt.gui.QLineF;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;
import com.trolltech.qt.gui.QWidget;

public class Burning extends QWidget {

    private final int PANEL_HEIGHT = 30;
    private final int DISTANCE = 19;
    private final int LINE_WIDTH = 5;
    private final int DIVISIONS = 10;
    private final float FULL_CAPACITY = 700f;
    private final float MAX_CAPACITY = 750f;
    private final QColor redColor = new QColor(255, 175, 175);
    private final QColor yellowColor = new QColor(255, 255, 184);

    private QWidget parent;
    private String num[] = {
        "75", "150", "225", "300",
        "375", "450", "525", "600",
        "675"
    };

    public Burning(QWidget parent) {
        super(parent);

        this.parent = parent;
        setMinimumHeight(PANEL_HEIGHT);
    }

    @Override
    protected void paintEvent(QPaintEvent event) {

        QPainter painter = new QPainter(this);

        drawWidget(painter);
        painter.end();
    }

    protected void drawWidget(QPainter painter) {

        JambiApp burn = (JambiApp) parent;

        float width = size().width();        
        float slid_width = burn.getCurrentWidth();
        float step = width / DIVISIONS;

        float till = (width / MAX_CAPACITY) * slid_width;
        float full = (width / MAX_CAPACITY) * FULL_CAPACITY;

        if (slid_width > FULL_CAPACITY) {

            painter.setPen(yellowColor);
            painter.setBrush(yellowColor);
            painter.drawRect(new QRectF(0, 0, full, PANEL_HEIGHT));
            painter.setPen(redColor);
            painter.setBrush(redColor);
            painter.drawRect(new QRectF(full+1, 0, till-full, PANEL_HEIGHT));

        } else {
            if (slid_width > 0) {
               painter.setPen(yellowColor);
               painter.setBrush(yellowColor);
               painter.drawRect(new QRectF(0, 0, till, PANEL_HEIGHT));
            }
        }

        painter.setPen(new QColor(90, 90, 90));
        painter.setBrush(QBrush.NoBrush);
        painter.drawRect(0, 0, size().width()-1, PANEL_HEIGHT-1);

        QFont newFont = font();
        newFont.setPointSize(7);
        painter.setFont(newFont);

        for (int i = 1; i <= num.length; i++) {
            painter.drawLine(new QLineF(i*step, 1, i*step, LINE_WIDTH));

            QFontMetrics metrics = new QFontMetrics(newFont);

            int w = metrics.width(num[i-1]);
            painter.drawText(new QPointF(i*step-w/2, DISTANCE), num[i-1]);

        }        
    }
}

在这个文件中,我们创建了刻录小部件。

public class Burning extends QWidget {

自定义窗口小部件基于QWidget小部件。

private final int PANEL_HEIGHT = 30;
private final int DISTANCE = 19;
private final int LINE_WIDTH = 5;
private final int DIVISIONS = 10;
private final float FULL_CAPACITY = 700f;
private final float MAX_CAPACITY = 750f;

这些是重要的常数。 PANEL_HEIGHT定义自定义窗口小部件的高度。 DISTANCE是比例尺上的数字与其父边框顶部之间的距离。 LINE_WIDTH是垂直线的宽度。 DIVISIONS是秤的数量。 FULL_CAPACITY是媒体的容量。 达到目标后,就会发生过度刻录。 用红色显示。 MAX_CAPACITY是介质的最大容量。

private String num[] = {
    "75", "150", "225", "300",
    "375", "450", "525", "600",
    "675"
};

我们使用这些数字来构建刻录小部件的比例。

@Override
protected void paintEvent(QPaintEvent event) {

    QPainter painter = new QPainter(this);

    drawWidget(painter);
    painter.end();
}

自定义窗口小部件的图形委托给drawWidget()方法。

JambiApp burn = (JambiApp) parent;

我们检索对父窗口小部件的引用。

float slid_width = burn.getCurrentWidth();

我们使用它来获取当前选定的滑块值。

float width = size().width();

我们得到小部件的宽度。 自定义窗口小部件的宽度是动态的。 用户可以调整大小。

float till = (width / MAX_CAPACITY) * slid_width;
float full = (width / MAX_CAPACITY) * FULL_CAPACITY;

我们使用width变量进行转换。 在比例尺值和自定义小部件的度量之间。 请注意,我们使用浮点值。 我们在绘图中获得了更高的精度。

painter.setPen(redColor);
painter.setBrush(redColor);
painter.drawRect(new QRectF(full+1, 0, till-full, PANEL_HEIGHT));

这三行画出红色矩形,表示过度燃烧。

painter.drawRect(0, 0, size().width()-1, PANEL_HEIGHT-1);

这是小部件的周长。 外部矩形。

painter.drawLine(new QLineF(i*step, 1, i*step, LINE_WIDTH));

在这里,我们画出小的垂直线。

int w = metrics.width(num[i-1]);
painter.drawText(new QPointF(i*step-w/2, 19), num[i-1]);

在这里,我们绘制刻度的数字。 为了精确定位数字,我们必须获得字符串的宽度。

JambiApp.java

package com.zetcode;

import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QFrame;
import com.trolltech.qt.gui.QHBoxLayout;
import com.trolltech.qt.gui.QSlider;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;

/**
 * ZetCode QtJambi tutorial
 *
 * In this program, we create
 * a custom widget
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class JambiApp extends QFrame {

    private final int MAX_CAPACITY = 750;

    QSlider slider;
    QWidget widget;
    int cur_width;

    public JambiApp() {
        setWindowTitle("The Burning Widget");

        initUI();

        resize(370, 200);
        move(300, 300);
        show();
    }

    private void initUI() {

       slider = new QSlider(Qt.Orientation.Horizontal , this);
       slider.setMaximum(MAX_CAPACITY);
       slider.setGeometry(50, 50, 130, 30);

       slider.valueChanged.connect(this, "valueChanged(int)");

       QVBoxLayout vbox = new QVBoxLayout(this);
       QHBoxLayout hbox = new QHBoxLayout();

       vbox.addStretch(1);

       widget = new Burning(this);
       hbox.addWidget(widget, 0);

       vbox.addLayout(hbox);

       setLayout(vbox);
    }

    public void valueChanged(int val) {
        cur_width = val;
        widget.repaint();
    }

    public int getCurrentWidth() {
      return cur_width;
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new JambiApp();
        QApplication.exec();
    }
}

这是主文件。 在这里,我们创建滑块小部件并使用我们的自定义小部件。

widget = new Burning(this);
hbox.addWidget(widget, 0);

我们创建了刻录小部件的实例,并将其添加到水平框中。

public void valueChanged(int val) {
    cur_width = val;
    widget.repaint();
}

当滑块的值更改时,我们将其存储在cur_width变量中,然后重新绘制自定义窗口小部件。

public int getCurrentWidth() {
  return cur_width;
}

定制小部件调用此方法以获取实际的滑块值。

The Burning widget

图:刻录小部件

在 QtJambi 教程的这一部分中,我们演示了如何创建自定义窗口小部件。

贪食蛇

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

在 QtJambi 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。

贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。

开发

蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。

Board.java

package com.zetcode;

import com.trolltech.qt.core.QBasicTimer;
import com.trolltech.qt.core.QPoint;
import com.trolltech.qt.core.QTimerEvent;
import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QFontMetrics;
import com.trolltech.qt.gui.QFrame;
import com.trolltech.qt.gui.QImage;
import com.trolltech.qt.gui.QKeyEvent;
import com.trolltech.qt.gui.QPaintEvent;
import com.trolltech.qt.gui.QPainter;

public class Board extends QFrame {

    private final int WIDTH = 300;
    private final int HEIGHT = 300;
    private final int DOT_SIZE = 10;
    private final int ALL_DOTS = 900;
    private final int RAND_POS = 29;
    private final int DELAY = 140;

    private int x[] = new int[ALL_DOTS];
    private int y[] = new int[ALL_DOTS];

    private int dots;
    private int apple_x;
    private int apple_y;

    private boolean left = false;
    private boolean right = true;

    private boolean up = false;
    private boolean down = false;
    private boolean inGame = true;

    private QBasicTimer timer;
    private QImage ball;
    private QImage apple;
    private QImage head;

    public Board() {

        setStyleSheet("QWidget { background-color: black }");

        setFocusPolicy(Qt.FocusPolicy.StrongFocus);

        ball = new QImage("dot.png");
        apple = new QImage("apple.png");
        head = new QImage("head.png");

        initGame();
    }

    private void initGame() {

        dots = 3;

        for (int z = 0; z < dots; z++) {
            x[z] = 50 - z*10;
            y[z] = 50;
        }

        locateApple();

        timer = new QBasicTimer();
        timer.start(DELAY, this);
    }

    @Override
    public void paintEvent(QPaintEvent event) {
        super.paintEvent(event);

        QPainter painter = new QPainter();
        painter.begin(this);

        if (inGame) {
            drawObjects(painter);
        } else {
            gameOver(painter);
        }

        painter.end();
    }

    private void drawObjects(QPainter painter) {

        painter.drawImage(apple_x, apple_y, apple);

        for (int z = 0; z < dots; z++) {
            if (z == 0)
                painter.drawImage(x[z], y[z], head);
            else painter.drawImage(x[z], y[z], ball);
        }
    }

    private void gameOver(QPainter painter) {
        String msg = "Game Over";
        QFont small = new QFont("Helvetica", 12,
            QFont.Weight.Bold.value());
        QFontMetrics metr = new QFontMetrics(small);

        int textWidth = metr.width(msg);
        int h = height();
        int w = width();

        painter.setPen(QColor.white);
        painter.setFont(small);
        painter.translate(new QPoint(w/2, h/2));
        painter.drawText(-textWidth/2, 0, msg);
    }

    private void checkApple() {

        if ((x[0] == apple_x) && (y[0] == apple_y)) {
            dots++;
            locateApple();
        }
    }

    private void move() {

        for (int z = dots; z > 0; z--) {
            x[z] = x[(z - 1)];
            y[z] = y[(z - 1)];
        }

        if (left) {
            x[0] -= DOT_SIZE;
        }

        if (right) {
            x[0] += DOT_SIZE;
        }

        if (up) {
            y[0] -= DOT_SIZE;
        }

        if (down) {
            y[0] += DOT_SIZE;
        }
    }

    private void checkCollision() {

        for (int z = dots; z > 0; z--) {

            if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
                inGame = false;
            }
        }

        if (y[0] > HEIGHT) {
            inGame = false;
        }

        if (y[0] < 0) {
            inGame = false;
        }

        if (x[0] > WIDTH) {
            inGame = false;
        }

        if (x[0] < 0) {
            inGame = false;
        }
    }

    private void locateApple() {
        int r = (int) (Math.random() * RAND_POS);
        apple_x = ((r * DOT_SIZE));
        r = (int) (Math.random() * RAND_POS);
        apple_y = ((r * DOT_SIZE));
    }

    @Override
    protected void timerEvent(QTimerEvent event) {

        if (inGame) {
            checkApple();
            checkCollision();
            move();
        } else {
            timer.stop();
        }

        repaint();
    }

    @Override
    public void keyPressEvent(QKeyEvent event)
    {

        int key = event.key();

        if (key == Qt.Key.Key_Left.value() && !right) {
            left = true;
            up = false;
            down = false;
        }

        if ((key == Qt.Key.Key_Right.value()) && !left) {
            right = true;
            up = false;
            down = false;            
        }

        if ((key == Qt.Key.Key_Up.value()) && !down) {
            up = true;
            right = false;
            left = false;            
        }

        if ((key == Qt.Key.Key_Down.value()) && !up) {
            down = true;
            right = false;
            left = false;       
        }
    }
}

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

WIDTHHEIGHT常数确定电路板的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义了板上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。

private int x[] = new int[ALL_DOTS];
private int y[] = new int[ALL_DOTS];

这两个数组存储蛇的所有可能关节的 x,y 坐标。

initGame()方法初始化变量,加载图像并启动超时功能。

if (inGame) {
    drawObjects(painter);
} else {
    gameOver(painter);
}

paintEvent()方法内部,我们检查inGame变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"文本。

private void drawObjects(QPainter painter) {

    painter.drawImage(apple_x, apple_y, apple);

    for (int z = 0; z < dots; z++) {
        if (z == 0)
            painter.drawImage(x[z], y[z], head);
        else painter.drawImage(x[z], y[z], ball);
    }
}

drawObjects()方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。

private void checkApple() {

    if ((x[0] == apple_x) && (y[0] == apple_y)) {
        dots++;
        locateApple();
    }
}

checkApple()方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locateApple()方法,该方法将随机放置一个新的Apple对象。

move()方法中,我们有游戏的关键算法。 要了解它,请看一下蛇是如何运动的。 您控制蛇的头。 您可以使用光标键更改其方向。 其余关节在链上向上移动一个位置。 第二关节移动到第一个关节的位置,第三关节移动到第二个关节的位置,依此类推。

for (int z = dots; z > 0; z--) {
    x[z] = x[(z - 1)];
    y[z] = y[(z - 1)];
}

该代码将关节向上移动。

if (left) {
    x[0] -= DOT_SIZE;
}

将头向左移动。

checkCollision()方法中,我们确定蛇是否击中了自己或撞墙之一。

for (int z = dots; z > 0; z--) {

    if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
        inGame = false;
    }
}

如果蛇用头撞到关节之一,我们就结束游戏。

if (y[0] > HEIGHT) {
    inGame = false;
}

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

locateApple()方法在板上随机放置一个苹果。

int r = (int) (Math.random() * RAND_POS);

我们得到一个从 0 到RAND_POS-1的随机数。

apple_x = ((r * DOT_SIZE));
...
apple_y = ((r * DOT_SIZE));

这些行设置了apple对象的 x,y 坐标。

if (inGame) {
    checkApple();
    checkCollision();
    move();
} else {
    timer.stop();
}

每 140 毫秒,将调用timerEvent()方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 否则,我们将停止计时器。

Board类的onKeyPressEvent()方法中,我们确定按下的键。

if (key == Qt.Key.Key_Left.value() && !right) {
    left = true;
    up = false;
    down = false;
}

如果单击左光标键,则将left变量设置为true。 在move()方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。

Nibbles.java

package com.zetcode;

import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QMainWindow;

/**
 * ZetCode QtJambi tutorial
 *
 * In this program, we create
 * a Nibbles game clone.
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified April 2009
 */

public class Nibbles extends QMainWindow {

    public Nibbles() {

        setWindowTitle("Nibbles");

        resize(310, 310);

        setCentralWidget(new Board());

        move(300, 300);
        show();
    }

    public static void main(String[] args) {
        QApplication.initialize(args);
        new Nibbles();
        QApplication.exec();
    }
}

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

Nibbles

图:贪食蛇

这是使用 QtJambi 库编程的贪食蛇电脑游戏。

Ruby GTK 教程

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

这是 Ruby GTK 教程。 在本教程中,您将学习使用 Ruby 语言的 GTK 进行 GUI 编程的基础。 本教程适合初学者和中级程序员。

目录

GTK

GTK 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK 库也称为 GIMP 工具包。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK 中创建的。 语言绑定适用于 C++ ,Python,Perl,Java,C# ,JavaScript,PHP 和其他编程语言。

Tweet

相关教程

您可以通过 Ruby 教程来重新了解 Ruby。 Ruby Qt 教程提供了 Ruby 中 Qt 库的教程。

PyQt5 中的菜单和工具栏

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

在 PyQt5 教程的这一部分中,我们创建一个状态栏,菜单栏和工具栏。 菜单是位于菜单栏中的一组命令。 工具栏上的按钮带有应用中的一些常用命令。 状态栏通常在应用窗口的底部显示状态信息。

QMainWindow

QMainWindow类提供一个主应用窗口。 这样可以创建带有状态栏,工具栏和菜单栏的经典应用框架。

状态栏

状态栏是用于显示状态信息的小部件。

statusbar.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a statusbar.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):               

        self.statusBar().showMessage('Ready')

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Statusbar')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

状态栏是在QMainWindow小部件的帮助下创建的。

self.statusBar().showMessage('Ready')

要获取状态栏,我们调用QtGui.QMainWindow类的statusBar()方法。 该方法的第一次调用将创建一个状态栏。 后续调用返回状态栏对象。 showMessage()在状态栏上显示一条消息。

简单菜单

菜单栏是 GUI 应用的常见部分。 它是位于各个菜单中的一组命令。 (Mac OS 对菜单栏的处理不同。要获得相似的结果,我们可以添加以下行:menubar.setNativeMenuBar(False)。)

simplemenu.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a menubar. The
menubar has one menu with an exit action.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: January 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from PyQt5.QtGui import QIcon

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):               

        exitAct = QAction(QIcon('exit.png'), '&Exit', self)        
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(qApp.quit)

        self.statusBar()

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Simple menu')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在上面的示例中,我们创建一个带有一个菜单的菜单栏。 该菜单将包含一个动作,如果选中该动作,它将终止应用。 也会创建一个状态栏。 可通过 Ctrl + Q 快捷方式访问该操作。

exitAct = QAction(QIcon('exit.png'), '&Exit', self)        
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit application')

QAction是使用菜单栏,工具栏或自定义键盘快捷键执行的操作的抽象。 在以上三行中,我们创建一个带有特定图标和“退出”标签的动作。 此外,为此操作定义了快捷方式。 第三行创建一个状态提示,当我们将鼠标指针悬停在菜单项上时,状态提示将显示在状态栏中。

exitAct.triggered.connect(qApp.quit)

当我们选择此特定动作时,将触发信号。 信号连接到QApplication小部件的quit()方法。 这将终止应用。

menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAction)

menuBar()方法创建一个菜单栏。 我们使用addMenu()创建文件菜单,并使用addAction()添加操作。

子菜单

子菜单是位于另一个菜单内的菜单。

submenu.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a submenu.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QAction, QMenu, QApplication

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):         

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('File')

        impMenu = QMenu('Import', self)
        impAct = QAction('Import mail', self) 
        impMenu.addAction(impAct)

        newAct = QAction('New', self)        

        fileMenu.addAction(newAct)
        fileMenu.addMenu(impMenu)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Submenu')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在示例中,我们有两个菜单项; 一个位于“文件”菜单中,另一个位于“文件的导入”子菜单中。

impMenu = QMenu('Import', self)

使用QMenu创建新菜单。

impAct = QAction('Import mail', self) 
impMenu.addAction(impAct)

使用addAction()将操作添加到子菜单。

Submenu

图:子菜单

复选菜单

在以下示例中,我们创建一个可以选中和取消选中的菜单。

checkmenu.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a checkable menu.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):         

        self.statusbar = self.statusBar()
        self.statusbar.showMessage('Ready')

        menubar = self.menuBar()
        viewMenu = menubar.addMenu('View')

        viewStatAct = QAction('View statusbar', self, checkable=True)
        viewStatAct.setStatusTip('View statusbar')
        viewStatAct.setChecked(True)
        viewStatAct.triggered.connect(self.toggleMenu)

        viewMenu.addAction(viewStatAct)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Check menu')    
        self.show()

    def toggleMenu(self, state):

        if state:
            self.statusbar.show()
        else:
            self.statusbar.hide()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

该代码示例使用一个动作创建一个视图菜单。 该操作显示或隐藏状态栏。 当状态栏可见时,将选中菜单项。

viewStatAct = QAction('View statusbar', self, checkable=True)

使用checkable选项,我们创建一个复选菜单。

viewStatAct.setChecked(True)

由于状态栏从一开始就可见,因此我们使用setChecked()方法检查操作。

def toggleMenu(self, state):

    if state:
        self.statusbar.show()
    else:
        self.statusbar.hide()

根据操作的状态,我们显示或隐藏状态栏。

Check menu

图:复现菜单

上下文菜单

上下文菜单,也称为弹出菜单,是在某些上下文下显示的命令列表。 例如,在 Opera Web 浏览器中,当我们右键单击某个网页时,将获得一个上下文菜单。 在这里,我们可以重新加载页面,返回页面或查看页面源。 如果右键单击工具栏,则将获得另一个用于管理工具栏的上下文菜单。

contextmenu.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a context menu.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, qApp, QMenu, QApplication

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):         

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Context menu')    
        self.show()

    def contextMenuEvent(self, event):

           cmenu = QMenu(self)

           newAct = cmenu.addAction("New")
           opnAct = cmenu.addAction("Open")
           quitAct = cmenu.addAction("Quit")
           action = cmenu.exec_(self.mapToGlobal(event.pos()))

           if action == quitAct:
               qApp.quit()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

要使用上下文菜单,我们必须重新实现contextMenuEvent()方法。

action = cmenu.exec_(self.mapToGlobal(event.pos()))

使用exec_()方法显示上下文菜单。 从事件对象获取鼠标指针的坐标。 mapToGlobal()方法将微件坐标转换为全局屏幕坐标。

if action == quitAct:
    qApp.quit()

如果从上下文菜单返回的操作等于退出操作,则我们终止该应用。

工具栏

菜单将我们可以在应用中使用的所有命令分组。 使用工具栏可以快速访问最常用的命令。

toolbar.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a toolbar.
The toolbar has one action, which
terminates the application, if triggered.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from PyQt5.QtGui import QIcon

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):               

        exitAct = QAction(QIcon('exit24.png'), 'Exit', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.triggered.connect(qApp.quit)

        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(exitAct)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Toolbar')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在上面的示例中,我们创建了一个简单的工具栏。 工具栏上有一个工具动作,一个退出动作,在触发时会终止应用。

exitAct = QAction(QIcon('exit24.png'), 'Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.triggered.connect(qApp.quit)

与上面的菜单栏示例类似,我们创建一个动作对象。 该对象具有标签,图标和快捷方式。 QtGui.QMainWindowquit()方法连接到触发信号。

self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(exitAction)

工具栏是使用addToolBar()方法创建的。 我们使用addAction()将动作对象添加到工具栏。

Toolbar

图:工具栏

把它放在一起

在本节的最后一个示例中,我们将创建一个菜单栏,工具栏和一个状态栏。 我们还将创建一个中央小部件。

mainwindow.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This program creates a skeleton of
a classic GUI application with a menubar,
toolbar, statusbar, and a central widget. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QTextEdit, QAction, QApplication
from PyQt5.QtGui import QIcon

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):               

        textEdit = QTextEdit()
        self.setCentralWidget(textEdit)

        exitAct = QAction(QIcon('exit24.png'), 'Exit', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(self.close)

        self.statusBar()

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)

        toolbar = self.addToolBar('Exit')
        toolbar.addAction(exitAct)

        self.setGeometry(300, 300, 350, 250)
        self.setWindowTitle('Main window')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

此代码示例使用菜单栏,工具栏和状态栏创建经典 GUI 应用的框架。

textEdit = QTextEdit()
self.setCentralWidget(textEdit)

在这里,我们创建一个文本编辑小部件。 我们将其设置为QMainWindow的中央小部件。 中央窗口小部件将占据剩余的所有空间。

Main window

图:主窗口

在 PyQt5 教程的这一部分中,我们使用了菜单,工具栏,状态栏和主应用窗口。

Ruby GTK 简介

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

在 Ruby GTK 编程教程的这一部分中,我们将介绍 GTK 库并使用 Ruby 编程语言创建第一个程序。

本教程的目的是帮助您开始使用 GTK 和 Ruby。 可以在此处下载来下载贪食蛇游戏的图像

关于

GTK 是用于创建图形用户界面的工具包。 Ruby 是一种流行的脚本语言。

简单的例子

在第一个示例中,我们创建一个简单的窗口。 窗口在屏幕上居中。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program centers a window on 
the screen.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Center"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        set_default_size 300, 200

        set_window_position Gtk::Window::Position::CENTER

        show
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

本示例在屏幕中央显示一个300x200px的窗口。

require 'gtk3'

require关键字导入我们将在应用中使用的必要类型。

class RubyApp < Gtk::Window

该示例继承自Gtk::Window-顶级容器。

set_title "Center"

我们为窗口设置标题。

signal_connect "destroy" do 
    Gtk.main_quit 
end

当单击标题栏中的关闭按钮或按 Alt + F4 时,会触发destroy信号。 Gtk.main_quit方法退出该应用。

set_default_size 300, 200

我们为应用窗口设置默认大小。

set_window_position Gtk::Window::Position::CENTER

这条线使窗口在屏幕上居中。 也可以使用:center符号。

show

一切准备就绪后,我们在屏幕上显示窗口。

Gtk.init
    window = RubyApp.new
Gtk.main

这三行设置了应用。

工具提示

第二个示例将显示一个工具提示。 工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program shows a tooltip on 
a window and a button.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_title  "Tooltips"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        fixed = Gtk::Fixed.new
        add fixed

        button = Gtk::Button.new :label =>'Button'
        button.set_size_request 80, 35      
        button.set_tooltip_text "Button widget"

        fixed.put button, 50, 50       

        set_tooltip_text "Window widget"
        set_default_size 300, 200
        set_window_position :center

        show_all
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

如果将鼠标指针悬停在窗口和按钮小部件的上方,则会弹出工具提示。

def initialize
    super

    init_ui
end

用户界面的创建委托给init_ui方法。

fixed = Gtk::Fixed.new
add fixed

Gtk::Fixed是一个允许将小部件定位在固定坐标的容器。 对于更复杂的应用,必须使用布局管理器。

button = Gtk::Button.new :label =>'Button'

Gtk::Button小部件已创建。

button.set_size_request 80, 35  

set_size_request方法为按钮小部件提供大小:宽度:80,高度:35。

button.set_tooltip_text "Button widget"

我们使用set_tooltip_text方法设置工具提示。

fixed.put button, 50, 50

按钮窗口小部件以 x:50,y:50 坐标放置在Gtk::Fixed容器中。 坐标系从窗口的左上方开始。

set_tooltip_text "Window widget"

我们为Gtk::Window小部件设置了一个工具提示。

Tooltips

图:工具提示 s

退出按钮

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

#!/usr/bin/ruby

'''
ZetCode Ruby 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: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        fixed = Gtk::Fixed.new
        add fixed

        button = Gtk::Button.new :label => "Quit"
        button.set_size_request 80, 35      
        button.signal_connect "clicked" do 
            Gtk.main_quit 
        end

        fixed.put button, 50, 50       

        set_title  "Quit button"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 200
        set_window_position(:center)
        show_all
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在示例中,我们将Gtk::Button小部件放置在窗口上。 我们将处理器附加到clicked信号。

button = Gtk::Button.new :label => "Quit"

创建带有标签"Quit"Gtk::Button

button.signal_connect "clicked" do 
    Gtk.main_quit 
end

我们将main_quit方法插入按钮clicked信号。

show_all

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

Quit button

图:退出按钮

本章介绍了使用 Ruby 语言的 GTK 库。

Ruby GTK 中的布局管理

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

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

在设计应用的 GUI 时,我们决定使用哪些小部件以及如何在应用中组织这些小部件。 为了组织窗口小部件,我们使用称为布局容器的专用非可见窗口小部件。 在本章中,我们提到Gtk::AlignmentGtk::FixedGtk::VBoxGtk::Grid

Gtk::Fixed

Gtk::Fixed容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 在大多数应用中,我们不使用此容器。 我们在某些特定领域使用它,例如游戏,使用图表的应用,可以移动的可调整大小的组件(例如电子表格应用中的图表),小型教育示例。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

In this program, we lay out widgets
using absolute positioning.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        override_background_color :normal, Gdk::RGBA::new(0.2, 0.2, 0.2, 1)

        begin       
            bardejov = Gdk::Pixbuf.new :file => "bardejov.jpg"
            rotunda = Gdk::Pixbuf.new :file => "rotunda.jpg"
            mincol = Gdk::Pixbuf.new :file => "mincol.jpg"
        rescue IOError => e
            puts e
            puts "cannot load images"
            exit
        end

        image1 = Gtk::Image.new :pixbuf => bardejov 
        image2 = Gtk::Image.new :pixbuf => rotunda 
        image3 = Gtk::Image.new :pixbuf => mincol 

        fixed = Gtk::Fixed.new

        fixed.put image1, 20, 20
        fixed.put image2, 40, 160
        fixed.put image3, 170, 50

        add fixed

        set_title "Fixed"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 280
        window_position = :center

        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在我们的示例中,我们在窗口上显示了三个小图像。 我们明确指定放置这些图像的 x,y 坐标。

override_background_color :normal, Gdk::RGBA::new(0.2, 0.2, 0.2, 1)

为了获得更好的视觉体验,我们将背景色更改为深灰色。

bardejov = Gdk::Pixbuf.new :file => "bardejov.jpg"

我们将图像从磁盘加载到Gtk::Pixbuf对象。

image1 = Gtk::Image.new :pixbuf => bardejov 
image2 = Gtk::Image.new :pixbuf => rotunda 
image3 = Gtk::Image.new :pixbuf => mincol 

Gtk::Image是用于显示图像的小部件。 它在构造器中使用一个Gdk::Pixbuf对象。

fixed = Gtk::Fixed.new

我们创建Gtk::Fixed容器。

fixed.put image1, 20, 20

我们将第一个图像放置在 x = 20,y = 20 坐标处。

add fixed

最后,我们将Gtk::Fixed容器添加到窗口中。

Gtk::Fixed

图:Gtk::Fixed

按钮

Gtk::Alignment容器控制其子窗口小部件的对齐方式和大小。

#!/usr/bin/ruby

'''
ZetCode Ruby 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: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_border_width 10

        vbox = Gtk::Box.new :vertical, 0
        hbox = Gtk::Box.new :horizontal, 5

        e_space = Gtk::Alignment.new 0, 0, 0, 0
        vbox.pack_start e_space, :expand => true

        ok_btn = Gtk::Button.new :label => "OK"
        ok_btn.set_size_request 70, 30
        close_btn = Gtk::Button.new :label => "Close"
        close_btn.set_size_request 70, 30

        hbox.add ok_btn
        hbox.add close_btn 

        halign = Gtk::Alignment.new 1, 0, 0, 0
        halign.add hbox

        vbox.pack_start halign, :expand => false, 
            :fill => false, :padding => 5

        add vbox

        set_title "Buttons"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 260, 150
        set_window_position :center

        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在代码示例中,我们在窗口的右下角放置了两个按钮。 为此,我们使用一个水平框,一个垂直框和两个对齐容器。

set_border_width 10

set_border_widthGtk::Window容器窗口小部件的边框周围设置了一些空白。 对于我们的示例来说很重要,因为关闭按钮不会太靠近窗口的右边缘。

vbox = Gtk::Box.new :vertical, 0
hbox = Gtk::Box.new :horizontal, 5

将创建一个垂直和水平框。 垂直框用作我们窗口的基础容器。 垂直框中放置有空白空间和包含两个按钮小部件的水平框。

e_space = Gtk::Alignment.new 0, 0, 0, 0
vbox.pack_start e_space, :expand => true

Gtk::Alignment小部件用作空白填充符。 它将按钮推到窗口底部。 :expand参数将导致Gtk::Alignment小部件消耗分配给垂直框的所有额外空间。

hbox = Gtk::Box.new :horizontal, 5
...
ok_btn = Gtk::Button.new :label => "OK"
ok_btn.set_size_request 70, 30
close_btn = Gtk::Button.new :label => "Close"
close_btn.set_size_request 70, 30

hbox.add ok_btn
hbox.add close_btn         

我们创建一个水平框,并在其中放置两个按钮。 Gtk::Box的第二个参数是子级之间的间距量。

halign = Gtk::Alignment.new 1, 0, 0, 0
halign.add hbox

vbox.pack_start halign, :expand => false, 
    :fill => false, :padding => 5

这将创建一个对齐容器,它将其子窗口小部件放在右侧。 Gtk::Alignment容器的第一个参数是水平对齐方式。 值为 1 会将其子项(包含两个按钮的水平框)向右推。 对齐容器仅需要一个子窗口小部件-我们必须使用水平框。

Buttons

图:按钮

计算器骨架

本示例借助Gtk::BoxGtk::Grid小部件创建计算器的骨架。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

In this program we create a skeleton of
a calculator. We use a Gtk::Grid widget
and a vertical Gtk::Box.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        vbox = Gtk::Box.new :vertical, 2

        mb = Gtk::MenuBar.new
        filemenu = Gtk::Menu.new
        file = Gtk::MenuItem.new "File"
        file.set_submenu filemenu
        mb.append file

        vbox.pack_start mb, :expand => false, :fill => false, 
            :padding => 0

        vbox.pack_start Gtk::Entry.new, :expand => false, 
            :fill => false, :padding => 0            

        grid = Gtk::Grid.new
        grid.set_property "row-homogeneous", true
        grid.set_property "column-homogeneous", true

        grid.attach Gtk::Button.new(:label => "Cls"), 0, 0, 1, 1
        grid.attach Gtk::Button.new(:label => "Bck"), 1, 0, 1, 1
        grid.attach Gtk::Label.new, 2, 0, 1, 1
        grid.attach Gtk::Button.new(:label => "Close"), 3, 0, 1, 1

        grid.attach Gtk::Button.new(:label => "7"), 0, 1, 1, 1
        grid.attach Gtk::Button.new(:label => "8"), 1, 1, 1, 1
        grid.attach Gtk::Button.new(:label => "9"), 2, 1, 1, 1
        grid.attach Gtk::Button.new(:label => "/"), 3, 1, 1, 1

        grid.attach Gtk::Button.new(:label => "4"), 0, 2, 1, 1
        grid.attach Gtk::Button.new(:label => "5"), 1, 2, 1, 1
        grid.attach Gtk::Button.new(:label => "6"), 2, 2, 1, 1
        grid.attach Gtk::Button.new(:label => "*"), 3, 2, 1, 1

        grid.attach Gtk::Button.new(:label => "1"), 0, 3, 1, 1
        grid.attach Gtk::Button.new(:label => "2"), 1, 3, 1, 1
        grid.attach Gtk::Button.new(:label => "3"), 2, 3, 1, 1
        grid.attach Gtk::Button.new(:label => "-"), 3, 3, 1, 1

        grid.attach Gtk::Button.new(:label => "0"), 0, 4, 1, 1
        grid.attach Gtk::Button.new(:label => "."), 1, 4, 1, 1
        grid.attach Gtk::Button.new(:label => "="), 2, 4, 1, 1
        grid.attach Gtk::Button.new(:label => "+"), 3, 4, 1, 1

        vbox.pack_start grid, :expand => true, :fill => true, 
            :padding => 0

        add vbox

        set_title "Calculator"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 250
        set_window_position :center

        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

Gtk::Grid小部件按行和列排列小部件。

vbox = Gtk::Box.new :vertical, 2

Gtk::Box用作我们应用的基础容器。 框的方向是垂直的,其子框(菜单栏,条目和网格小部件)之间有 2px 的间距。 由于它是一个垂直框,因此该空间垂直放置在小部件之间。

mb = Gtk::MenuBar.new
filemenu = Gtk::Menu.new
file = Gtk::MenuItem.new "File"
file.set_submenu filemenu
mb.append file

vbox.pack_start mb, :expand => false, :fill => false, 
    :padding => 0

创建带有一个菜单的Gtk::MenuBar。 它放置在垂直框内。

vbox.pack_start Gtk::Entry.new, :expand => false, 
    :fill => false, :padding => 0    

Gtk::Entry放置在菜单栏下方。 我们将:expand参数设置为false,因为我们不想垂直扩展条目窗口小部件。 放在垂直框中的小部件从左向右拉伸。 如果要更改此设置,则需要一个附加的水平框。

grid = Gtk::Grid.new

Gtk::Grid容器已创建。

grid.set_property "row-homogeneous", true
grid.set_property "column-homogeneous", true

我们将行和列的均质属性设置为true。 这将导致所有子项具有相同的大小。

grid.attach Gtk::Button.new(:label => "Cls"), 0, 0, 1, 1

我们在网格容器的左上角单元格上附加一个按钮。 前两个参数是列索引和行索引。 最后两个参数是列跨度和行跨度。 网格内的所有小部件都占用一个单元格。

vbox.pack_start grid, :expand => true, :fill => true, 
    :padding => 0

我们将网格小部件打包到垂直框中。 :expand:fill选项的组合将使网格小部件及其子级占据窗口区域的大部分。

add vbox

垂直框放置在Gtk::Window容器内。

Calculator skeleton

图:计算机骨架

窗口

在最后一个示例中,我们将使用Gtk::Grid容器。 此容器将其子级放置到单元格中,这些单元格由行和列的交点界定。 网格容器的attach方法采用五个参数。 第一个参数是附加的子窗口小部件。 接下来的两个参数是放置子项的行和列索引。 最后两个参数是行跨度和列跨度。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This is a more complicated layout example.
We use Gtk::Alignment, Gtk::Box, and Gtk::Grid widgets.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_border_width 15

        grid = Gtk::Grid.new 
        grid.set_column_spacing 5
        grid.set_row_spacing 5

        title = Gtk::Label.new "Windows"

        align1 = Gtk::Alignment.new 0, 0, 0, 0
        align1.add title

        grid.attach align1, 0, 0, 1, 1 

        frame = Gtk::Frame.new
        frame.set_hexpand true
        frame.set_vexpand true
        grid.attach frame, 0, 1, 3, 3

        vbox = Gtk::Box.new :vertical, 4
        act_btn = Gtk::Button.new :label => "Activate"
        act_btn.set_size_request 70, 30

        close_btn = Gtk::Button.new :label => "Close"
        close_btn.set_size_request 70, 30

        vbox.add act_btn
        vbox.add close_btn
        grid.attach vbox, 3, 1, 1, 1

        help_btn = Gtk::Button.new :label => "Help"
        help_btn.set_size_request 70, 30
        align2 = Gtk::Alignment.new 0, 0, 0, 0
        align2.add help_btn        
        grid.attach align2, 0, 4, 1, 1

        ok_btn = Gtk::Button.new :label => "OK" 
        ok_btn.set_size_request 70, 30
        grid.attach ok_btn, 3, 4, 1, 1

        add grid

        set_title "Windows"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        set_default_size 350, 300
        set_window_position :center

        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

该代码在 Ruby GTK 中创建了一个真实世界的窗口。

grid = Gtk::Grid.new 
grid.set_column_spacing 5
grid.set_row_spacing 5

创建Gtk::Grid容器的实例。 该容器将小部件打包为行和列。 我们在行和列之间设置一些空间。

title = Gtk::Label.new "Windows"

align1 = Gtk::Alignment.new 0, 0, 0, 0
align1.add title

grid.attach align1, 0, 0, 1, 1 

我们创建一个标签小部件。 将此小部件放置在Gtk::Alignment小部件中,以便使其与标签上的空格左侧对齐。 网格容器的attach方法将标签放入其左上角的单元格中。 标签将占据一个单元格。

frame = Gtk::Frame.new
frame.set_hexpand true
frame.set_vexpand true
grid.attach frame, 0, 1, 3, 3

框架小部件位于column = 0row = 1处。 它跨越三行和树列。 set_hexpandset_vexpand方法将窗口小部件设置为占用任何可用的额外水平和垂直空间。 当窗口增长时,框架小部件也增长; 其他小部件保留其大小。

vbox = Gtk::Box.new :vertical, 4
act_btn = Gtk::Button.new :label => "Activate"
act_btn.set_size_request 70, 30

close_btn = Gtk::Button.new :label => "Close"
close_btn.set_size_request 70, 30

vbox.add act_btn
vbox.add close_btn
grid.attach vbox, 3, 1, 1, 1

创建两个按钮并将其放置在垂直框中。 “到”框位于框架小部件旁边。

help_btn = Gtk::Button.new :label => "Help"
help_btn.set_size_request 70, 30
align2 = Gtk::Alignment.new 0, 0, 0, 0
align2.add help_btn        
grid.attach align2, 0, 4, 1, 1

帮助按钮位于对齐容器内部,该对齐按钮将其对齐到网格容器旁边放置的单元格的左侧。 较早的set_hexpand方法调用使框架窗口小部件可扩展; 它还会影响框架窗口小部件占用的列中的窗口小部件。 因此,我们需要使用Gtk::Alignment小部件来保持按钮的大小不变,并将其向左对齐。

Windows

图:窗口

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

Ruby GTK 中的小部件

原文: http://zetcode.com/gui/rubygtk/widgets/

在 Ruby GTK 编程教程的这一部分中,我们将介绍一些小部件。

小部件是 GUI 应用的基本构建块。 多年来,一些小部件已成为所有 OS 平台上所有工具包中的标准,例如按钮,复选框或滚动条。 GTK 工具箱的理念是将小部件的数量保持在最低水平。 将创建更多专门的窗口小部件作为自定义 GTK 窗口小部件。

Gtk::CheckButton

Gtk::CheckButton是具有两种状态的窗口小部件:开和关。 接通状态通过复选标记显示。 它用来表示一些布尔属性。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program toggles the title of the
window with the Gtk::CheckButton widget.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window
    def initialize
        super

        init_ui
    end

    def init_ui

        fixed = Gtk::Fixed.new
        add fixed

        cb = Gtk::CheckButton.new "Show title"
        cb.set_active true
        cb.set_can_focus false
        cb.signal_connect("clicked") do |w|
            on_clicked w
        end

        fixed.put cb, 50, 50    

        set_title "Gkt::CheckButton"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 200
        set_window_position :center
        show_all        

    end

    def on_clicked sender

        if sender.active?
            self.set_title "Gtk::CheckButton"
        else
           self.set_title ""
        end
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

根据Gtk::CheckButton的状态,我们将在窗口的标题栏中显示标题。

cb = Gtk::CheckButton.new "Show title"

Gtk::CheckButton小部件已创建。

cb.set_active true

默认情况下标题是可见的,因此我们首先激活复选按钮。

if sender.active?
    self.set_title "Gtk::CheckButton"
else
    self.set_title ""
end

如果选中该按钮,我们将显示标题。 按钮的状态由active?方法确定。

Gtk::CheckButton widget

图:Gtk::CheckButton小部件

Gtk::Label

Gtk::Label小部件显示文本。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example demonstrates the Gtk::Label widget.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

$lyrics = %{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 sniff 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 men 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}

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_title  "You know I'm no Good"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        set_border_width 10
        label = Gtk::Label.new $lyrics
        add label    

        set_default_size 250, 200
        set_window_position :center

        show_all        

    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

该代码示例在窗口上显示了一些歌词。

$lyrics = %{Meet you downstairs in the bar and heard
your rolled up sleeves and your skull t-shirt
...

我们创建多行文本。

set_border_width 10

Gtk::Label小部件被一些空白包围。

label = Gtk::Label.new $lyrics
add label 

Gtk::Label小部件已创建并添加到窗口。

Gtk::Label widget

图:Gtk::Label小部件

Gtk::Entry

Gtk::Entry是单行文本输入字段。 该小部件用于输入文本数据。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example demonstrates the Gtk::Entry widget.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''
require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui

    end

    def init_ui

        fixed = Gtk::Fixed.new

        label = Gtk::Label.new "..."
        fixed.put label, 60, 40

        entry = Gtk::Entry.new
        fixed.put entry, 60, 100

        entry.signal_connect "key-release-event" do |w, e|
            on_key_release w, e, label
        end

        add fixed

        set_title "Gtk::Entry"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 250, 200
        set_window_position :center

        show_all        
    end  

    def on_key_release sender, event, label
        label.set_text sender.text
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

此示例显示了条目小部件和标签。 我们输入的文本将立即显示在标签小部件中。

entry = Gtk::Entry.new

Gtk::Entry小部件已创建。

entry.signal_connect "key-release-event" do |w, e|
    on_key_release(w, e, label)
end

我们将on_key_release方法插入Entry小部件的key-release-event

def on_key_release sender, event, label
    label.set_text sender.text
end

我们从Gtk::Entry小部件获取文本并将其设置为标签。

Entry Widget

图:Entry小部件

Gtk::Image

Gtk::Image小部件显示图像。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example demonstrates the Gtk::Image widget.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_border_width 2

        image = Gtk::Image.new :file => "redrock.png"
        add image

        set_title "Red Rock"
        signal_connect "destroy" do 
            Gtk.main_quit
        end

        set_window_position :center

        show_all            
    end

end

Gtk.init
    window = RubyApp.new
Gtk.main

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

set_border_width 2

我们在图像周围放置了一些空边框。

image = Gtk::Image.new :file => "redrock.png"

Gtk::Image小部件已创建。 如果未成功加载文件,则该图像将包含“残破图像”图标。 因此,我们不添加错误检查代码。

add image

窗口小部件已添加到容器中。

Gtk::ComboBoxText

Gtk::ComboBoxText是一个小部件,允许用户从文本选项列表中进行选择。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example demonstrates the Gtk::ComboBoxText widget.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        fixed = Gtk::Fixed.new
        label = Gtk::Label.new '-'
        fixed.put label, 50, 140

        cb = Gtk::ComboBoxText.new
        cb.signal_connect "changed" do |w, e|
            on_changed w, e, label
        end

        cb.append_text 'Xubuntu'
        cb.append_text 'Mandriva'
        cb.append_text 'Redhat'
        cb.append_text 'Gentoo'
        cb.append_text 'Arch' 

        fixed.put cb, 50, 30

        add fixed

        set_title "Gtk::ComboBoxText"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 200
        set_window_position :center

        show_all        
    end

    def on_changed sender, event, label
        label.set_label sender.active_text
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

该示例显示了一个组合框和一个标签。 组合框具有五个选项的列表。 这些是 Linux Distros 的名称。 标签窗口小部件显示了从组合框中选择的选项。

cb = Gtk::ComboBoxText.new

Gtk::ComboBoxText小部件已创建。

cb.signal_connect "changed" do |w, e|
    on_changed w, e, label
end

自定义on_changed方法插入到组合框的changed信号中。 每当更改组合框的活动项目时,都会发出该信号。 我们将三个参数传递给on_changed方法:发件人小部件(组合框),事件对象和标签小部件。

cb.append_text 'Xubuntu'
cb.append_text 'Mandriva'
cb.append_text 'Redhat'
cb.append_text 'Gentoo'
cb.append_text 'Arch' 

组合框充满数据。

def on_changed sender, event, label
    label.set_label sender.active_text
end

on_changed方法内部,我们从组合框中获取选定的文本并将其设置为标签。

Gtk::ComboBoxText widget

图:GtkComboBoxText小部件

在 Ruby GTK 教程的这一章中,我们展示了一些基本的小部件。

Ruby GTK 中的菜单和工具栏

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

在 Ruby GTK 编程教程的这一部分中,我们将使用菜单和工具栏。

GUI 应用中的常见部分是菜单栏。 菜单栏由称为菜单的对象组成。 顶层菜单在菜单栏上带有其标签。 菜单具有菜单项-在应用内部执行特定操作的命令。 菜单也可以具有子菜单,这些子菜单具有自己的菜单项。

简单的菜单栏

在第一个示例中,我们将创建一个带有一个文件菜单的菜单栏。 该菜单将只有一个菜单项。 通过选择项目,应用退出。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example creates a simple menubar.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        override_background_color :normal, 
            Gdk::RGBA.new(0.2, 0.2, 0.2, 1)

        mb = Gtk::MenuBar.new

        filemenu = Gtk::Menu.new
        filem = Gtk::MenuItem.new "File"
        filem.set_submenu filemenu

        exit = Gtk::MenuItem.new "Exit"
        exit.signal_connect "activate" do
            Gtk.main_quit
        end

        filemenu.append exit

        mb.append filem

        vbox = Gtk::Box.new :vertical, 2

        vbox.pack_start mb, :expand => false, 
            :fill => false, :padding => 0

        add vbox

        set_title "Simple menubar"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 200
        set_window_position :center

        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main    

这是一个最小的菜单栏功能示例。

mb = Gtk::MenuBar.new

Gtk::MenuBar小部件已创建。 这是菜单的容器。

filemenu = Gtk::Menu.new
filem = Gtk::MenuItem.new "File"
filem.set_submenu filemenu

创建包含一个Gtk::Menu的顶层Gtk::MenuItem

exit = Gtk::MenuItem.new "Exit"
exit.signal_connect "activate" do
    Gtk.main_quit
end

filemenu.append exit

创建出口Gtk::MenuItem并将其附加到文件Gtk::MenuItem中。

mb.append filem

顶级Gtk::MenuItem被附加到Gtk::MenuBar小部件。

vbox = Gtk::VBox.new false, 2
vbox.pack_start mb, false, false, 0

我们将菜单栏放入垂直框中。

Simple menu

图:简单菜单

子菜单

下面的示例演示如何创建子菜单。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example shows a submenu.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        override_background_color :normal, 
            Gdk::RGBA.new(0.2, 0.2, 0.2, 1)
        mb = Gtk::MenuBar.new

        filemenu = Gtk::Menu.new
        filem = Gtk::MenuItem.new "File"
        filem.set_submenu filemenu

        mb.append filem

        imenu = Gtk::Menu.new

        importm = Gtk::MenuItem.new "Import"
        importm.set_submenu imenu

        inews = Gtk::MenuItem.new "Import news feed..."
        ibookmarks = Gtk::MenuItem.new "Import bookmarks..."
        imail = Gtk::MenuItem.new "Import mail..."

        imenu.append inews
        imenu.append ibookmarks
        imenu.append imail

        filemenu.append importm

        exit = Gtk::MenuItem.new "Exit"
        exit.signal_connect "activate" do
            Gtk.main_quit
        end

        filemenu.append exit

        vbox = Gtk::Box.new :vertical, 2

        vbox.pack_start mb, :expand => false, 
            :fill => false, :padding => 0

        add vbox

        set_title "Submenu"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 350, 250
        set_window_position :center
        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

子菜单是另一个菜单中的菜单。

imenu = Gtk::Menu.new

子菜单是Gtk::Menu

importm = Gtk::MenuItem.new "Import"
importm.set_submenu imenu
...
filemenu.append importm

它是菜单项的子菜单,该菜单项会登录到顶级文件菜单。

inews = Gtk::MenuItem.new "Import news feed..."
ibookmarks = Gtk::MenuItem.new "Import bookmarks..."
imail = Gtk::MenuItem.new "Import mail..."

imenu.append inews
imenu.append ibookmarks
imenu.append imail

子菜单有其自己的菜单项。

Submenu

图:子菜单

图像菜单

在下一个示例中,我们将进一步探索菜单。 我们将图像和加速器添加到我们的菜单项中。 加速器是用于激活菜单项的键盘快捷键。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example shows a menu with
images, accelerators and a separator.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        override_background_color :normal, 
            Gdk::RGBA.new(0.2, 0.2, 0.2, 1)
        mb = Gtk::MenuBar.new

        filemenu = Gtk::Menu.new
        filem = Gtk::MenuItem.new "File"
        filem.set_submenu filemenu

        agr = Gtk::AccelGroup.new
        add_accel_group agr

        newi = Gtk::ImageMenuItem.new :stock_id => Gtk::Stock::NEW, 
            :accel_group => agr
        key, mod = Gtk::Accelerator.parse "N"

        newi.add_accelerator "activate", agr, key,
           mod, Gtk::AccelFlags::VISIBLE
        filemenu.append newi

        openm = Gtk::ImageMenuItem.new :stock_id => Gtk::Stock::OPEN, 
            :accel_group => agr
        key, mod = Gtk::Accelerator.parse "O"
        openm.add_accelerator "activate", agr, key, 
            mod, Gtk::AccelFlags::VISIBLE
        filemenu.append openm

        sep = Gtk::SeparatorMenuItem.new
        filemenu.append sep

        exit = Gtk::ImageMenuItem.new :stock_id => Gtk::Stock::QUIT, 
            :accel_group => agr
        key, mod = Gtk::Accelerator.parse "Q"

        exit.add_accelerator "activate", agr, key, 
            mod, :visible

        exit.signal_connect "activate" do
            Gtk.main_quit
        end
        filemenu.append exit

        mb.append filem

        vbox = Gtk::Box.new :vertical, 2
        vbox.pack_start mb, :expand => false, :fill => false, 
            :padding => 0

        add vbox

        set_title "Image menu"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 200
        set_window_position :center
        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

我们的示例显示了具有三个菜单项的顶级菜单项。 每个菜单项都有一个图像和一个加速器。 退出菜单项的加速器处于活动状态。

agr = Gtk::AccelGroup.new
add_accel_group agr

要使用加速器,我们创建一个全局Gtk::AccelGroup对象。 稍后将使用。

newi = Gtk::ImageMenuItem.new :stock_id => Gtk::Stock::NEW, 
    :accel_group => agr
key, mod = Gtk::Accelerator.parse "N"

newi.add_accelerator "activate", agr, key,
    mod, Gtk::AccelFlags::VISIBLE
filemenu.append newi

创建了Gtk::ImageMenuItem。 图片来自图片库。 我们还创建了 Ctrl + N 加速器。

sep = Gtk::SeparatorMenuItem.new
filemenu.append sep

这些行创建一个分隔符,该分隔符用于将菜单项放入逻辑组中。

Image menu

图:图像 menu

简单的工具栏

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 接下来,我们创建一个简单的工具栏。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example creates a simple toolbar.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        toolbar = Gtk::Toolbar.new
        toolbar.set_toolbar_style Gtk::Toolbar::Style::ICONS

        newtb = Gtk::ToolButton.new :stock_id => Gtk::Stock::NEW
        opentb = Gtk::ToolButton.new :stock_id => Gtk::Stock::OPEN
        savetb = Gtk::ToolButton.new :stock_id => Gtk::Stock::SAVE
        sep = Gtk::SeparatorToolItem.new
        quittb = Gtk::ToolButton.new :stock_id => Gtk::Stock::QUIT

        toolbar.insert newtb, 0
        toolbar.insert opentb, 1
        toolbar.insert savetb, 2
        toolbar.insert sep, 3
        toolbar.insert quittb, 4

        quittb.signal_connect "clicked" do
            Gtk.main_quit
        end

        vbox = Gtk::Box.new :vertical, 2
        vbox.pack_start toolbar, :expand => false, 
            :fill => false, :padding => 0

        add vbox

        set_title "Toolbar"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 200
        set_window_position :center

        show_all        
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

该示例显示了一个工具栏和四个工具按钮。

toolbar = Gtk::Toolbar.new

Gtk::Toolbar小部件已创建。

toolbar.set_toolbar_style Gtk::Toolbar::Style::ICONS

在工具栏上,我们仅显示图标。

newtb = Gtk::ToolButton.new :stock_id => Gtk::Stock::NEW

创建带有库存图像的Gtk::ToolButton。 该图像来自图像的内置库存。

sep = Gtk::SeparatorToolItem.new

这是一个分隔符。 它可用于将工具栏按钮放入逻辑组。

toolbar.insert newtb, 0
toolbar.insert opentb, 1
...

工具栏按钮插入到工具栏小部件中。

Toolbar

图:工具栏

撤销重做

以下示例演示了如何停用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法,例如保存按钮。 如果我们将文档的所有更改都保存到磁盘上,则在大多数文本编辑器中,“保存”按钮将被停用。 这样,应用会向用户指示所有更改都已保存。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example shows how to 
activate/deactivate a Gtk::ToolButton.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        @count = 2

        init_ui
    end

    def init_ui

        toolbar = Gtk::Toolbar.new
        toolbar.set_toolbar_style Gtk::Toolbar::Style::ICONS

        @undo = Gtk::ToolButton.new :stock_id => Gtk::Stock::UNDO
        @redo = Gtk::ToolButton.new :stock_id => Gtk::Stock::REDO
        sep = Gtk::SeparatorToolItem.new
        quit = Gtk::ToolButton.new :stock_id => Gtk::Stock::QUIT

        toolbar.insert @undo, 0
        toolbar.insert @redo, 1
        toolbar.insert sep, 2
        toolbar.insert quit, 3

        @undo.signal_connect "clicked" do
            on_undo
        end

        @redo.signal_connect "clicked" do
            on_redo
        end

        quit.signal_connect "clicked" do
            Gtk.main_quit
        end

        vbox = Gtk::Box.new :vertical, 2
        vbox.pack_start toolbar, :expand => false, 
            :fill => false, :padding => 0

        self.add vbox

        set_default_size 300, 200
        set_window_position :center

        show_all        

        set_title "Undo redo"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end
    end

    def on_undo

        @count = @count - 1

        if @count <= 0
            @undo.set_sensitive false
            @redo.set_sensitive true
        end
    end

    def on_redo
        @count = @count + 1

        if @count >= 5
            @redo.set_sensitive false
            @undo.set_sensitive true
        end
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

我们的示例从 GTK 库存资源创建撤消和重做按钮。 单击几下后,每个按钮均被停用。 按钮显示为灰色。

@count = 2

@count变量决定激活或停用哪个按钮。

@undo = Gtk::ToolButton.new :stock_id => Gtk::Stock::UNDO
@redo = Gtk::ToolButton.new :stock_id => Gtk::Stock::REDO

我们有两个工具按钮:撤消和重做工具按钮。 图像来自库存资源。

@undo.signal_connect "clicked" do
    on_undo
end         

单击撤消按钮,我们触发on_undo方法。

if @count <= 0
    @undo.set_sensitive false
    @redo.set_sensitive true
end

要激活或停用小部件,我们使用set_sensitive方法

Undo redo

图:撤销和重做

在 Ruby GTK 教程的这一章中,我们展示了如何使用菜单和工具栏。

Ruby GTK 中的对话框

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

在 Ruby GTK 编程教程的这一部分中,我们将介绍对话框。

对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。

MessageDialog

消息对话框是方便的对话框,可向应用的用户提供消息。 该消息包含文本和图像数据。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example shows message dialogs.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        table = Gtk::Table.new 2, 2, true

        info = Gtk::Button.new :label => "Information"
        warn = Gtk::Button.new :label => "Warning"
        ques = Gtk::Button.new :label => "Question"
        erro = Gtk::Button.new :label => "Error"

        info.signal_connect "clicked" do
            on_info
        end    

        warn.signal_connect "clicked" do
            on_warn
        end

        ques.signal_connect "clicked" do
            on_ques
        end

        erro.signal_connect "clicked" do
            on_erro
        end

        table.attach info, 0, 1, 0, 1
        table.attach warn, 1, 2, 0, 1
        table.attach ques, 0, 1, 1, 2
        table.attach erro, 1, 2, 1, 2

        add table

        set_title "Messages"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        set_default_size 300, 100
        set_window_position :center

        show_all        

    end

    def on_info

        md = Gtk::MessageDialog.new :parent => self, 
            :flags => :destroy_with_parent, :type => :info, 
            :buttons_type => :close, :message => "Download completed"
        md.run
        md.destroy
    end

    def on_erro

        md = Gtk::MessageDialog.new :parent => self, 
            :flags => :modal, :type => :error, 
            :buttons_type => :close, :message => "Error loading file"
        md.run
        md.destroy
    end

    def on_ques

        md = Gtk::MessageDialog.new :parent => self, 
            :flags => :destroy_with_parent, :type => :question, 
            :buttons_type => :close, :message => "Are you sure to quit?"
        md.run
        md.destroy
    end

    def on_warn

        md = Gtk::MessageDialog.new :parent => self, 
            :flags => :destroy_with_parent, :type =>  :warning, 
            :buttons_type => :close, :message => "Unallowed operation"
        md.run
        md.destroy
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在我们的示例中,我们将显示四种消息对话框:信息,警告,问题和错误消息对话框。

info = Gtk::Button.new :label => "Information"
warn = Gtk::Button.new :label => "Warning"
ques = Gtk::Button.new :label => "Question"
erro = Gtk::Button.new :label => "Error"

我们有四个按钮。 这些按钮中的每个按钮都会显示不同类型的消息对话框。

def on_info

    md = Gtk::MessageDialog.new :parent => self, 
        :flags => :destroy_with_parent, :type => :info, 
        :buttons_type => :close, :message => "Download completed"
    md.run
    md.destroy
end

如果单击信息按钮,则会显示信息对话框。 Gtk::MessageDialog::INFO指定对话框的类型。 Gtk::MessageDialog::BUTTONS_CLOSE指定要在对话框中显示的按钮的类型。 最后一个参数是显示的消息。 该对话框使用run方法显示。 程序员还必须调用destroyhide方法。

Information message dialog

Warning message dialog

Question message dialog

Error message dialog

AboutDialog

AboutDialog显示有关应用的信息。 它可以显示徽标,应用名称,版本,版权,网站或许可证信息。 也有可能对作者,文档撰写者,翻译者和艺术家予以赞扬。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example demonstrates the
Gtk::AboutDialog dialog.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "About dialog"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        init_ui

        set_default_size 300, 150
        set_window_position :center

        show_all
    end

    def init_ui

        button = Gtk::Button.new :label => "About"
        button.set_size_request 80, 30

        button.signal_connect "clicked" do
            on_clicked
        end

        fix = Gtk::Fixed.new
        fix.put button, 20, 20

        add fix
    end

    def on_clicked

        about = Gtk::AboutDialog.new
        about.set_program_name "Battery"
        about.set_version "0.1"
        about.set_copyright "(c) Jan Bodnar"
        about.set_comments "Battery is a simple tool for battery checking"
        about.set_website "http://www.zetcode.com"        

        begin       
            logo = Gdk::Pixbuf.new :file => "batter.png"
            about.set_logo logo
        rescue IOError => e
            puts e
            puts "cannot load image"
            exit            
        end

        about.run
        about.destroy
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

该代码示例使用具有某些功能的Gtk::AboutDialog

about = Gtk::AboutDialog.new

我们创建Gkt::AboutDialog小部件。

about.set_program_name "Battery"
about.set_version "0.1"
about.set_copyright "(c) Jan Bodnar"

在这里,我们指定名称,版本和版权。

begin       
    logo = Gdk::Pixbuf.new :file => "batter.png"
    about.set_logo logo
rescue IOError => e
    puts e
    puts "cannot load image"
    exit            
end

此行为对话框创建徽标。 执行一些错误检查。

Gtk::AboutDialog

图:Gtk::AboutDialog

Gtk::FontSelectionDialog

Gtk::FontSelectionDialog是用于选择字体的对话框。 它通常用于进行一些文本编辑或格式化的应用中。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example presents the Gtk::FontSelectionDialog.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_border_width 10
        @label = Gtk::Label.new "The only victory over love is flight."
        button = Gtk::Button.new :label => "Select font"

        button.signal_connect "clicked" do
            on_clicked
        end

        fix = Gtk::Fixed.new
        fix.put button, 100, 30
        fix.put @label, 30, 90
        add fix

        set_title "Gtk::FontSelectionDialog"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 300, 150
        set_window_position :center

        show_all        
    end

    def on_clicked

        fdia = Gtk::FontChooserDialog.new :title => "Select font name", 
            :parent => nil
        response = fdia.run

        if response == Gtk::ResponseType::OK

            font_desc = Pango::FontDescription.new fdia.font_desc
            if font_desc

                @label.override_font font_desc
            end
        end

        fdia.destroy
    end     
end

Gtk.init
    window = RubyApp.new
Gtk.main

在代码示例中,我们有一个按钮和一个标签。 单击按钮显示Gtk::FontSelectionDialog

fdia = Gtk::FontSelectionDialog.new "Select font name"

我们创建GtkFontSelectionDialog

if response == Gtk::ResponseType::OK

    font_desc = Pango::FontDescription.new fdia.font_desc
    if font_desc

        @label.override_font font_desc
    end
end

如果单击“确定”按钮,则标签小部件的字体将更改为我们在对话框中选择的字体。

Gtk::ColorSelectionDialog

Gtk::ColorSelectionDialog是用于选择颜色的对话框。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example presents the Gtk::ColorSelectionDialog.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        init_ui
    end

    def init_ui

        set_border_width 10
        @label = Gtk::Label.new "The only victory over love is flight."
        button = Gtk::Button.new :label => "Select colour"

        button.signal_connect "clicked" do
            on_clicked
        end

        fix = Gtk::Fixed.new
        fix.put button, 100, 30
        fix.put @label, 30, 90
        add fix

        set_title "Gtk::ColorSelectionDialog"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end        

        set_default_size 350, 150
        set_window_position :center

        show_all        
    end

    def on_clicked

        cdia = Gtk::ColorSelectionDialog.new :title => "Select colour"
        response = cdia.run

        if response == Gtk::ResponseType::OK

            colorsel = cdia.color_selection
            col = colorsel.current_rgba
            @label.override_color :normal, col
        end

        cdia.destroy
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

该示例与上一个示例非常相似。 这次我们更改标签的颜色。

cdia = Gtk::ColorSelectionDialog.new :title => "Select colour"

我们创建Gtk::ColorSelectionDialog

if response == Gtk::ResponseType::OK

    colorsel = cdia.color_selection
    col = colorsel.current_rgba
    @label.override_color :normal, col
end

如果按“确定”按钮,我们将获得颜色值并修改标签的颜色。

Gtk::ColorSelectionDialog

图:Gtk::ColorSelectionDialog

在 Ruby GTK 教程的这一部分中,我们介绍了对话框。

Ruby GTK Cario 绘图

原文: http://zetcode.com/gui/rubygtk/cairo/

在 Ruby GTK 教程的这一部分中,我们将使用 Cairo 库进行一些绘图。

Cairo 是用于创建 2D 矢量图形的库。 我们可以使用它来绘制自己的小部件,图表或各种效果和动画。

色彩

在第一个示例中,我们将处理颜色。 颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 Cario 有效 RGB 值在 0 到 1 的范围内。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program draws tree rectangles filled
with different colours.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Colours"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        init_ui

        set_default_size 360, 100
        set_window_position :center
        show_all
    end

    def init_ui

        @darea = Gtk::DrawingArea.new  

        @darea.signal_connect "draw" do  
            on_draw
        end

        add @darea
    end

    def on_draw

        cr = @darea.window.create_cairo_context  
        draw_colors cr
    end 

    def draw_colors cr

        cr.set_source_rgb 0.2, 0.23, 0.9
        cr.rectangle 10, 15, 90, 60
        cr.fill

        cr.set_source_rgb 0.9, 0.1, 0.1
        cr.rectangle 130, 15, 90, 60
        cr.fill

        cr.set_source_rgb 0.4, 0.9, 0.4
        cr.rectangle 250, 15, 90, 60
        cr.fill
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在我们的示例中,我们将绘制三个矩形,并用三种不同的颜色填充它们。

@darea = Gtk::DrawingArea.new 

我们将在Gtk::DrawingArea小部件上进行绘制操作。

@darea.signal_connect "draw" do  
    on_draw
end

当需要重绘窗口时,会触发draw信号。 响应此信号,我们称为on_draw方法。

cr = @darea.window.create_cairo_context  

我们从绘图区域的Gdk::Window创建 cairo 上下文对象。 上下文是我们绘制所有图纸的对象。

draw_colors cr

实际图形委托给draw_colors方法。 我们通过 Cario 语境。

cr.set_source_rgb 0.2, 0.23, 0.9

set_source_rgb方法为 Cario 上下文设置颜色。 该方法的三个参数是颜色强度值。

cr.rectangle 10, 15, 90, 60

我们画一个矩形。 前两个参数是矩形左上角的 x,y 坐标。 最后两个参数是矩形的宽度和高度。

cr.fill

我们用当前颜色填充矩形的内部。

Colours

图:颜色

基本形状

下一个示例将一些基本形状绘制到窗口上。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This code example draws basic shapes.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Basic shapes"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        init_ui

        set_default_size 390, 240
        set_window_position :center

        show_all
    end

    def init_ui

        @darea = Gtk::DrawingArea.new  

        @darea.signal_connect "draw" do  
            on_draw
        end

        add @darea
    end

    def on_draw

        cr = @darea.window.create_cairo_context  
        draw_shapes cr
    end

    def draw_shapes cr

        cr.set_source_rgb 0.6, 0.6, 0.6

        cr.rectangle 20, 20, 120, 80
        cr.rectangle 180, 20, 80, 80
        cr.fill

        cr.arc 330, 60, 40, 0, 2*Math::PI
        cr.fill

        cr.arc 90, 160, 40, Math::PI/4, Math::PI
        cr.fill

        cr.translate 220, 180
        cr.scale 1, 0.7
        cr.arc 0, 0, 50, 0, 2*Math::PI
        cr.fill
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在此示例中,我们将创建一个矩形,一个正方形,一个圆形,一个弧形和一个椭圆形。 我们用蓝色绘制轮廓,内部用白色绘制。

cr.rectangle 20, 20, 120, 80
cr.rectangle 180, 20, 80, 80
cr.fill

这些线绘制一个矩形和一个正方形。

cr.arc 330, 60, 40, 0, 2*Math::PI
cr.fill

此处arc方法绘制一个完整的圆。

cr.translate 220, 180
cr.scale 1, 0.7
cr.arc 0, 0, 50, 0, 2*Math::PI
cr.fill

translate方法将对象移动到特定点。 如果要绘制椭圆形,请先进行一些缩放。 在这里scale方法缩小 y 轴。

Basic shapes

图:基本形状

透明矩形

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

在计算机图形学中,我们可以使用 alpha 合成实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program shows transparent rectangles.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Transparent rectangles"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        init_ui

        set_default_size 590, 90
        set_window_position :center

        show_all
    end

    def init_ui

        @darea = Gtk::DrawingArea.new  

        @darea.signal_connect "draw" do  
            on_draw
        end

        add @darea
    end

    def on_draw

        cr = @darea.window.create_cairo_context  
        draw_rectangles cr        
    end

    def draw_rectangles cr

        for i in 1..10
            cr.set_source_rgba 0, 0, 1, i*0.1
            cr.rectangle 50*i, 20, 40, 40
            cr.fill
        end    
    end 
end

Gtk.init
    window = RubyApp.new
Gtk.main

在示例中,我们将绘制十个具有不同透明度级别的矩形。

cr.set_source_rgba 0, 0, 1, i*0.1

set_source_rgba方法的最后一个参数是 alpha 透明度。

Transparent rectangles

图:透明矩形

甜甜圈

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program draws a donut shape.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Donut"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        init_ui

        set_default_size 350, 250
        set_window_position :center

        show_all
    end

    def init_ui

        @darea = Gtk::DrawingArea.new  

        @darea.signal_connect "draw" do  
            on_draw
        end

        add @darea
    end

    def on_draw

        cr = @darea.window.create_cairo_context  
        draw_donut cr
    end

    def draw_donut cr

        cr.set_line_width 0.5

        w = allocation.width
        h = allocation.height

        cr.translate w/2, h/2
        cr.arc 0, 0, 120, 0, 2*Math::PI
        cr.stroke

        for i in 1..36
            cr.save
            cr.rotate i*Math::PI/36
            cr.scale 0.3, 1
            cr.arc 0, 0, 120, 0, 2*Math::PI
            cr.restore
            cr.stroke
        end
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。

cr.translate w/2, h/2

用户空间原点移动到窗口的中心。 圆和所有其他椭圆的中心都位于此处。

cr.translate w/2, h/2
cr.arc 0, 0, 120, 0, 2*Math::PI
cr.stroke

这是外部椭圆。 在此椭圆内,我们绘制所有其他椭圆。

for i in 1..36
    cr.save
    cr.rotate i*Math::PI/36
    cr.scale 0.3, 1
    cr.arc 0, 0, 120, 0, 2*Math::PI
    cr.restore
    cr.stroke
end

我们沿着边界圆的路径创建了 36 个椭圆。 我们使用save()restore()方法将每个旋转和缩放操作相互隔离。

Donut

图:多纳圈

绘制文字

在下一个示例中,我们在窗口上绘制一些文本。

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This program draws text.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Soulmate"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        init_ui

        set_default_size 370, 240
        set_window_position :center

        show_all
    end

    def init_ui

        @darea = Gtk::DrawingArea.new  

        @darea.signal_connect "draw" do  
            on_draw
        end

        add @darea 
    end

    def on_draw

        cr = @darea.window.create_cairo_context  
        draw_lyrics cr
    end

    def draw_lyrics cr

        cr.set_source_rgb 0.1, 0.1, 0.1

        cr.select_font_face "Purisa", Cairo::FONT_SLANT_NORMAL, 
            Cairo::FONT_WEIGHT_NORMAL
        cr.set_font_size 13 

        cr.move_to 20, 30
        cr.show_text "Most relationships seem so transitory"
        cr.move_to 20, 60
        cr.show_text "They're all good but not the permanent one"
        cr.move_to 20, 120
        cr.show_text "Who doesn't long for someone to hold"
        cr.move_to 20, 150
        cr.show_text "Who knows how to love without being told"
        cr.move_to 20, 180
        cr.show_text "Somebody tell me why I'm on my own"
        cr.move_to 20, 210
        cr.show_text "If there's a soulmate for everyone"
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

我们将展示 Natasha Bedingfield 的 Soulmate 歌曲的歌词的一部分。

cr.select_font_face "Purisa", Cairo::FONT_SLANT_NORMAL, 
    Cairo::FONT_WEIGHT_NORMAL

在这里,我们指定使用的字体:Purisa normal。

cr.set_font_size 13 

set_font_size方法指定字体的大小。

cr.move_to 20, 30

我们移至将要绘制文本的位置。

cr.show_text "Most relationships seem so transitory"

show_text方法在窗口上绘制文本。

Soulmate

图:灵魂伴侣

在 Ruby GTK 教程的这一章中,我们使用 Cairo 库绘图。

Ruby GTK 中的自定义小部件

原文: http://zetcode.com/gui/rubygtk/customwidget/

工具箱通常仅提供最常见的窗口小部件,例如按钮,文本窗口小部件,滑块等。没有工具箱可以提供所有可能的窗口小部件。 程序员必须自己创建此类小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能:程序员可以修改或增强现有的小部件,或者可以从头开始创建自定义小部件。

刻录小部件

这是我们从头开始创建的小部件的示例。 可以在各种媒体刻录应用(例如 K3B)中找到此小部件。

custom.rb

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This example creates a custom widget.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'

class Burning < Gtk::DrawingArea

    def initialize parent
        @parent = parent

        super()

        @num = [ "75", "150", "225", "300", 
            "375", "450", "525", "600", "675" ]

        set_size_request 1, 30
        signal_connect "draw" do
            on_draw
        end
    end

    def on_draw

        cr = window.create_cairo_context
        draw_widget cr
    end

    def draw_widget cr

        cr.set_line_width 0.8

        cr.select_font_face "Courier", 
            Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL
        cr.set_font_size 11

        width = allocation.width

        @cur_width = @parent.get_cur_value

        step = (width / 10.0).round

        till = (width / 750.0) * @cur_width
        full = (width / 750.0) * 700

        if @cur_width >= 700

            cr.set_source_rgb 1.0, 1.0, 0.72
            cr.rectangle 0, 0, full, 30
            cr.clip
            cr.paint
            cr.reset_clip

            cr.set_source_rgb 1.0, 0.68, 0.68
            cr.rectangle full, 0, till-full, 30
            cr.clip
            cr.paint
            cr.reset_clip

        else
            cr.set_source_rgb 1.0, 1.0, 0.72
            cr.rectangle 0, 0, till, 30
            cr.clip
            cr.paint
            cr.reset_clip
        end

        cr.set_source_rgb(0.35, 0.31, 0.24)

        for i in 1..@num.length
            cr.move_to i*step, 0
            cr.line_to i*step, 5
            cr.stroke

            te = cr.text_extents @num[i-1]
            cr.move_to i*step-te.width/2, 15
            cr.text_path @num[i-1]
            cr.stroke
        end         
    end
end

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Burning"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        set_size_request 350, 200        
        set_window_position :center

        @cur_value = 0

        vbox = Gtk::Box.new :vertical, 2

        scale = Gtk::Scale.new :horizontal
        scale.set_range 0, 750
        scale.set_digits 0
        scale.set_size_request 160, 35
        scale.set_value @cur_value

        scale.signal_connect "value-changed" do |w|
            on_changed w
        end

        fix = Gtk::Fixed.new
        fix.put scale, 50, 50

        vbox.pack_start fix

        @burning = Burning.new self
        vbox.pack_start @burning, :expand => false, 
            :fill => false, :padding => 0

        add vbox
        show_all
    end    

    def on_changed widget

        @cur_value = widget.value
        @burning.queue_draw
    end

    def get_cur_value
        return @cur_value
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

我们在窗口底部放置一个Gtk::DrawingArea并手动绘制整个窗口小部件。 所有重要的代码都驻留在draw_widget中,这是从 Burning 类的on_draw方法调用的。 此小部件以图形方式显示介质的总容量和可用空间。 该小部件由比例小部件控制。 自定义窗口小部件的最小值为 0,最大值为 750。如果值达到 700,则开始绘制红色。 这通常表示过度燃烧。

@num = [ "75", "150", "225", "300", 
    "375", "450", "525", "600", "675" ]

这些数字显示在刻录小部件上。 它们显示了介质的容量。

@cur_width = @parent.get_cur_value

从父小部件中,我们获得了比例小部件的当前值。

till = (width / 750.0) * @cur_width
full = (width / 750.0) * 700

我们使用width变量在小数位的值和自定义小部件的度量之间进行转换。 请注意,我们使用浮点值-在绘图中获得更高的精度。 till参数确定要绘制的总大小。 该值来自滑块小部件。 它占整个面积的一部分。 full参数确定我们开始绘制红色的点。

cr.set_source_rgb 1.0, 1.0, 0.72
cr.rectangle 0, 0, till, 30
cr.clip
cr.paint
cr.reset_clip

我们绘制一个黄色矩形,直到介质充满的地方。

te = cr.text_extents @num[i-1]
cr.move_to i*step-te.width/2, 15
cr.text_path @num[i-1]
cr.stroke

这里的代码在刻录小部件上绘制数字。 我们计算文本范围以正确定位文本。

def on_changed widget

    @cur_value = widget.value
    @burning.queue_draw
end

我们从小部件中获取值,并将其存储在@cur_value变量中以备后用。 我们重新绘制刻录的小部件。

Burning widget

图:刻录小部件

在本章中,我们使用 GTK 和 Ruby 编程语言创建了一个自定义小部件。

Ruby GTK 中的贪食蛇

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

在 Ruby GTK 编程教程的这一部分中,我们将创建一个贪食蛇游戏克隆。

贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。

开发

蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。

board.rb

WIDTH = 300
HEIGHT = 270
DOT_SIZE = 10
ALL_DOTS = WIDTH * HEIGHT / (DOT_SIZE * DOT_SIZE)
RAND_POS = 26
DELAY = 100

$x = [0] * ALL_DOTS
$y = [0] * ALL_DOTS

class Board < Gtk::DrawingArea

    def initialize
        super

        override_background_color :normal, Gdk::RGBA.new(0, 0, 0, 1)

        signal_connect "draw" do  
            on_draw
        end

        init_game
    end

    def on_timer

        if @inGame
            check_apple
            check_collision
            move
            queue_draw
            return true
        else
            return false
        end
    end

    def init_game

        @left = false
        @right = true
        @up = false
        @down = false
        @inGame = true
        @dots = 3

        for i in 0..@dots
            $x[i] = 50 - i * 10
            $y[i] = 50
        end

        begin
            @dot = Cairo::ImageSurface.from_png "dot.png"
            @head = Cairo::ImageSurface.from_png "head.png"
            @apple = Cairo::ImageSurface.from_png "apple.png"
        rescue Exception => e
            puts "cannot load images"
            exit
        end

        locate_apple
        GLib::Timeout.add(DELAY) { on_timer }
     end   

    def on_draw 

        cr = window.create_cairo_context

        if @inGame
            draw_objects cr
        else
            game_over cr
        end      
    end

    def draw_objects cr

        cr.set_source_rgb 0, 0, 0
        cr.paint

        cr.set_source @apple, @apple_x, @apple_y
        cr.paint

        for z in 0..@dots
            if z == 0 
                cr.set_source @head, $x[z], $y[z]
                cr.paint
            else
                cr.set_source @dot, $x[z], $y[z]
                cr.paint
            end    
        end
    end

    def game_over cr

        w = allocation.width / 2
        h = allocation.height / 2

        cr.set_font_size 15
        te = cr.text_extents "Game Over"

        cr.set_source_rgb 65535, 65535, 65535

        cr.move_to w - te.width/2, h
        cr.show_text "Game Over"
    end

    def check_apple

        if $x[0] == @apple_x and $y[0] == @apple_y 
            @dots = @dots + 1
            locate_apple
        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 check_collision

        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] > HEIGHT - DOT_SIZE
            @inGame = false
        end

        if $y[0] < 0
            @inGame = false
        end

        if $x[0] > WIDTH - DOT_SIZE
            @inGame = false
        end

        if $x[0] < 0
            @inGame = false
        end    
    end

    def locate_apple

        r = rand RAND_POS
        @apple_x = r * DOT_SIZE
        r = rand RAND_POS
        @apple_y = r * DOT_SIZE
    end

    def on_key_down event

        key = event.keyval

        if key == Gdk::Keyval::GDK_KEY_Left and not @right
            @left = true
            @up = false
            @down = false
        end

        if key == Gdk::Keyval::GDK_KEY_Right and not @left
            @right = true
            @up = false
            @down = false
        end

        if key == Gdk::Keyval::GDK_KEY_Up and not @down
            @up = true
            @right = false
            @left = false
        end

        if key == Gdk::Keyval::GDK_KEY_Down and not @up
            @down = true
            @right = false
            @left = false
        end
    end   
end

首先,我们将定义一些在游戏中使用的全局变量。 WIDTHHEIGHT常数确定Board的大小。 DOT_SIZE是苹果的大小和蛇的点。 ALL_DOTS常数定义Board上可能的最大点数。 RAND_POS常数用于计算苹果的随机位置。 DELAY常数确定游戏的速度。

$x = [0] * ALL_DOTS
$y = [0] * ALL_DOTS

这两个数组存储蛇的所有可能关节的 x,y 坐标。

init_game方法初始化游戏。

@left = false
@right = true
@up = false
@down = false
@inGame = true
@dots = 3

我们初始化我们在游戏中使用的变量。

for i in 0..@dots
    $x[i] = 50 - i * 10
    $y[i] = 50
end

我们给蛇关节初始坐标。 它总是从同一位置开始。

begin
    @dot = Cairo::ImageSurface.from_png "dot.png"
    @head = Cairo::ImageSurface.from_png "head.png"
    @apple = Cairo::ImageSurface.from_png "apple.png"
rescue Exception => e
    puts "cannot load images"
    exit
end

加载了必要的图像。

locate_apple

苹果进入初始随机位置。

GLib::Timeout.add(DELAY) { on_timer }

GLib::Timeout.add方法将on_timer方法设置为每DELAY毫秒调用一次。

if @inGame
    draw_objects cr
else
    game_over cr
end      

on_draw方法内部,我们检查@inGame变量。 如果是真的,我们绘制对象:苹果和蛇关节。 否则,我们显示"Game Over"文本。

def draw_objects cr

    cr.set_source_rgb 0, 0, 0
    cr.paint

    cr.set_source @apple, @apple_x, @apple_y
    cr.paint

    for z in 0..@dots
        if z == 0 
            cr.set_source @head, $x[z], $y[z]
            cr.paint
        else
            cr.set_source @dot, $x[z], $y[z]
            cr.paint
        end    
    end
end

draw_objects方法绘制苹果和蛇的关节。 蛇的第一个关节是其头部,用红色圆圈表示。

def check_apple

    if $x[0] == @apple_x and $y[0] == @apple_y 
        @dots = @dots + 1
        locate_apple
    end
end

check_apple方法检查蛇是否击中了苹果对象。 如果是这样,我们添加另一个蛇形关节并调用locate_apple方法,该方法随机放置一个新的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

如果向左移动,则磁头向左移动。

check_collision方法中,我们确定蛇是否击中了自己或撞墙之一。

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] > HEIGHT - DOT_SIZE
    @inGame = false
end

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

locate_apple方法在板上随机放置一个苹果。

r = rand RAND_POS

我们得到一个从 0 到RAND_POS-1的随机数。

@apple_x = r * DOT_SIZE
...
@apple_y = r * DOT_SIZE

这些行设置了apple对象的 x,y 坐标。

if @inGame
    check_apple
    check_collision
    move
    queue_draw
    return true
else
    return false
end

DELAY ms 会调用一次on_timer方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 否则,我们返回false,它将停止计时器事件。

Board类的on_key_down方法中,我们确定按下的键。

if key == Gdk::Keyval::GDK_KEY_Left and not @right
    @left = true
    @up = false
    @down = false
end

如果单击左光标键,则将left变量设置为true。 在move方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。

nibbles.rb

#!/usr/bin/ruby

'''
ZetCode Ruby GTK tutorial

This is a simple Nibbles game
clone.

Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''

require 'gtk3'
require './board'

class RubyApp < Gtk::Window

    def initialize
        super

        set_title "Nibbles"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end

        @board = Board.new
        signal_connect "key-press-event" do |w, e|
            on_key_down w, e
        end

        add @board

        set_default_size WIDTH, HEIGHT
        set_window_position :center
        show_all
    end

    def on_key_down widget, event 

        key = event.keyval
        @board.on_key_down event
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

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

def on_key_down widget, event 

    key = event.keyval
    @board.on_key_down event
end

我们捕获按键事件,并将处理委托给电路板类的on_key_down方法。

Nibbles

图:贪食蛇

这是使用 GTK 库和 Ruby 编程语言编程的贪食蛇电脑游戏。

GTK# 教程

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

这是针对 C# 编程语言的 GTK# 教程。 本教程适合初学者和更高级的程序员。

目录

GTK#

GTK# 是针对 C# 编程语言的 GTK+ 的包装。 该库有助于使用 Mono 或任何其他兼容的 CLR 构建图形 GNOME 应用。 使用 Gtk# 构建的应用将在许多平台上运行,包括 Linux,Microsoft Windows 和 Mac OSX。GTK# 是 Mono 计划的一部分。

Tweet

相关教程

C# 教程深入介绍了 C# 语言。 Mono C# Winforms 教程C# Qyoto 教程提供了 C# 语言的替代 GUI 工具箱。

GTK# 简介

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

这是 GTK# 编程入门教程。 本教程针对 C# 编程语言。 它已在 Linux 上创建并经过测试。 GTK# 编程教程适合新手和中级程序员。 这是本教程中使用的图像

GTK+

GTK+ 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK+ 库也称为 GIMP 工具包。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK+ 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK+ 中创建的。 GTK+ 是面向对象的应用编程接口。 面向对象的系统是使用 Glib 对象系统创建的,该系统是 GTK+ 库的基础。 GObject还可以为其他各种编程语言创建语言绑定。 存在用于 C++ ,Python,Perl,Java,C# 和其他编程语言的语言绑定。

GTK+ 本身取决于以下库。

  • Glib
  • Pango
  • ATK
  • GDK
  • GdkPixbuf
  • Cario

Glib是通用工具库。 它提供各种数据类型,字符串工具,启用错误报告,消息日志记录,使用线程和其他有用的编程功能。 Pango是一个使国际化的库。 ATK是辅助功能工具包。 该工具包提供的工具可帮助残障人士使用计算机。 GDK是底层图形系统提供的低级绘图和窗口功能的包装。 在 Linux 上,GDK 位于 X Server 和 GTK+ 库之间。 最近,它的许多功能已委托给 Cario 库。 GdkPixbuf库是用于图像加载和像素缓冲区操作的工具包。 Cario 是用于创建 2D 矢量图形的库。 自 2.8 版起,它已包含在 GTK+ 中。

Gnome 和 XFce 桌面环境已使用 GTK+ 库创建。 SWT 和 wxWidgets 是使用 GTK+ 的众所周知的编程框架。 使用 GTK+ 的著名软件应用包括 Firefox 或 Inkscape。

GTK#

GTK# 是针对 C# 编程语言的 GTK+ 的包装。 该库有助于使用 Mono 或任何其他兼容的 CLR 构建图形 GNOME 应用。 Gtk# 是一个事件驱动的系统,就像任何其他现代的窗口库一样,其中应用中的每个小部件都具有处理器方法,这些处理器方法在发生特定事件时被调用。 使用 Gtk# 构建的应用将在许多平台上运行,包括 Linux,Microsoft,Windows 和 Mac OSX。GTK# 是 Mono 计划的一部分。 Mono 中基本上有两个窗口小部件工具箱:Winforms 和 GTK# 。 GTK# 被认为是 Linux/Unix 操作系统的本机。

编译 GTK# 应用

我们使用 gmcs 编译器来编译我们的 GTK# 应用。

$ gmcs -pkg:gtk-sharp-2.0 -r:/usr/lib/mono/2.0/Mono.Cairo.dll application.cs

上一行编译了一个也使用 Cario 库的 GTK# 应用。

数据来源

在 GTK# 教程的这一部分中,我们介绍了 GTK# 库。

PyQt5 中的布局管理

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

布局管理是我们将小部件放置在应用窗口中的方式。 我们可以使用绝对定位或布局类放置小部件。 使用布局管理器管理布局是组织窗口小部件的首选方式。

绝对定位

程序员以像素为单位指定每个小部件的位置和大小。 使用绝对定位时,我们必须了解以下限制:

  • 如果我们调整窗口大小,则小部件的大小和位置不会改变
  • 在各种平台上,应用看起来可能有所不同
  • 在我们的应用中更改字体可能会破坏布局
  • 如果我们决定更改布局,则必须完全重做布局,这既繁琐又耗时

以下示例将小部件放置在绝对坐标中。

absolute.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This example shows three labels on a window
using absolute positioning. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        lbl1 = QLabel('Zetcode', self)
        lbl1.move(15, 10)

        lbl2 = QLabel('tutorials', self)
        lbl2.move(35, 40)

        lbl3 = QLabel('for programmers', self)
        lbl3.move(55, 70)        

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Absolute')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

我们使用move()方法定位小部件。 在我们的例子中,这些是标签。 我们通过提供 x 和 y 坐标来定位它们。 坐标系的起点在左上角。 x 值从左到右增长。 y 值从上到下增长。

lbl1 = QLabel('Zetcode', self)
lbl1.move(15, 10)

标签窗口小部件位于x=15y=10处。

Absolute positioning

图:绝对定位

盒子布局

QHBoxLayoutQVBoxLayout是在水平和垂直方向排列小部件的基本布局类。

想象一下,我们想在右下角放置两个按钮。 为了创建这样的布局,我们使用一个水平框和一个垂直框。 为了创建必要的空间,我们添加了拉伸因子。

buttons.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we position two push
buttons in the bottom-right corner 
of the window. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import (QWidget, QPushButton, 
    QHBoxLayout, QVBoxLayout, QApplication)

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        okButton = QPushButton("OK")
        cancelButton = QPushButton("Cancel")

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(okButton)
        hbox.addWidget(cancelButton)

        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)

        self.setLayout(vbox)    

        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('Buttons')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

该示例在窗口的右下角放置了两个按钮。 当我们调整应用窗口的大小时,它们会停留在该位置。 我们同时使用HBoxLayoutQVBoxLayout

okButton = QPushButton("OK")
cancelButton = QPushButton("Cancel")

在这里,我们创建两个按钮。

hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)

我们创建一个水平框布局,并添加一个拉伸因子和两个按钮。 拉伸在两个按钮之前增加了可拉伸的空间。 这会将它们推到窗口的右侧。

vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)

将水平布局放置在垂直布局中。 垂直框中的拉伸因子会将带有按钮的水平框推到窗口底部。

self.setLayout(vbox)

最后,我们设置窗口的主要布局。

Buttons

图:按钮

QGridLayout

QGridLayout是最通用的布局类。 它将空间分为行和列。

calculator.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we create a skeleton
of a calculator using QGridLayout.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout, 
    QPushButton, QApplication)

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        grid = QGridLayout()
        self.setLayout(grid)

        names = ['Cls', 'Bck', '', 'Close',
                 '7', '8', '9', '/',
                '4', '5', '6', '*',
                 '1', '2', '3', '-',
                '0', '.', '=', '+']

        positions = [(i,j) for i in range(5) for j in range(4)]

        for position, name in zip(positions, names):

            if name == '':
                continue
            button = QPushButton(name)
            grid.addWidget(button, *position)

        self.move(300, 150)
        self.setWindowTitle('Calculator')
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们创建了一个按钮网格。

grid = QGridLayout()
self.setLayout(grid)

创建QGridLayout的实例,并将其设置为应用窗口的布局。

names = ['Cls', 'Bck', '', 'Close',
            '7', '8', '9', '/',
        '4', '5', '6', '*',
            '1', '2', '3', '-',
        '0', '.', '=', '+']

这些是稍后用于按钮的标签。

positions = [(i,j) for i in range(5) for j in range(4)]

我们在网格中创建位置列表。

for position, name in zip(positions, names):

    if name == '':
        continue
    button = QPushButton(name)
    grid.addWidget(button, *position)

使用addWidget()方法创建按钮并将其添加到布局。

Calculator skeleton

图:计算机骨架

回顾示例

小部件可以跨越网格中的多个列或行。 在下一个示例中,我们将对此进行说明。

review.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we create a bit
more complicated window layout using
the QGridLayout manager. 

author: Jan Bodnar
website: zetcode.com 
last edited: January 2015
"""

import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit, 
    QTextEdit, QGridLayout, QApplication)

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        title = QLabel('Title')
        author = QLabel('Author')
        review = QLabel('Review')

        titleEdit = QLineEdit()
        authorEdit = QLineEdit()
        reviewEdit = QTextEdit()

        grid = QGridLayout()
        grid.setSpacing(10)

        grid.addWidget(title, 1, 0)
        grid.addWidget(titleEdit, 1, 1)

        grid.addWidget(author, 2, 0)
        grid.addWidget(authorEdit, 2, 1)

        grid.addWidget(review, 3, 0)
        grid.addWidget(reviewEdit, 3, 1, 5, 1)

        self.setLayout(grid) 

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Review')    
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

我们创建一个窗口,其中有三个标签,两个行编辑和一个文本编辑小部件。 使用QGridLayout完成布局。

grid = QGridLayout()
grid.setSpacing(10)

我们创建网格布局并设置小部件之间的间距。

grid.addWidget(reviewEdit, 3, 1, 5, 1)

如果我们将小部件添加到网格,则可以提供小部件的行跨度和列跨度。 在我们的例子中,我们使reviewEdit小部件跨越 5 行。

Review example

图:回顾 example

PyQt5 教程的这一部分专门用于布局管理。

GTK 的第一步

原文: http://zetcode.com/gui/gtksharp/firststeps/

在 GTK# 编程教程的这一部分中,我们将进行编程的第一步。 我们将创建简单的程序。

简单的例子

第一个代码示例是一个简单的示例,它显示了居中的窗口。

center.cs

using Gtk;

class SharpApp : Window {

    public SharpApp() : base("Center")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        Show();    
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();        
        Application.Run();
    }
}

该代码示例在屏幕中央显示一个小窗口。

$ gmcs -pkg:gtk-sharp-2.0 center.cs

这是我们编译代码示例的方式。

using Gtk;

现在,我们可以直接使用Gtk名称空间中的对象。 我们可以写Window而不是Gtk.Window

class SharpApp : Window {

我们的应用基于SharpApp类。 该类继承自Window类。

public SharpApp() : base("Center")
{
    ...   
}

这是构造器。 它构建了我们的应用。 它还通过base()关键字调用其父构造器。

SetDefaultSize(250, 200);

这行为我们的窗口设置默认大小。

SetPosition(WindowPosition.Center);

这条线使窗口在屏幕上居中。

DeleteEvent += delegate { Application.Quit(); };

我们将一个代理插入DeleteEvent。 当我们单击标题栏中的关闭按钮时,将触发此事件。 或按 Alt + F4 。 我们的代表永久退出了申请。

Show();

现在我们显示窗口。 在调用Show()方法之前,该窗口不可见。

public static void Main()
{
    Application.Init();
    new SharpApp();        
    Application.Run();
}

Main()方法是应用的入口点。 它启动并运行程序。

图标

在下一个示例中,我们显示应用图标。 大多数窗口管理器在标题栏的左上角以及任务栏上都显示图标。

icon.cs


using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Icon")
    {
        SetDefaultSize(250, 160);
        SetPosition(WindowPosition.Center);
        SetIconFromFile("web.png");

        DeleteEvent += new DeleteEventHandler(OnDelete);

        Show();      
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }

    void OnDelete(object obj, DeleteEventArgs args)
    {
        Application.Quit();
    }
}

该代码示例显示了应用图标。

SetIconFromFile("web.png");

SetIconFromFile()方法为窗口设置图标。 从当前工作目录中的磁盘加载映像。

DeleteEvent += new DeleteEventHandler(OnDelete);

这是另一种方式,我们如何将事件处理器插入事件。 只是有点冗长。

void OnDelete(object obj, DeleteEventArgs args)
{
    Application.Quit();
}

这是删除事件的事件处理器。

Icon

图:图标

按钮

在下一个示例中,我们将使用 GTK# 库进一步增强我们的编程技能。

buttons.cs

using Gtk;

class SharpApp : Window
{

    public SharpApp() : base("Buttons")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        Fixed fix = new Fixed();

        Button btn1 = new Button("Button");
        btn1.Sensitive = false;
        Button btn2 = new Button("Button");
        Button btn3 = new Button(Stock.Close);
        Button btn4 = new Button("Button");
        btn4.SetSizeRequest(80, 40);

        fix.Put(btn1, 20, 30);
        fix.Put(btn2, 100, 30);
        fix.Put(btn3, 20, 80);
        fix.Put(btn4, 100, 80);

        Add(fix);
        ShowAll();
    }

    public static void Main() 
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们在窗口上显示四个不同的按钮。 我们将看到容器窗口小部件和子窗口小部件之间的区别,并将更改子窗口小部件的某些属性。

Fixed fix = new Fixed();

Fixed小部件是不可见的容器小部件。 其目的是包含其他子窗口小部件。

Button btn1 = new Button("Button");

Button是子窗口小部件。 子窗口小部件放置在容器内。

btn1.Sensitive = false;

我们使此按钮不敏感。 这意味着我们无法单击它。 图形化的小部件为灰色。

Button btn3 = new Button(Stock.Close);

第三个按钮在其区域内显示图像。 GTK# 库具有我们可以使用的内置图像库。

btn4.SetSizeRequest(80, 40);

在这里,我们更改按钮的大小。

fix.Put(btn1, 20, 30);
fix.Put(btn2, 100, 30);
...

在这里,我们将按钮小部件放置在固定容器小部件内。

Add(fix);

我们将Fixed容器设置为Window小部件的主要容器。

ShowAll();

我们可以调用ShowAll()方法,也可以在每个小部件上调用Show()方法。 包括容器。

Buttons

图:按钮

在本章中,我们在 GTK# 编程库中创建了第一个程序。

GTK# 中的布局管理

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

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

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

Fixed

Fixed容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 在大多数应用中,我们不使用此容器。 我们在某些专业领域使用它。 例如游戏,使用图表的专用应用,可以移动的可调整大小的组件(如电子表格应用中的图表),小型教育示例。

fixed.cs

using Gtk;
using System;

class SharpApp : Window {

    private Gdk.Pixbuf rotunda;
    private Gdk.Pixbuf bardejov;
    private Gdk.Pixbuf mincol;

    public SharpApp() : base("Fixed")
    {
        SetDefaultSize(300, 280);
        SetPosition(WindowPosition.Center);
        ModifyBg(StateType.Normal, new Gdk.Color(40, 40, 40));
        DeleteEvent += delegate { Application.Quit(); };

        try {
            bardejov = new Gdk.Pixbuf("bardejov.jpg");
            rotunda = new Gdk.Pixbuf("rotunda.jpg");
            mincol = new Gdk.Pixbuf("mincol.jpg");
        } catch {
            Console.WriteLine("Images not found");
            Environment.Exit(1);
        }

        Image image1 = new Image(bardejov);
        Image image2 = new Image(rotunda);
        Image image3 = new Image(mincol);

        Fixed fix = new Fixed();

        fix.Put(image1, 20, 20);
        fix.Put(image2, 40, 160);
        fix.Put(image3, 170, 50);

        Add(fix);
        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的示例中,我们在窗口上显示了三个小图像。 我们明确指定放置这些图像的 x,y 坐标。

ModifyBg(StateType.Normal, new Gdk.Color(40, 40, 40));

为了获得更好的视觉体验,我们将背景色更改为深灰色。

bardejov = new Gdk.Pixbuf("bardejov.jpg");

我们将图像从磁盘加载到Gdk.Pixbuf对象。

Image image1 = new Image(bardejov);
Image image2 = new Image(rotunda);
Image image3 = new Image(mincol);

Image是用于显示图像的小部件。 它在构造器中使用Gdk.Pixbuf对象。

Fixed fix = new Fixed();

我们创建Fixed容器。

fix.Put(image1, 20, 20);

我们将第一个图像放置在x = 20y = 20坐标处。

Add(fix);

最后,我们将Fixed容器添加到窗口中。

Fixed

图:固定

Alignment

Alignment容器控制其子窗口小部件的对齐方式和大小。

alignment.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Alignment")
    {
        SetDefaultSize(260, 150);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        VBox vbox = new VBox(false, 5);
        HBox hbox = new HBox(true, 3);

        Alignment valign = new Alignment(0, 1, 0, 0);
        vbox.PackStart(valign);

        Button ok = new Button("OK");
        ok.SetSizeRequest(70, 30);
        Button close = new Button("Close");

        hbox.Add(ok);
        hbox.Add(close);

        Alignment halign = new Alignment(1, 0, 0, 0);
        halign.Add(hbox);

        vbox.PackStart(halign, false, false, 3);

        Add(vbox);

        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在代码示例中,我们在窗口的右下角放置了两个按钮。 为此,我们使用一个水平框和一个垂直框以及两个对齐容器。

Alignment valign = new Alignment(0, 1, 0, 0);

这会将子窗口小部件置于底部。

vbox.PackStart(valign);

在这里,我们将Alignment小部件放置到垂直框中。

HBox hbox = new HBox(true, 3);
...
Button ok = new Button("OK");
ok.SetSizeRequest(70, 30);
Button close = new Button("Close");

hbox.Add(ok);
hbox.Add(close);

我们创建一个水平框,并在其中放置两个按钮。

Alignment halign = new Alignment(1, 0, 0, 0);
halign.Add(hbox);

vbox.PackStart(halign, false, false, 3);

这将创建一个对齐容器,它将其子窗口小部件放在右侧。 我们将水平框添加到对齐容器中,然后将对齐容器包装到垂直框中。 我们必须记住,对齐容器仅包含一个子窗口小部件。 这就是为什么我们必须使用盒子。

Alignment

图:对齐

Table

Table小部件按行和列排列小部件。

calculator.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Calculator")
    {
        SetDefaultSize(250, 230);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        VBox vbox = new VBox(false, 2);

        MenuBar mb = new MenuBar();
        Menu filemenu = new Menu();
        MenuItem file = new MenuItem("File");
        file.Submenu = filemenu;
        mb.Append(file);

        vbox.PackStart(mb, false, false, 0);

        Table table = new Table(5, 4, true);

        table.Attach(new Button("Cls"), 0, 1, 0, 1);
        table.Attach(new Button("Bck"), 1, 2, 0, 1);
        table.Attach(new Label(), 2, 3, 0, 1);
        table.Attach(new Button("Close"), 3, 4, 0, 1);

        table.Attach(new Button("7"), 0, 1, 1, 2);
        table.Attach(new Button("8"), 1, 2, 1, 2);
        table.Attach(new Button("9"), 2, 3, 1, 2);
        table.Attach(new Button("/"), 3, 4, 1, 2);

        table.Attach(new Button("4"), 0, 1, 2, 3);
        table.Attach(new Button("5"), 1, 2, 2, 3);
        table.Attach(new Button("6"), 2, 3, 2, 3);
        table.Attach(new Button("*"), 3, 4, 2, 3);

        table.Attach(new Button("1"), 0, 1, 3, 4);
        table.Attach(new Button("2"), 1, 2, 3, 4);
        table.Attach(new Button("3"), 2, 3, 3, 4);
        table.Attach(new Button("-"), 3, 4, 3, 4);

        table.Attach(new Button("0"), 0, 1, 4, 5);
        table.Attach(new Button("."), 1, 2, 4, 5);
        table.Attach(new Button("="), 2, 3, 4, 5);
        table.Attach(new Button("+"), 3, 4, 4, 5);

        vbox.PackStart(new Entry(), false, false, 0);
        vbox.PackEnd(table, true, true, 0);

        Add(vbox);
        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们使用Table小部件创建一个计算器框架。

Table table = new Table(5, 4, true);

我们创建一个具有 5 行 4 列的表小部件。 第三个参数是齐次参数。 如果设置为true,则表中的所有小部件都具有相同的大小。 所有窗口小部件的大小等于表容器中最大的窗口小部件。

table.Attach(new Button("Cls"), 0, 1, 0, 1);

我们在表格容器上附加一个按钮。 到表格的左上方单元格。 前两个参数是单元格的左侧和右侧,后两个参数是单元格的顶部和左侧。

vbox.PackEnd(table, true, true, 0);

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

Calculator skeleton

图:计算机骨架

窗口

接下来,我们将创建一个更高级的示例。 我们显示一个窗口,可以在 JDeveloper IDE 中找到它。

windows.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Windows")
    {
        SetDefaultSize(300, 250);
        SetPosition(WindowPosition.Center);
        BorderWidth = 15;
        DeleteEvent += delegate { Application.Quit(); };

        Table table = new Table(8, 4, false);
        table.ColumnSpacing = 3;

        Label title = new Label("Windows");

        Alignment halign = new Alignment(0, 0, 0, 0);
        halign.Add(title);

        table.Attach(halign, 0, 1, 0, 1, AttachOptions.Fill, 
            AttachOptions.Fill, 0, 0);

        TextView wins = new TextView();
        wins.ModifyFg(StateType.Normal, new Gdk.Color(20, 20, 20));
        wins.CursorVisible = false;
        table.Attach(wins, 0, 2, 1, 3, AttachOptions.Fill | AttachOptions.Expand,
            AttachOptions.Fill | AttachOptions.Expand, 1, 1);

        Button activate = new Button("Activate");
        activate.SetSizeRequest(50, 30);
        table.Attach(activate, 3, 4, 1, 2, AttachOptions.Fill, 
            AttachOptions.Shrink, 1, 1);

        Alignment valign = new Alignment(0, 0, 0, 0);
        Button close = new Button("Close");
        close.SetSizeRequest(70, 30);
        valign.Add(close);
        table.SetRowSpacing(1, 3);
        table.Attach(valign, 3, 4, 2, 3, AttachOptions.Fill,
            AttachOptions.Fill | AttachOptions.Expand, 1, 1);

        Alignment halign2 = new Alignment(0, 1, 0, 0);
        Button help = new Button("Help");
        help.SetSizeRequest(70, 30);
        halign2.Add(help);
        table.SetRowSpacing(3, 6);
        table.Attach(halign2, 0, 1, 4, 5, AttachOptions.Fill, 
            AttachOptions.Fill, 0, 0);

        Button ok = new Button("OK");
        ok.SetSizeRequest(70, 30);
        table.Attach(ok, 3, 4, 4, 5, AttachOptions.Fill, 
            AttachOptions.Fill, 0, 0);

        Add(table);
        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该代码示例显示了如何在 GTK# 中创建类似的窗口。

Table table = new Table(8, 4, false);
table.ColumnSpacing = 3;

该示例基于Table容器。 列之间将有 3px 的间距。

Label title = new Label("Windows");

Alignment halign = new Alignment(0, 0, 0, 0);
halign.Add(title);

table.Attach(halign, 0, 1, 0, 1, AttachOptions.Fill, 
    AttachOptions.Fill, 0, 0);

这段代码创建了一个向左对齐的标签。 标签放置在Table容器的第一行中。

TextView wins = new TextView();
wins.ModifyFg(StateType.Normal, new Gdk.Color(20, 20, 20));
wins.CursorVisible = false;
table.Attach(wins, 0, 2, 1, 3, AttachOptions.Fill | AttachOptions.Expand,
    AttachOptions.Fill | AttachOptions.Expand, 1, 1);

文本视图小部件跨越两行两列。 我们使小部件不可编辑并隐藏光标。

Alignment valign = new Alignment(0, 0, 0, 0);
Button close = new Button("Close");
close.SetSizeRequest(70, 30);
valign.Add(close);
table.SetRowSpacing(1, 3);
table.Attach(valign, 3, 4, 2, 3, AttachOptions.Fill,
    AttachOptions.Fill | AttachOptions.Expand, 1, 1);

我们将关闭按钮放在文本视图小部件旁边的第四列中。 (我们从零开始计数)将按钮添加到对齐小部件中,以便可以将其对齐到顶部。

Windows

图:窗口

在本章中,我们介绍了 GTK# 小部件的布局管理。

GTK 中的菜单

原文: http://zetcode.com/gui/gtksharp/menus/

在 GTK# 编程教程的这一部分中,我们将使用菜单。

菜单栏是 GUI 应用中最常见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,我们必须记住各种奥术命令,而在这里,我们将大多数命令分组为逻辑部分。 这些公认的标准可进一步减少学习新应用的时间。

简单菜单

在第一个示例中,我们将创建一个带有一个文件菜单的菜单栏。 该菜单将只有一个菜单项。 通过选择项目,应用退出。

simplemenu.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Simple menu")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        MenuBar mb = new MenuBar();

        Menu filemenu = new Menu();
        MenuItem file = new MenuItem("File");
        file.Submenu = filemenu;

        MenuItem exit = new MenuItem("Exit");
        exit.Activated += OnActivated;
        filemenu.Append(exit);

        mb.Append(file);

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(mb, false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnActivated(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

这是一个最小的菜单栏功能示例。

MenuBar mb = new MenuBar();

MenuBar小部件已创建。

Menu filemenu = new Menu();
MenuItem file = new MenuItem("File");
file.Submenu = filemenu;

创建顶层MenuItem

MenuItem exit = new MenuItem("Exit");
exit.Activated += OnActivated;
filemenu.Append(exit);

将创建出口MenuItem,并将其附加到文件MenuItem中。

mb.Append(file);

顶级MenuItem被附加到MenuBar小部件。

VBox vbox = new VBox(false, 2);
vbox.PackStart(mb, false, false, 0);

与其他工具包不同,我们必须自己照顾布局管理。 我们将菜单栏放入垂直框中。

Simple menu

图:简单菜单

图像菜单

在下一个示例中,我们将进一步探索菜单。 我们将图像和加速器添加到我们的菜单项中。 加速器是用于激活菜单项的键盘快捷键。

imagemenu.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Image menu")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        MenuBar mb = new MenuBar();

        Menu filemenu = new Menu();
        MenuItem file = new MenuItem("File");
        file.Submenu = filemenu;

        AccelGroup agr = new AccelGroup();
        AddAccelGroup(agr);

        ImageMenuItem newi = new ImageMenuItem(Stock.New, agr);
        newi.AddAccelerator("activate", agr, new AccelKey(
            Gdk.Key.n, Gdk.ModifierType.ControlMask, AccelFlags.Visible));
        filemenu.Append(newi);

        ImageMenuItem open = new ImageMenuItem(Stock.Open, agr);
        open.AddAccelerator("activate", agr, new AccelKey(
            Gdk.Key.n, Gdk.ModifierType.ControlMask, AccelFlags.Visible));
        filemenu.Append(open);

        SeparatorMenuItem sep = new SeparatorMenuItem();
        filemenu.Append(sep);

        ImageMenuItem exit = new ImageMenuItem(Stock.Quit, agr);
        exit.AddAccelerator("activate", agr, new AccelKey(
            Gdk.Key.q, Gdk.ModifierType.ControlMask, AccelFlags.Visible));

        exit.Activated += OnActivated;
        filemenu.Append(exit);

        mb.Append(file);

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(mb, false, false, 0);
        vbox.PackStart(new Label(), false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnActivated(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们的示例显示了具有三个子菜单项的顶级菜单项。 每个菜单项都有一个图像和一个加速器。 退出菜单项的加速器处于活动状态。

AccelGroup agr = new AccelGroup();
AddAccelGroup(agr);

要使用加速器,我们创建一个全局AccelGroup对象。 稍后将使用。

ImageMenuItem newi = new ImageMenuItem(Stock.New, agr);

ImageMenuItem已创建。 图片来自图片库。

exit.AddAccelerator("activate", agr, new AccelKey(
    Gdk.Key.q, Gdk.ModifierType.ControlMask, AccelFlags.Visible));

这将为退出菜单项创建 Ctrl + Q 加速器。

SeparatorMenuItem sep = new SeparatorMenuItem();
filemenu.Append(sep);

这些行创建一个分隔符。 它用于将菜单项分组为逻辑组。

Image menu

图:图像 menu

CheckMenuItem

CheckMenuItem是带有复选框的菜单项。 它可以用于布尔属性。

checkmenuitem.cs

using Gtk;
using System;

class SharpApp : Window {

    private Statusbar statusbar;

    public SharpApp() : base("Check menu item")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        MenuBar mb = new MenuBar();

        Menu filemenu = new Menu();
        MenuItem file = new MenuItem("File");
        file.Submenu = filemenu;

        Menu viewmenu = new Menu();
        MenuItem view = new MenuItem("View");
        view.Submenu = viewmenu;

        CheckMenuItem stat = new CheckMenuItem("View Statusbar");
        stat.Toggle();
        stat.Toggled += OnStatusView;
        viewmenu.Append(stat);

        MenuItem exit = new MenuItem("Exit");
        exit.Activated += OnActivated;
        filemenu.Append(exit);

        mb.Append(file);
        mb.Append(view);

        statusbar = new Statusbar();
        statusbar.Push(1, "Ready");

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(mb, false, false, 0);
        vbox.PackStart(new Label(), true, false, 0);
        vbox.PackStart(statusbar, false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnStatusView(object sender, EventArgs args)
    {
        CheckMenuItem item = (CheckMenuItem) sender;

        if (item.Active) {
            statusbar.Show();
        } else {
            statusbar.Hide();
        }
    }

    void OnActivated(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的代码示例中,我们显示一个检查菜单项。 如果该复选框已激活,则显示状态栏小部件。 如果不是,状态栏将被隐藏。

CheckMenuItem stat = new CheckMenuItem("View Statusbar");

CheckMenuItem小部件已创建。

stat.Toggle();

Toggle()方法选中/取消选中检查菜单项。

if (item.Active) {
    statusbar.Show();
} else {
    statusbar.Hide();
}

根据CheckMenuItem的状态,我们显示或隐藏状态栏小部件。

CheckMenuItem

图:CheckMenuItem

子菜单

我们的最后一个示例演示了如何在 GTK# 中创建一个子菜单。

submenu.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Submenu")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        MenuBar mb = new MenuBar();

        Menu filemenu = new Menu();
        MenuItem file = new MenuItem("File");
        file.Submenu = filemenu;

        // submenu creation
        Menu imenu = new Menu();

        MenuItem import = new MenuItem("Import");
        import.Submenu = imenu;

        MenuItem inews = new MenuItem("Import news feed...");
        MenuItem ibookmarks = new MenuItem("Import bookmarks...");
        MenuItem imail = new MenuItem("Import mail...");

        imenu.Append(inews);
        imenu.Append(ibookmarks);
        imenu.Append(imail);

        // exit menu item
        MenuItem exit = new MenuItem("Exit");
        exit.Activated += OnActivated;

        filemenu.Append(import);
        filemenu.Append(exit);
        mb.Append(file);

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(mb, false, false, 0);
        vbox.PackStart(new Label(), false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnActivated(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

子菜单创建。

Menu imenu = new Menu();

子菜单是Menu

MenuItem import = new MenuItem("Import");
import.Submenu = imenu;

它是菜单项的子菜单,它会登录到顶级文件菜单。

MenuItem inews = new MenuItem("Import news feed...");
MenuItem ibookmarks = new MenuItem("Import bookmarks...");
MenuItem imail = new MenuItem("Import mail...");

imenu.Append(inews);
imenu.Append(ibookmarks);
imenu.Append(imail);

子菜单有其自己的菜单项。

Submenu

图:子菜单

在 GTK# 编程库的这一章中,我们展示了如何使用菜单。

GTK# 中的工具栏

原文: http://zetcode.com/gui/gtksharp/toolbars/

在 GTK# 编程教程的这一部分中,我们将使用工具栏。

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。 工具栏是带有按钮的水平或垂直面板。 这些按钮包含图像或图像和文本。 通过单击工具栏按钮,我们执行一个动作。

简单的工具栏

接下来,我们创建一个简单的工具栏。

toolbar.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Toolbar")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        Toolbar toolbar = new Toolbar();
        toolbar.ToolbarStyle = ToolbarStyle.Icons;

        ToolButton newtb = new ToolButton(Stock.New);
        ToolButton opentb = new ToolButton(Stock.Open);
        ToolButton savetb = new ToolButton(Stock.Save);
        SeparatorToolItem sep = new SeparatorToolItem();
        ToolButton quittb = new ToolButton(Stock.Quit);

        toolbar.Insert(newtb, 0);
        toolbar.Insert(opentb, 1);
        toolbar.Insert(savetb, 2);
        toolbar.Insert(sep, 3);
        toolbar.Insert(quittb, 4);

        quittb.Clicked += OnClicked;

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(toolbar, false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnClicked(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该示例显示了一个工具栏和四个工具按钮。

Toolbar toolbar = new Toolbar();

Toolbar小部件已创建。

toolbar.ToolbarStyle = ToolbarStyle.Icons;

在工具栏上,我们仅显示图标。 没有文字。

ToolButton newtb = new ToolButton(Stock.New);

创建带有库存图像的ToolButton

SeparatorToolItem sep = new SeparatorToolItem(); 

这是一个分隔符。 它可用于将工具栏按钮分组为逻辑组。

toolbar.Insert(newtb, 0);
toolbar.Insert(opentb, 1);
...

工具栏按钮插入到工具栏小部件中。

Toolbar

图:工具栏

工具栏

在第二个示例中,我们显示了两个工具栏。 许多应用具有多个工具栏。 我们展示了如何在 GTK# 中做到这一点。

toolbars.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Toolbars")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        Toolbar upper = new Toolbar();
        upper.ToolbarStyle = ToolbarStyle.Icons;

        ToolButton newtb = new ToolButton(Stock.New);
        ToolButton opentb = new ToolButton(Stock.Open);
        ToolButton savetb = new ToolButton(Stock.Save);

        upper.Insert(newtb, 0);
        upper.Insert(opentb, 1);
        upper.Insert(savetb, 2);

        Toolbar lower = new Toolbar();
        lower.ToolbarStyle = ToolbarStyle.Icons;

        ToolButton quittb = new ToolButton(Stock.Quit);
        quittb.Clicked += OnClicked;
        lower.Insert(quittb, 0);

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(upper, false, false, 0);
        vbox.PackStart(lower, false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnClicked(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们的应用显示了两个工具栏。

Toolbar upper = new Toolbar();
...
Toolbar lower = new Toolbar();

我们创建两个Toolbar小部件。

upper.Insert(newtb, 0);
...
lower.Insert(quittb, 0);

它们每个都有自己的工具按钮。

VBox vbox = new VBox(false, 2);
vbox.PackStart(upper, false, false, 0);
vbox.PackStart(lower, false, false, 0)

工具栏一个接一个地包装在垂直盒中。

Toolbars

图:工具栏 s

撤销重做

以下示例演示了如何停用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法。 例如,保存按钮。 如果我们将文档的所有更改都保存到磁盘上,则在大多数文本编辑器中,“保存”按钮将被禁用。 这样,应用会向用户指示所有更改都已保存。

undoredo.cs

using Gtk;
using System;

class SharpApp : Window {

    private int count = 2;
    private ToolButton undo;
    private ToolButton redo;

    public SharpApp() : base("Undo redo")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        Toolbar toolbar = new Toolbar();
        toolbar.ToolbarStyle = ToolbarStyle.Icons;

        undo = new ToolButton(Stock.Undo);
        redo = new ToolButton(Stock.Redo);
        SeparatorToolItem sep = new SeparatorToolItem();
        ToolButton quit = new ToolButton(Stock.Quit);

        toolbar.Insert(undo, 0);
        toolbar.Insert(redo, 1);
        toolbar.Insert(sep, 2);
        toolbar.Insert(quit, 3);

        undo.Clicked += OnUndo;
        redo.Clicked += OnRedo;
        quit.Clicked += OnClicked;

        VBox vbox = new VBox(false, 2);
        vbox.PackStart(toolbar, false, false, 0);
        vbox.PackStart(new Label(), false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnUndo(object sender, EventArgs args)
    {
        count -= 1;

        if (count <= 0) {
            undo.Sensitive = false;
            redo.Sensitive = true;
        }
    }

    void OnRedo(object sender, EventArgs args)
    {
        count += 1;

        if (count >= 5) {
            redo.Sensitive = false;
            undo.Sensitive = true;
        }
    }

    void OnClicked(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们的示例从 GTK# 库存资源创建撤消和重做按钮。 单击几下后,每个按钮均被禁用。 按钮显示为灰色。

private int count = 2;

计数变量决定哪个按钮被激活和禁用。

undo = new ToolButton(Stock.Undo);
redo = new ToolButton(Stock.Redo);

我们有两个工具按钮。 撤消和重做工具按钮。 图片来自库存资源。

undo.Clicked += OnUndo;
redo.Clicked += OnRedo;

我们为两个工具按钮都插入了Clicked事件的方法。

if (count <= 0) {
    undo.Sensitive = false;
    redo.Sensitive = true;
}

要激活小部件,我们将其Sensitive属性设置为 true。 要使其无效,我们将其设置为false

Undo redo

图:撤销和重做

在 GTK# 编程库的这一章中,我们提到了工具栏。

GTK# 中的事件

原文: http://zetcode.com/gui/gtksharp/events/

在 GTK# 编程教程的这一部分中,我们将讨论事件。

GTK# 库是一个事件驱动的系统。 所有 GUI 应用都会对事件做出反应。 应用启动一个主循环,该循环不断检查新生成的事件。 如果没有事件,则应用将等待并且不执行任何操作。 事件主要由应用的用户生成。 但是它们也可以通过其他方式生成,例如互联网连接,窗口管理器或计时器。

简单事件示例

下一个示例显示了我们如何应对两个基本事件。

quitbutton.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base ("Button")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        Fixed fix = new Fixed();

        Button quit = new Button("Quit");
        quit.Clicked += OnClick;
        quit.SetSizeRequest(80, 35);

        fix.Put(quit, 50, 50);
        Add(fix);
        ShowAll();
    }

    void OnClick(object sender, EventArgs args)
    {
        Application.Quit();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的代码示例中,我们对两个事件作出反应:Delete事件和Clicked事件。

当我们关闭窗口时,将触发删除事件。 默认情况下,当我们单击标题栏中的关闭按钮时,应用不会退出。

DeleteEvent += delegate { Application.Quit(); };

当我们使用delegate关键字时,我们可以编写将对这个特定事件做出反应的行代码。

quit.Clicked += OnClick;

在这里,我们指定使用OnClick()方法对Clicked事件做出反应。

void OnClick(object sender, EventArgs args)
{
    Application.Quit();
}

这是OnClick()方法。 它有两个参数。 第一个参数是对象,它触发了此事件。 在我们的例子中,它是退出按钮。 第二个参数为我们提供了有关事件的各种其他信息。 事件参数始终取决于事件的类型。 每种方法的签名都可以在 GTK# 库的参考手册中找到。 http://www.go-mono.com/docs/

移动窗口

下一个示例显示了我们如何对移动窗口事件做出反应。 我们在标题栏中显示窗口左上角的当前位置。

move.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("")
    {
        SetDefaultSize(250, 150);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };
        Show();
    }

    protected override bool OnConfigureEvent(Gdk.EventConfigure args)
    {
        base.OnConfigureEvent(args);
        Title = args.X + ", " + args.Y;
        return true;
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在前面的示例中,我们已将委托或方法插入事件。 在 GTK# 中,许多事件已经具有处理器方法。 在这种情况下,我们可以覆盖该方法。 在我们的代码示例中就是这种情况。

protected override bool OnConfigureEvent(Gdk.EventConfigure args)
{
    base.OnConfigureEvent(args);
    Title = args.X + ", " + args.Y;
    return true;
}

在这里,我们将覆盖预定义的OnConfigureEvent()方法。 当我们调整大小或移动窗口小部件时,触发Configure事件。 请注意,该方法的第一行调用默认方法。 没有此行,程序将无法正常运行。 下一行将窗口的 x,y 坐标设置为窗口的标题。

Move event

图:移动事件

EnterNotifyEvent

当我们使用鼠标指针进入小部件的区域时,会发出EnterNotifyEvent

enter.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Enter")
    {
        SetDefaultSize(200, 150);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        Button button = new Button("Button");
        button.EnterNotifyEvent += OnEnter;

        Fixed fix = new Fixed();
        fix.Put(button, 20, 20);

        Add(fix);

        ShowAll();
    }

    void OnEnter(object sender, EnterNotifyEventArgs args)
    {
        Button button = (Button) sender;
        button.ModifyBg(StateType.Prelight, new Gdk.Color(220, 220, 220));
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

一旦将鼠标指针悬停在按钮小部件上,我们将更改其背景颜色。

button.EnterNotifyEvent += OnEnter;

我们将OnEnter()方法插入EnterNotifyEvent

Button button = (Button) sender;
button.ModifyBg(StateType.Prelight, new Gdk.Color(220, 220, 220));

我们获取按钮小部件并修改其背景颜色。

断开事件处理器

我们可以从事件断开处理器方法。 下一个代码示例演示了这种情况。

disconnect.cs

using Gtk;
using System;

class SharpApp : Window {

    Button button;

    public SharpApp() : base("Disconnect")
    {
        SetDefaultSize(250, 150);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        button = new Button("Button");

        CheckButton cb = new CheckButton("connect");
        cb.Toggled += OnToggled;

        Fixed fix = new Fixed();
        fix.Put(button, 30, 50);
        fix.Put(cb, 130, 50);

        Add(fix);

        ShowAll();
    }

    void OnClick(object sender, EventArgs args)
    {
        Console.WriteLine("Click");
    }

    void OnToggled(object sender, EventArgs args)
    {
        CheckButton cb = (CheckButton) sender;
        if (cb.Active) {
            button.Clicked += OnClick;
        } else {
            button.Clicked -= OnClick;
        }
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在代码示例中,我们有一个按钮和一个复选框。 当我们单击按钮并且复选框处于活动状态时,我们在控制台中显示"Click"文本。 该复选框可将处理器方法与按钮Clicked事件连接或断开连接。

CheckButton cb = new CheckButton("connect");
cb.Toggled += OnToggled;

我们有一个复选框。 该小部件具有Toggled事件。 我们将OnToggled()方法插入此事件。

CheckButton cb = (CheckButton) sender;
if (cb.Active) {
    button.Clicked += OnClick;
} else {
    button.Clicked -= OnClick;
}

这些行根据复选框小部件的状态连接或断开事件处理器。

Disconnect

图:断开连接

本章是关于 GTK# 中的事件的。

GTK# 中的小部件

原文: http://zetcode.com/gui/gtksharp/widgets/

在 GTK# 编程教程的这一部分中,我们将介绍一些 GTK# 小部件。

小部件是 GUI 应用的基本构建块。 多年来,几个小部件已成为所有 OS 平台上所有工具包中的标准。 例如,按钮,复选框或滚动条。 GTK# 工具箱的理念是将小部件的数量保持在最低水平。 将创建更多专门的小部件作为自定义 GTK# 小部件。

Label

Label小部件显示文本。

label.cs

using Gtk;

class SharpApp : Window {

   string 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 sniff 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 men 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";

    public SharpApp() : base("You know I'm No Good")
    {
        BorderWidth = 8;
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        Label lyrics = new Label(text);
        Add(lyrics);

        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该代码示例在窗口上显示了一些歌词。

    string text = @"Meet you downstairs in the bar and heard
your rolled up sleeves and your skull t-shirt
...

在 C# 编程语言中,多行字符串以@字符开头。

BorderWidth = 8;

Label周围有一些空白。

Label lyrics = new Label(text);
Add(lyrics);

Label小部件已创建并添加到窗口。

Label Widget

图:Label小部件

CheckButton

CheckButton是具有两种状态的窗口小部件:打开和关闭。 开状态通过复选标记显示。 它用来表示一些布尔属性。

checkbutton.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("CheckButton")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);

        DeleteEvent += delegate { Application.Quit(); };

        CheckButton cb = new CheckButton("Show title");
        cb.Active = true;
        cb.Toggled += OnToggle;

        Fixed fix = new Fixed();
        fix.Put(cb, 50, 50);

        Add(fix);
        ShowAll();
    }

    void OnToggle(object sender, EventArgs args) 
    {
        CheckButton cb = (CheckButton) sender;

        if (cb.Active) {
            Title = "CheckButton";
        } else {
            Title = " ";
        }
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

根据CheckButton的状态,我们将在窗口的标题栏中显示标题。

CheckButton cb = new CheckButton("Show title");

CheckButton小部件已创建。

cb.Active = true;

默认情况下标题是可见的,因此我们默认情况下选中复选按钮。

CheckButton cb = (CheckButton) sender;

在这里,我们将发送方对象转换为CheckButton类。

if (cb.Active) {
    Title = "CheckButton";
} else {
    Title = " ";
}

根据CheckButtonActive属性,我们显示或隐藏窗口的标题。

CheckButton

图:CheckButton

ComboBox

ComboBox是一个小部件,允许用户从选项列表中进行选择。

combobox.cs

using Gtk;
using System;

class SharpApp : Window {

    Label label;

    public SharpApp() : base("ComboBox")
    {
       string[] distros = new string[] {"Ubuntu",
            "Mandriva",
            "Red Hat",
            "Fedora",
            "Gentoo" };

        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        BorderWidth = 7;
        DeleteEvent += delegate { Application.Quit(); };

        Fixed fix = new Fixed();

        ComboBox cb = new ComboBox(distros);
        cb.Changed += OnChanged;
        label = new Label("-");

        fix.Put(cb, 50, 30);
        fix.Put(label, 50, 140);
        Add(fix);

        ShowAll();
    }

    void OnChanged(object sender, EventArgs args)
    {
        ComboBox cb = (ComboBox) sender;
        label.Text = cb.ActiveText;
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该示例显示了一个组合框和一个标签。 组合框具有六个选项的列表。 这些是 Linux 发行版的名称。 标签窗口小部件显示了从组合框中选择的选项。

string[] distros = new string[] {"Ubuntu",
    "Mandriva",
    "Red Hat",
    "Fedora",
    "Gentoo" };

这是一个字符串数组,将显示在ComboBox小部件中。

ComboBox cb = new ComboBox(distros);

ComboBox小部件已创建。 构造器将字符串数组作为参数。

void OnChanged(object sender, EventArgs args)
{
    ComboBox cb = (ComboBox) sender;
    label.Text = cb.ActiveText;
}

OnChanged()方法内部,我们从组合框中获取选定的文本并将其设置为标签。

ComboBox

图:ComboBox

Image

下一个示例介绍Image小部件。 此小部件显示图片。

image.cs

using Gtk;
using System;

class SharpApp : Window {

    Gdk.Pixbuf castle;

    public SharpApp() : base("Red Rock")
    {
        BorderWidth = 1;
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        try {
            castle = new Gdk.Pixbuf("redrock.png");
        } catch {
            Console.WriteLine("Image not found");
            Environment.Exit(1);
        }

        Image image = new Image(castle);
        Add(image);

        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们在窗口中显示红色岩石城堡。

try {
    castle = new Gdk.Pixbuf("redrock.png");
} catch {
    Console.WriteLine("Image not found");
    Environment.Exit(1);
}

我们创建Gdk.Pixbuf小部件。 我们将构造器放在trycatch关键字之间,以处理可能的错误。

Image image = new Image(castle);
Add(image);

Image小部件已创建并添加到窗口。

Image

图:图像

在本章中,我们展示了 GTK# 编程库的第一组基本小部件。

GTK 中的小部件 II

原文: http://zetcode.com/gui/gtksharp/widgetsII/

在 GTK# 编程教程的这一部分中,我们继续介绍 GTK# 小部件。

我们将介绍Entry小部件,Scale小部件,ToggleButtonCalendar小部件。

Entry

Entry是单行文本输入字段。 该小部件用于输入文本数据。

entry.cs

using Gtk;
using System;

class SharpApp : Window {

    Label label;

    public SharpApp() : base("Entry")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        BorderWidth = 7;
        DeleteEvent += delegate { Application.Quit(); };

        label = new Label("...");

        Entry entry = new Entry();
        entry.Changed += OnChanged;

        Fixed fix = new Fixed();
        fix.Put(entry, 60, 100);
        fix.Put(label, 60, 40);

        Add(fix);

        ShowAll();
    }

    void OnChanged(object sender, EventArgs args)
    {
        Entry entry = (Entry) sender;
        label.Text = entry.Text;
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

此示例显示了条目小部件和标签。 我们输入的文本将立即显示在标签控件中。

Entry entry = new Entry();

Entry小部件已创建。

entry.Changed += OnChanged;

如果Entry小部件中的文本被更改,我们将调用OnChanged()方法。

void OnChanged(object sender, EventArgs args)
{
    Entry entry = (Entry) sender;
    label.Text = entry.Text;
}

我们从Entry小部件获取文本并将其设置为标签。

Entry Widget

图:Entry小部件

HScale

Scale是一个小部件,可让用户通过在有限间隔内滑动旋钮以图形方式选择一个值。 我们的示例将显示音量控制。

hscale.cs

using Gtk;
using System;

class SharpApp : Window {

    Gdk.Pixbuf mute, min, med, max;
    Image image;

    public SharpApp() : base("Scale")
    {
        SetDefaultSize(260, 150);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        HScale scale = new HScale(0, 100, 1);
        scale.SetSizeRequest(160, 35);
        scale.ValueChanged += OnChanged;

        LoadPixbufs();

        image = new Image(mute);

        Fixed fix = new Fixed();
        fix.Put(scale, 20, 40);
        fix.Put(image, 219, 50);

        Add(fix);

        ShowAll();
    }

    void LoadPixbufs() 
    {
        try {
            mute = new Gdk.Pixbuf("mute.png");
            min = new Gdk.Pixbuf("min.png");
            med = new Gdk.Pixbuf("med.png");
            max = new Gdk.Pixbuf("max.png");
        } catch {
            Console.WriteLine("Error reading Pixbufs");
            Environment.Exit(1);
        }
    }

    void OnChanged(object obj, EventArgs args)
    {
        HScale scale = (HScale) obj;
        double val = scale.Value;

        if (val == 0) {
            image.Pixbuf = mute;
        } else if (val > 0 && val < 30) {
            image.Pixbuf = min;
        } else if (val > 30 && val < 80) {
            image.Pixbuf = med;
        } else {
            image.Pixbuf = max;
        }
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在上面的示例中,我们有HScaleImage小部件。 通过拖动比例尺,我们可以在Image小部件上更改图像。

HScale scale = new HScale(0, 100, 1);

HScale小部件已创建。 参数是下边界,上边界和阶跃。

HScale scale = (HScale) obj;
double val = scale.Value;

OnChange()方法中,我们获得了比例小部件的值。

if (val == 0) {
    image.Pixbuf = mute;
} else if (val > 0 && val <= 30) {
    image.Pixbuf = min;
} else if (val > 30 && val < 80) {
    image.Pixbuf = med;
} else {
image.Pixbuf = max;
}

根据获得的值,我们在图像小部件中更改图片。

HScale Widget

图:HScale小部件

ToggleButton

ToggleButton是具有两种状态的按钮:已按下和未按下。 通过单击可以在这两种状态之间切换。 在某些情况下此功能非常合适。

togglebuttons.cs

using Gtk;
using System;

class SharpApp : Window {

    DrawingArea darea;
    Gdk.Color col;

    public SharpApp() : base("ToggleButtons")
    {
        col = new Gdk.Color(0, 0, 0);

        SetDefaultSize(350, 240);
        SetPosition(WindowPosition.Center);
        BorderWidth = 7;
        DeleteEvent += delegate { Application.Quit(); };

        ToggleButton red = new ToggleButton("Red");
        red.SetSizeRequest(80, 35);
        red.Clicked += OnRed;

        ToggleButton green = new ToggleButton("Green");
        green.SetSizeRequest(80, 35);
        green.Clicked += OnGreen;

        ToggleButton blue = new ToggleButton("Blue");
        blue.SetSizeRequest(80, 35);
        blue.Clicked += OnBlue;

        darea = new DrawingArea();
        darea.SetSizeRequest(150, 150);
        darea.ModifyBg(StateType.Normal, col);

        Fixed fix = new Fixed();
        fix.Put(red, 30, 30);
        fix.Put(green, 30, 80);
        fix.Put(blue, 30, 130);
        fix.Put(darea, 150, 30);

        Add(fix);

        ShowAll();
    }

    void OnRed(object sender, EventArgs args) 
    {
        ToggleButton tb = (ToggleButton) sender;

        if (tb.Active) {
            col.Red = 65535; 
        } else {
            col.Red = 0;
        }

        darea.ModifyBg(StateType.Normal, col);         
    }

    void OnGreen(object sender, EventArgs args) 
    {
        ToggleButton tb = (ToggleButton) sender;

        if (tb.Active) {
            col.Green = 65535; 
        } else {
            col.Green = 0;
        }

        darea.ModifyBg(StateType.Normal, col);
    }

    void OnBlue(object sender, EventArgs args) 
    {
        ToggleButton tb = (ToggleButton) sender;

        if (tb.Active) {
            col.Blue = 65535; 
        } else {
            col.Blue = 0;
        }

        darea.ModifyBg(StateType.Normal, col);
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的示例中,我们显示了三个切换按钮和一个DrawingArea。 我们将区域的背景色设置为黑色。 切换按钮将切换颜色值的红色,绿色和蓝色部分。 背景颜色取决于我们按下的切换按钮。

col = new Gdk.Color(0, 0, 0);

这是将使用切换按钮更新的颜色值。

ToggleButton red = new ToggleButton("Red");
red.SetSizeRequest(80, 35);
red.Clicked += OnRed;

ToggleButton小部件已创建。 我们将其大小设置为80x35像素。 每个切换按钮都有其自己的处理器方法。

darea = new DrawingArea();
darea.SetSizeRequest(150, 150);
darea.ModifyBg(StateType.Normal, col);

DrawingArea小部件是显示颜色的小部件,由切换按钮混合。 开始时,它显示为黑色。

if (tb.Active) {
    col.Red = 65535; 
} else {
    col.Red = 0;
}

我们根据Active属性的值更新颜色的红色部分。

darea.ModifyBg(StateType.Normal, col);

我们更新DrawingArea小部件的颜色。

ToggleButton widget

图:ToggleButton widget

Calendar

我们最终的窗口小部件是Calendar小部件。 它用于处理日期。

calendar.cs

using Gtk;
using System;

class SharpApp : Window {

    private Label label;

    public SharpApp() : base("Calendar")
    {
        SetDefaultSize(300, 270);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        label = new Label("...");

        Calendar calendar = new Calendar();
        calendar.DaySelected += OnDaySelected;

        Fixed fix = new Fixed();
        fix.Put(calendar, 20, 20);
        fix.Put(label, 40, 230);

        Add(fix);

        ShowAll();
    }

    void OnDaySelected(object sender, EventArgs args)
    {
        Calendar cal = (Calendar) sender;
        label.Text = cal.Month + 1 + "/" + cal.Day + "/" + cal.Year;
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们有Calendar小部件和Label。 从日历中选择的日期显示在标签中。

Calendar calendar = new Calendar();

Calendar小部件已创建。

Calendar cal = (Calendar) sender;
label.Text = cal.Month + 1 + "/" + cal.Day + "/" + cal.Year;

OnDaySelected()方法中,我们将引荐给Calendar小部件,并将标签更新为当前选择的日期。

Calendar

图:日历

在本章中,我们结束了有关 GTK# 小部件的讨论。

GTK# 中的高级小部件

原文: http://zetcode.com/gui/gtksharp/advancedwidgets/

在 GTK# 编程教程的这一部分中,我们将介绍 GTK# 中的一些更高级的小部件。 我们将介绍IconView,ListViewTreeView小部件。

IconView

IconView是一个小部件,在网格中显示图标列表。

iconview.cs

using System;
using System.IO;
using Gtk;

public class SharpApp : Window
{
    const int COL_PATH = 0;
    const int COL_DISPLAY_NAME = 1;
    const int COL_PIXBUF = 2;
    const int COL_IS_DIRECTORY = 3;

    DirectoryInfo root = new DirectoryInfo("/");
    Gdk.Pixbuf dirIcon, fileIcon;
    ListStore store;
    ToolButton upButton;

    public SharpApp() : base("IconView")
    {
        SetDefaultSize(650, 400);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        VBox vbox = new VBox(false, 0);
        Add(vbox);

        Toolbar toolbar = new Toolbar();
        vbox.PackStart(toolbar, false, false, 0);

        upButton = new ToolButton(Stock.GoUp);
        upButton.IsImportant = true;
        upButton.Sensitive = false;
        toolbar.Insert(upButton, -1);

        ToolButton homeButton = new ToolButton(Stock.Home);
        homeButton.IsImportant = true;
        toolbar.Insert(homeButton, -1);

        fileIcon = GetIcon(Stock.File);
        dirIcon = GetIcon(Stock.Open);

        ScrolledWindow sw = new ScrolledWindow();
        sw.ShadowType = ShadowType.EtchedIn;
        sw.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);
        vbox.PackStart(sw, true, true, 0);

        store = CreateStore();
        FillStore();

        IconView iconView = new IconView(store);
        iconView.SelectionMode = SelectionMode.Multiple;

        upButton.Clicked += new EventHandler(OnUpClicked);
        homeButton.Clicked += new EventHandler(OnHomeClicked);

        iconView.TextColumn = COL_DISPLAY_NAME;
        iconView.PixbufColumn = COL_PIXBUF;

        iconView.ItemActivated += OnItemActivated;
        sw.Add(iconView);
        iconView.GrabFocus();

        ShowAll();
    }

    Gdk.Pixbuf GetIcon(string name)
    {
        return Gtk.IconTheme.Default.LoadIcon(name, 48, (IconLookupFlags) 0);
    }

    ListStore CreateStore()
    {
        ListStore store = new ListStore(typeof (string), 
            typeof(string), typeo (Gdk.Pixbuf), typeof(bool));

        store.SetSortColumnId(COL_DISPLAY_NAME, SortType.Ascending);

        return store;
    }

    void FillStore()
    {
        store.Clear();

        if (!root.Exists)
            return;

        foreach (DirectoryInfo di in root.GetDirectories())
        {
            if (!di.Name.StartsWith("."))
                store.AppendValues(di.FullName, di.Name, dirIcon, true);
        }

        foreach (FileInfo file in root.GetFiles())
        {
            if (!file.Name.StartsWith("."))
                store.AppendValues(file.FullName, file.Name, fileIcon, false);
        }
    }

    void OnHomeClicked(object sender, EventArgs a)
    {
        root = new DirectoryInfo(Environment.GetFolderPath(
            Environment.SpecialFolder.Personal));
        FillStore();
        upButton.Sensitive = true;
    }

    void OnItemActivated(object sender, ItemActivatedArgs a)
    {
        TreeIter iter;
        store.GetIter(out iter, a.Path);
        string path = (string) store.GetValue(iter, COL_PATH);
        bool isDir = (bool) store.GetValue(iter, COL_IS_DIRECTORY);

        if (!isDir)
            return;

        root = new DirectoryInfo(path);
        FillStore();

        upButton.Sensitive = true;
    }

    void OnUpClicked(object sender, EventArgs a)
    {
        root = root.Parent;
        FillStore();
        upButton.Sensitive = (root.FullName == "/" ? false : true);
    }

    public static void Main()
    {   
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

本示例显示当前所选目录的图标。 它有一个工具栏和两个按钮。 向上按钮和主页按钮。 我们使用它们来浏览文件系统。

ListStore store;

ListStore是一个列列表数据结构。 我们使用它来存储IconView小部件的数据。

DirectoryInfo root = new DirectoryInfo("/");

这是根目录。 我们从这里开始。 根目录是我们在IconView小部件中显示的目录。

ListStore store = new ListStore(typeof(string), 
    typeof(string), typeof(Gdk.Pixbuf), typeof(bool));

CreateStore()方法中,我们创建ListStore对象。 它有四个参数。 路径,目录或文件的名称,图像以及确定它是目录还是文件的布尔值。

foreach (DirectoryInfo di in root.GetDirectories())
{
    if (!di.Name.StartsWith("."))
        store.AppendValues(di.FullName, di.Name, dirIcon, true);
}

FillStore()方法中,我们用数据填充列表存储。 在这里,我们找出当前路径中的所有目录。

void OnHomeClicked(object sender, EventArgs a)
{
    root = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
    FillStore();
    upButton.Sensitive = true;
}

如果单击主页按钮,则将获得对主目录的引用。 重新填充列表存储。 并激活向上按钮。

void OnItemActivated(object sender, ItemActivatedArgs a)
{ 
...
}

OnItemActivated()方法中,当我们从图标视图小部件中单击一个图标时,我们会对生成的事件做出反应。

string path = (string) store.GetValue(iter, COL_PATH);
bool isDir = (bool) store.GetValue(iter, COL_IS_DIRECTORY);

if (!isDir)
    return;

我们得到激活项目的路径。 然后我们确定它是目录还是文件。 如果是文件,我们返回。

root = new DirectoryInfo(path);
FillStore();

upButton.Sensitive = true;

如果是目录,我们将根目录替换为当前路径,重新填充商店,然后使向上按钮敏感。

void OnUpClicked(object sender, EventArgs a)
{
    root = root.Parent;
    FillStore();
    upButton.Sensitive = (root.FullName == "/" ? false : true);
}

如果单击向上按钮,则将根目录替换为其父目录。 重新填充列表存储。 如果我们位于文件系统的根(/)目录下,则向上按钮将被激活。

IconView

图:图标 View

ListView

在下面的示例中,我们使用TreeView小部件显示列表视图。 同样ListStore用于存储数据。

listview.cs

using System;
using System.Collections;
using Gtk;

public class Actress
{
    public string Name;
    public string Place;
    public int Year;

    public Actress(string name, string place, int year)
    {
        Name = name;
        Place = place;
        Year = year;
    }
}

public class SharpApp : Window
{
    ListStore store;
    Statusbar statusbar;

    enum Column
    {
        Name,
        Place,
        Year
    }

    Actress[] actresses =
    {
        new Actress("Jessica Alba", "Pomona", 1981),
        new Actress("Sigourney Weaver", "New York", 1949),
        new Actress("Angelina Jolie", "Los Angeles", 1975),
        new Actress("Natalie Portman", "Jerusalem", 1981),
        new Actress("Rachel Weissz", "London", 1971),
        new Actress("Scarlett Johansson", "New York", 1984) 
    };

    public SharpApp() : base ("ListView")
    {
        BorderWidth = 8;

        SetDefaultSize(350, 250);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        VBox vbox = new VBox(false, 8);

        ScrolledWindow sw = new ScrolledWindow();
        sw.ShadowType = ShadowType.EtchedIn;
        sw.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);
        vbox.PackStart(sw, true, true, 0);

        store = CreateModel();

        TreeView treeView = new TreeView(store);
        treeView.RulesHint = true;
        treeView.RowActivated += OnRowActivated;
        sw.Add(treeView);

        AddColumns(treeView);

        statusbar = new Statusbar();

        vbox.PackStart(statusbar, false, false, 0);

        Add(vbox);
        ShowAll();
    }

    void OnRowActivated (object sender, RowActivatedArgs args) {

        TreeIter iter;        
        TreeView view = (TreeView) sender;   

        if (view.Model.GetIter(out iter, args.Path)) {
            string row = (string) view.Model.GetValue(iter, (int) Column.Name );
            row += ", " + (string) view.Model.GetValue(iter, (int) Column.Place );
            row += ", " + view.Model.GetValue(iter, (int) Column.Year );
            statusbar.Push(0, row);
        }
    }

    void AddColumns(TreeView treeView)
    {
        CellRendererText rendererText = new CellRendererText();
        TreeViewColumn column = new TreeViewColumn("Name", rendererText,
            "text", Column.Name);
        column.SortColumnId = (int) Column.Name;
        treeView.AppendColumn(column);

        rendererText = new CellRendererText();
        column = new TreeViewColumn("Place", rendererText, 
            "text", Column.Place);
        column.SortColumnId = (int) Column.Place;
        treeView.AppendColumn(column);

        rendererText = new CellRendererText();
        column = new TreeViewColumn("Year", rendererText, 
            "text", Column.Year);
        column.SortColumnId = (int) Column.Year;
        treeView.AppendColumn(column);
    }

    ListStore CreateModel()
    {
        ListStore store = new ListStore( typeof(string),
            typeof(string), typeof(int) );

        foreach (Actress act in actresses) {
            store.AppendValues(act.Name, act.Place, act.Year );
        }

        return store;
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
} 

在我们的示例中,我们在TreeView小部件中显示了六个女演员的列表。 每行分别显示名称,出生地和出生年份。

public class Actress
{
    public string Name;
    public string Place;
    public int Year;
...
}

Actress类用于存储有关女演员的数据。

ListStore CreateModel()
{
    ListStore store = new ListStore( typeof(string), 
        typeof(string), typeof(int) );

    foreach (Actress act in actresses) {
        store.AppendValues(act.Name, act.Place, act.Year );
    }

    return store;
}

CreateModel()方法中,我们创建列表存储。 列表存储具有三个参数。 女演员的名字,出生地和出生年份。 这是我们的TreeView小部件的数据模型。

TreeView treeView = new TreeView(store);
treeView.RulesHint = true;
treeView.RowActivated += OnRowActivated;

在这里,我们以列表存储为参数创建TreeView小部件。

CellRendererText rendererText = new CellRendererText();
TreeViewColumn column = new TreeViewColumn("Name", rendererText, "text", Column.Name);
column.SortColumnId = (int) Column.Name;
treeView.AppendColumn(column);

AddColumns()方法中,我们向TreeView小部件中添加了三列。 上面的代码创建了一个显示女演员姓名的列。

if (view.Model.GetIter(out iter, args.Path)) {
    string row = (string) view.Model.GetValue(iter, (int) Column.Name );
    row += ", " + (string) view.Model.GetValue(iter, (int) Column.Place );
    row += ", " + view.Model.GetValue(iter, (int) Column.Year );
    statusbar.Push(0, row);
}

如果双击某个项目,则会在状态栏中显示整行。

ListView

图:ListView

TreeView

在本章的最后一个示例中,我们使用TreeView小部件显示分层的数据树。

tree.cs

using Gtk;

public class SharpApp : Window
{

    public SharpApp() : base ("Tree")
    {
        SetDefaultSize(400, 300);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        TreeView tree = new TreeView();

        TreeViewColumn languages = new TreeViewColumn();
        languages.Title = "Programming languages";

        CellRendererText cell = new CellRendererText();
        languages.PackStart(cell, true);
        languages.AddAttribute(cell, "text", 0);

        TreeStore treestore = new TreeStore(typeof(string), typeof(string));

        TreeIter iter = treestore.AppendValues("Scripting languages");
        treestore.AppendValues(iter, "Python");
        treestore.AppendValues(iter, "PHP");
        treestore.AppendValues(iter, "Perl");
        treestore.AppendValues(iter, "Ruby");

        iter = treestore.AppendValues("Compiling languages");
        treestore.AppendValues(iter, "C#");
        treestore.AppendValues(iter, "C++");
        treestore.AppendValues(iter, "C");
        treestore.AppendValues(iter, "Java");

        tree.AppendColumn(languages);
        tree.Model = treestore;

        Add(tree);
        ShowAll();
    }

    public static void Main()
    {
        Gtk.Application.Init();
        new SharpApp();
        Gtk.Application.Run();
    }
}

这次我们使用TreeView小部件显示分层数据。

TreeView tree = new TreeView();

TreeView小部件已创建。

TreeViewColumn languages = new TreeViewColumn();
languages.Title = "Programming languages";

它有一列名为"Programming languages"

CellRendererText cell = new CellRendererText();
languages.PackStart(cell, true);
languages.AddAttribute(cell, "text", 0);

我们在TreeView小部件中显示文本数据。

TreeStore treestore = new TreeStore(typeof(string), typeof(string));

要存储数据,我们使用TreeStore对象。

TreeIter iter = treestore.AppendValues("Scripting languages");
treestore.AppendValues(iter, "Python");
treestore.AppendValues(iter, "PHP");

我们将数据附加到树上。 TreeIter用于连续访问数据。

tree.AppendColumn(languages);

一列被附加到树上。

tree.Model = treestore;

最后,我们为树小部件设置数据模型。

Tree

图:Tree

在本章中,我们讨论的是高级 GTK# 小部件。

GTK# 中的对话框

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

在 GTK# 编程教程的这一部分中,我们将介绍对话框。

对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。

MessageDialog

消息对话框是方便的对话框,可向应用的用户提供消息。 该消息包含文本和图像数据。

messages.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Messages")
    {
        SetDefaultSize(250, 100);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); }; 

        Table table = new Table(2, 2, true);

        Button info = new Button("Information");
        Button warn = new Button("Warning");
        Button ques = new Button("Question");
        Button erro = new Button("Error");

        info.Clicked += delegate {
            MessageDialog md = new MessageDialog(this, 
                DialogFlags.DestroyWithParent, MessageType.Info, 
                ButtonsType.Close, "Download completed");
            md.Run();
            md.Destroy();
        };

        warn.Clicked += delegate {
            MessageDialog md = new MessageDialog(this, 
                DialogFlags.DestroyWithParent, MessageType.Warning, 
                ButtonsType.Close, "Unallowed operation");
            md.Run();
            md.Destroy();
        };

        ques.Clicked += delegate {
            MessageDialog md = new MessageDialog(this, 
                DialogFlags.DestroyWithParent, MessageType.Question, 
                ButtonsType.Close, "Are you sure to quit?");
            md.Run();
            md.Destroy();
        };

        erro.Clicked += delegate {
            MessageDialog md = new MessageDialog (this, 
                DialogFlags.DestroyWithParent, MessageType.Error, 
                ButtonsType.Close, "Error loading file");
            md.Run();
            md.Destroy();
        };

        table.Attach(info, 0, 1, 0, 1);
        table.Attach(warn, 1, 2, 0, 1);
        table.Attach(ques, 0, 1, 1, 2);
        table.Attach(erro, 1, 2, 1, 2);

        Add(table);

        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的示例中,我们将显示四种消息对话框。 信息,警告,问题和错误消息对话框。

Button info = new Button("Information");
Button warn = new Button("Warning");
Button ques = new Button("Question");
Button erro = new Button("Error");

我们有四个按钮。 这些按钮中的每个按钮都会显示不同类型的消息对话框。

info.Clicked += delegate {
    MessageDialog md = new MessageDialog(this, 
        DialogFlags.DestroyWithParent, MessageType.Info, 
        ButtonsType.Close, "Download completed");
    md.Run();
    md.Destroy();
};

如果单击信息按钮,将显示“信息”对话框。 MessageType.Info指定对话框的类型。 ButtonsType.Close指定要在对话框中显示的按钮。 最后一个参数是显示的消息。 该对话框使用Run()方法显示。 程序员还必须调用Destroy()Hide()方法。

Information message dialog

Warning message dialog

Question message dialog

Error message dialog

AboutDialog

AboutDialog显示有关应用的信息。 AboutDialog可以显示徽标,应用名称,版本,版权,网站或许可证信息。 也有可能对作者,文档撰写者,翻译者和艺术家予以赞扬。

aboutdialog.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("About")
    {
        SetDefaultSize(300, 270);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); } ;

        Button button = new Button("About");
        button.Clicked += OnClicked;

        Fixed fix = new Fixed();
        fix.Put(button, 20, 20);
        Add(fix);

        ShowAll();
    }

    void OnClicked(object sender, EventArgs args)
    {
        AboutDialog about = new AboutDialog();
        about.ProgramName = "Battery";
        about.Version = "0.1";
        about.Copyright = "(c) Jan Bodnar";
        about.Comments = @"Battery is a simple tool for 
battery checking";
        about.Website = "http://www.zetcode.com";
        about.Logo = new Gdk.Pixbuf("battery.png");
        about.Run();
        about.Destroy();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该代码示例使用具有某些功能的AboutDialog

AboutDialog about = new AboutDialog();

我们创建一个AboutDialog

about.ProgramName = "Battery";
about.Version = "0.1";
about.Copyright = "(c) Jan Bodnar";

通过设置对话框的属性,我们指定名称,版本和版权。

about.Logo = new Gdk.Pixbuf("battery.png");

此行创建徽标。

AboutDialog

图:AboutDialog

FontSelectionDialog

FontSelectionDialog是用于选择字体的对话框。 它通常用于进行一些文本编辑或格式化的应用中。

fontdialog.cs

using Gtk;
using System;

class SharpApp : Window {

    Label label;

    public SharpApp() : base("Font Selection Dialog")
    {
        SetDefaultSize(300, 220);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); } ;

        label = new Label("The only victory over love is flight.");
        Button button = new Button("Select font");
        button.Clicked += OnClicked;

        Fixed fix = new Fixed();
        fix.Put(button, 100, 30);
        fix.Put(label, 30, 90);
        Add(fix);

        ShowAll();
    }

    void OnClicked(object sender, EventArgs args)
    {
        FontSelectionDialog fdia = new FontSelectionDialog("Select font name");
        fdia.Response += delegate (object o, ResponseArgs resp) {

            if (resp.ResponseId == ResponseType.Ok) {
               Pango.FontDescription fontdesc = 
                   Pango.FontDescription.FromString(fdia.FontName);
               label.ModifyFont(fontdesc);
            }
        };

        fdia.Run();
        fdia.Destroy();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在代码示例中,我们有一个按钮和一个标签。 单击按钮显示FontSelectionDialog

FontSelectionDialog fdia = new FontSelectionDialog("Select font name");

我们创建了FontSelectionDialog.

fdia.Response += delegate (object o, ResponseArgs resp) {

    if (resp.ResponseId == ResponseType.Ok) {
        Pango.FontDescription fontdesc = Pango.FontDescription.FromString(fdia.FontName);
        label.ModifyFont(fontdesc);
    }
};

如果单击“确定”按钮,则标签小部件的字体将更改为我们在对话框中选择的字体。

FontSelectionDialog

图:FontSelectionDialog

ColorSelectionDialog

ColorSelectionDialog是用于选择颜色的对话框。

colordialog.cs

using Gtk;
using System;

class SharpApp : Window {

    Label label;

    public SharpApp() : base("Color Dialog")
    {
        SetDefaultSize(300, 220);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); } ;

        label = new Label("The only victory over love is flight.");
        Button button = new Button("Select color");
        button.Clicked += OnClicked;

        Fixed fix = new Fixed();
        fix.Put(button, 100, 30);
        fix.Put(label, 30, 90);
        Add(fix);

        ShowAll();
    }

    void OnClicked(object sender, EventArgs args)
    {
        ColorSelectionDialog cdia = new ColorSelectionDialog("Select color");
        cdia.Response += delegate (object o, ResponseArgs resp) {

            if (resp.ResponseId == ResponseType.Ok) {
               label.ModifyFg(StateType.Normal, cdia.ColorSelection.CurrentColor);
            }
        };

        cdia.Run();
        cdia.Destroy();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该示例与上一个示例非常相似。 这次我们更改标签的颜色。

ColorSelectionDialog cdia = new ColorSelectionDialog("Select color");

我们创建ColorSelectionDialog

cdia.Response += delegate (object o, ResponseArgs resp) {

    if (resp.ResponseId == ResponseType.Ok) {
    label.ModifyFg(StateType.Normal, cdia.ColorSelection.CurrentColor);
    }
};

如果用户按下 OK,我们将获得颜色并修改标签的颜色。

ColorSelectionDialog

图:颜色 electionDialog

在 GTK# 教程的这一部分中,我们讨论了对话框。

Pango

原文: http://zetcode.com/gui/gtksharp/pango/

在 GTK# 编程教程的这一部分中,我们将探索 Pango 库。

Pango 是一个免费的开源计算库,可高质量呈现国际化文本。 可以使用不同的字体后端,从而允许跨平台支持。 (维基百科)

Pango 提供了用于 Gdk 和 Gtk 的高级字体和文本处理。

简单的例子

在第一个示例中,我们展示了如何更改标签小部件的字体。

quotes.cs

using Gtk;
using System;

class SharpApp : Window {

    private Label label;

    public SharpApp() : base("Pango")
    {
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        string text = @"Excess of joy is harder to bear than any amount of sorrow.
The more one judges, the less one loves.
There is no such thing as a great talent without great will power. ";

        label = new Label(text);

        Pango.FontDescription fontdesc = Pango.FontDescription.FromString("Purisa 10");
        label.ModifyFont(fontdesc);

        Fixed fix = new Fixed();

        fix.Put(label, 5, 5);
        Add(fix);
        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在上面的代码示例中,我们有一个带有三个引号的标签小部件。 我们将其字体更改为 Purisa 10。

string text = @"Excess of joy is harder to bear than any amount of sorrow.
...

这是要在标签中显示的文本。

Pango.FontDescription fontdesc = Pango.FontDescription.FromString("Purisa 10");

FontDescription用于指定要加载的字体的特征。 FromString()方法从字符串表示形式创建新的字体描述。

label.ModifyFont(fontdesc);

我们将标签小部件的字体更改为 Purisa 10。

Quotations

图:Quotations

系统字体

下一个代码示例显示TreeView小部件中的所有可用字体。

systemfonts.cs

using System;
using Pango;
using Gtk;

public class SharpApp : Window
{
    ListStore store;
    FontFamily[] fam;

    public SharpApp() : base("System fonts")
    {
        BorderWidth = 8;

        SetDefaultSize(350, 250);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        ScrolledWindow sw = new ScrolledWindow();
        sw.ShadowType = ShadowType.EtchedIn;
        sw.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);

        Context context = this.CreatePangoContext();
        fam = context.Families;

        store = CreateModel();

        TreeView treeView = new TreeView(store);
        treeView.RulesHint = true;
        sw.Add(treeView);

        CreateColumn(treeView);

        Add(sw);
        ShowAll();
    }

    void CreateColumn(TreeView treeView)
    {
        CellRendererText rendererText = new CellRendererText();
        TreeViewColumn column = new TreeViewColumn("FontName",
            rendererText, "text", Column.FontName);
        column.SortColumnId = (int) Column.FontName;
        treeView.AppendColumn(column);
    }

    ListStore CreateModel()
    {
        ListStore store = new ListStore( typeof(string) );

        foreach (FontFamily ff in fam) {
            store.AppendValues(ff.Name);
        }

        return store;
    }

    enum Column
    {
        FontName
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该代码示例显示了系统上所有可用的字体。

Context context = this.CreatePangoContext();

此代码行创建一个Pango.Context对象。 它包含有关文本渲染过程的全局信息。

fam = context.Families;

从上下文对象中,我们检索所有可用的字体系列。

foreach (FontFamily ff in fam) {
    store.AppendValues(ff.Name);
}

TreeView小部件的模型创建期间,我们从字体家族数组中获取所有字体名称,并将它们放入列表存储中。

System fonts

图:系统字体

Unicode

Pango 用于处理国际化文本。

unicode.cs

using Gtk;
using System;

class SharpApp : Window {

    public SharpApp() : base("Unicode")
    {
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        string text = @"Фёдор Михайлович Достоевский родился 30 октября (11 ноября)
1821 года в Москве.Был вторым из 7 детей. Отец, Михаил Андреевич, работал в 
госпитале для бедных. Мать, Мария Фёдоровна (в девичестве Нечаева),
происходила из купеческого рода.";

        Label label = new Label(text);

        Pango.FontDescription fontdesc = Pango.FontDescription.FromString("Purisa 10");
        label.ModifyFont(fontdesc);

        Fixed fix = new Fixed();

        fix.Put(label, 5, 5);
        Add(fix);
        ShowAll();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们在西里尔字母中显示一些文本。

string text = @"Фёдор Михайлович Достоевский родился 30 октября ...

我们可以直接使用 unicode 文本。

Label label = new Label(text);

我们通常在标签小部件中使用它。

Unicode

图:Unicode

彩色文字

在最后一个示例中,我们将进一步探索 Pango 功能。 我们将在DrawingArea小部件上绘制居中的彩色文本。

coloured.cs

using System;
using Gtk;
using Pango;

public class SharpApp : Window
{

    public SharpApp () : base ("Australia")
    {
        SetDefaultSize(250, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        Gdk.Color white = new Gdk.Color(255, 255, 255);

        DrawingArea drawingArea = new DrawingArea();
        drawingArea.ModifyBg(StateType.Normal, white);
        drawingArea.ExposeEvent += OnExposeEvent;

        Add(drawingArea);

        ShowAll();
    }

    void OnExposeEvent (object sender, ExposeEventArgs a)
    {
        DrawingArea drawingArea = sender as DrawingArea;

        int width = drawingArea.Allocation.Width;

        Gdk.PangoRenderer renderer = Gdk.PangoRenderer.GetDefault(drawingArea.Screen);
        renderer.Drawable = drawingArea.GdkWindow;
        renderer.Gc = drawingArea.Style.BlackGC;

        Context context = drawingArea.CreatePangoContext();
        Pango.Layout layout = new Pango.Layout(context);

        layout.Width = Pango.Units.FromPixels(width);
        layout.SetText("Australia");

        FontDescription desc = FontDescription.FromString("Serif Bold 20");
        layout.FontDescription = desc;

        renderer.SetOverrideColor(RenderPart.Foreground, new Gdk.Color(200, 30, 30));
        layout.Alignment = Pango.Alignment.Center;
        renderer.DrawLayout(layout, 0, 0);

        renderer.SetOverrideColor(RenderPart.Foreground, Gdk.Color.Zero);
        renderer.Drawable = null;
        renderer.Gc = null;
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们绘制水平居中的"Australia"文本,颜色为深红色。

Gdk.PangoRenderer renderer = Gdk.PangoRenderer.GetDefault(drawingArea.Screen);
renderer.Drawable = drawingArea.GdkWindow;
renderer.Gc = drawingArea.Style.BlackGC;

我们获得了屏幕的默认渲染器,并将其设置为绘制。

Context context = drawingArea.CreatePangoContext();
Pango.Layout layout = new Pango.Layout(context);

我们创建一个Pango.Layout。 它是用于布置整个文本块的高级驱动程序。

layout.Width = Pango.Units.FromPixels(width);

我们指定布局的宽度。

layout.SetText("Australia");

我们设置文本。

FontDescription desc = FontDescription.FromString("Serif Bold 20");
layout.FontDescription = desc;

我们为布局指定字体。

renderer.SetOverrideColor(RenderPart.Foreground, new Gdk.Color(200, 30, 30));
layout.Alignment = Pango.Alignment.Center;

我们设置颜色和对齐方式。

renderer.DrawLayout(layout, 0, 0);

我们绘制 Pango 布局。

renderer.SetOverrideColor(RenderPart.Foreground, Gdk.Color.Zero);
renderer.Drawable = null;
renderer.Gc = null;

我们清理资源。

Australia

图:澳大利亚

在 GTK# 编程库的这一章中,我们使用了 Pango 库。

PyQt5 中的事件和信号

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

在 PyQt5 编程教程的这一部分中,我们将探讨应用中发生的事件和信号。

事件

GUI 应用是事件驱动的。 事件主要由应用的用户生成。 但是它们也可以通过其他方式生成。 例如互联网连接,窗口管理器或计时器。 当我们调用应用的exec_()方法时,应用进入主循环。 主循环获取事件并将其发送到对象。

在事件模型中,有三个参与者:

  • 事件来源
  • 事件对象
  • 事件目标

事件源是状态更改的对象。 它生成事件。事件对象(事件)将状态更改封装在事件源中。事件目标是要通知的对象。 事件源对象将处理事件的任务委托给事件目标。

PyQt5 具有独特的信号和槽机制来处理事件。 信号和槽用于对象之间的通信。 当发生特定事件时,会发出信号。槽可以是任何 Python 可调用的。 发出连接信号时,将调用槽。

信号和槽

这是一个简单的示例,展示了 PyQt5 中的信号和槽。

sigslot.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we connect a signal
of a QSlider to a slot of a QLCDNumber. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: January 2017
"""

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, 
    QVBoxLayout, QApplication)

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(sld)

        self.setLayout(vbox)
        sld.valueChanged.connect(lcd.display)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal and slot')
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们显示QtGui.QLCDNumberQtGui.QSlider。 我们通过拖动滑块来更改lcd编号。

sld.valueChanged.connect(lcd.display)

在这里,我们将滑块的valueChanged信号连接到lcd号的display槽。

发送器是发送信号的对象。接收器是接收信号的对象。槽是对信号做出反应的方法。

Signal & slot

图:信号和槽

重新实现事件处理器

PyQt5 中的事件通常通过重新实现事件处理器来处理。

escape.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we reimplement an 
event handler. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Event handler')
        self.show()

    def keyPressEvent(self, e):

        if e.key() == Qt.Key_Escape:
            self.close()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们重新实现了keyPressEvent()事件处理器。

def keyPressEvent(self, e):

    if e.key() == Qt.Key_Escape:
        self.close()

如果单击“退出”按钮,则应用终止。

事件对象

事件对象是一个 Python 对象,其中包含许多描述事件的属性。 事件对象特定于生成的事件类型。

eventobject.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we display the x and y 
coordinates of a mouse pointer in a label widget.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QLabel

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        grid = QGridLayout()

        x = 0
        y = 0

        self.text = "x: {0},  y: {1}".format(x, y)

        self.label = QLabel(self.text, self)
        grid.addWidget(self.label, 0, 0, Qt.AlignTop)

        self.setMouseTracking(True)

        self.setLayout(grid)

        self.setGeometry(300, 300, 350, 200)
        self.setWindowTitle('Event object')
        self.show()

    def mouseMoveEvent(self, e):

        x = e.x()
        y = e.y()

        text = "x: {0},  y: {1}".format(x, y)
        self.label.setText(text)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在此示例中,我们在标签小部件中显示了鼠标指针的 x 和 y 坐标。

self.text = "x: {0},  y: {1}".format(x, y)

self.label = QLabel(self.text, self)

x 和 y 坐标显示在QLabel小部件中。

self.setMouseTracking(True)

默认情况下,鼠标跟踪是禁用的,因此,仅在移动鼠标时按下至少一个鼠标按钮时,窗口小部件才会接收鼠标移动事件。 如果启用了鼠标跟踪,则即使未按任何按钮,窗口小部件也会接收鼠标移动事件。

def mouseMoveEvent(self, e):

    x = e.x()
    y = e.y()

    text = "x: {0},  y: {1}".format(x, y)
    self.label.setText(text)

e是事件对象; 它包含有关已触发事件的数据; 在我们的例子中,是一个鼠标移动事件。 使用x()y()方法,我们可以确定鼠标指针的 x 和 y 坐标。 我们构建字符串并将其设置为标签小部件。

Event object

图:事件对象

事件发送者

有时很方便地知道哪个窗口小部件是信号的发送者。 为此,PyQt5 具有sender()方法。

eventsource.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we determine the event sender
object.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        btn1 = QPushButton("Button 1", self)
        btn1.move(30, 50)

        btn2 = QPushButton("Button 2", self)
        btn2.move(150, 50)

        btn1.clicked.connect(self.buttonClicked)            
        btn2.clicked.connect(self.buttonClicked)

        self.statusBar()

        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Event sender')
        self.show()

    def buttonClicked(self):

        sender = self.sender()
        self.statusBar().showMessage(sender.text() + ' was pressed')

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

我们的示例中有两个按钮。 在buttonClicked()方法中,我们通过调用sender()方法来确定单击了哪个按钮。

btn1.clicked.connect(self.buttonClicked)            
btn2.clicked.connect(self.buttonClicked)

两个按钮都连接到同一槽。

def buttonClicked(self):

    sender = self.sender()
    self.statusBar().showMessage(sender.text() + ' was pressed')

我们通过调用sender()方法来确定信号源。 在应用的状态栏中,我们显示了被按下的按钮的标签。

Event sender

图:事件发送者

发射信号

QObject创建的对象可以发出信号。 以下示例说明了如何发出自定义信号。

customsignal.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we show how to 
emit a custom signal. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QMainWindow, QApplication

class Communicate(QObject):

    closeApp = pyqtSignal() 

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        self.c = Communicate()
        self.c.closeApp.connect(self.close)       

        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Emit signal')
        self.show()

    def mousePressEvent(self, event):

        self.c.closeApp.emit()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

我们创建一个名为closeApp的新信号。 在鼠标按下事件期间发出此信号。 信号连接到QMainWindowclose()槽。

class Communicate(QObject):

    closeApp = pyqtSignal()     

使用pyqtSignal()作为外部Communicate类的类属性创建信号。

self.c = Communicate()
self.c.closeApp.connect(self.close) 

定制的closeApp信号连接到QMainWindowclose()槽。

def mousePressEvent(self, event):

    self.c.closeApp.emit()

当我们用鼠标指针单击窗口时,会发出closeApp信号。 该应用终止。

在 PyQt5 教程的这一部分中,我们介绍了信号和槽。

GTK# 中的 Cario 绘图

原文: http://zetcode.com/gui/gtksharp/drawing/

在 GTK# 编程教程的这一部分中,我们将使用 Cairo 库进行一些绘制。

Cairo 是用于创建 2D 矢量图形的库。 我们可以使用它来绘制自己的小部件,图表或各种效果或动画。

简单绘图

描边操作绘制形状的轮廓,填充操作填充形状的内部。 接下来,我们将演示这两个操作。

simpledrawing.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Simple drawing")
    {
        SetDefaultSize(230, 150);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };;

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        cr.LineWidth = 9;
        cr.SetSourceRGB(0.7, 0.2, 0.0);

        int width, height;
        width = Allocation.Width;
        height = Allocation.Height;

        cr.Translate(width/2, height/2);
        cr.Arc(0, 0, (width < height ? width : height) / 2 - 10, 0, 2*Math.PI);
        cr.StrokePreserve();

        cr.SetSourceRGB(0.3, 0.4, 0.6);
        cr.Fill();

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的示例中,我们将绘制一个圆并将其用纯色绘制。

gmcs -pkg:gtk-sharp-2.0 -r:/usr/lib/mono/2.0/Mono.Cairo.dll  simple.cs

这是我们编译示例的方式。

DrawingArea darea = new DrawingArea();

我们将在DrawingArea小部件上进行绘制操作。

darea.ExposeEvent += OnExpose;

所有绘图都是通过我们插入ExposeEvent的方法完成的。

DrawingArea area = (DrawingArea) sender;
Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

我们从绘图区域的GdkWindow创建Cairo.Context对象。 上下文是用于在所有Drawable对象上绘制的对象。

cr.LineWidth = 9;

我们将线条的宽度设置为 9 像素。

cr.SetSourceRGB(0.7, 0.2, 0.0);

我们将颜色设置为深红色。

int width, height;
width = Allocation.Width;
height = Allocation.Height;

cr.Translate(width/2, height/2);

我们得到绘图区域的宽度和高度。 我们将原点移动到窗口的中间。

cr.Arc(0, 0, (width < height ? width : height) / 2 - 10, 0, 2*Math.PI);
cr.StrokePreserve();

我们绘制一个圆形的外部形状。 StrokePreserve()根据当前的线宽,线连接,线帽和笔划线设置描边当前路径。 与Stroke()不同,它在 cairo 上下文中保留路径。

cr.SetSourceRGB(0.3, 0.4, 0.6);
cr.Fill();

这会用一些蓝色填充圆圈的内部。

Simple drawing

图:简单 drawing

基本形状

下一个示例将一些基本形状绘制到窗口上。

basicshapes.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Basic shapes")
    {
        SetDefaultSize(390, 240);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);
        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cc =  Gdk.CairoHelper.Create(area.GdkWindow);

        cc.SetSourceRGB(0.2, 0.23, 0.9);
        cc.LineWidth = 1;

        cc.Rectangle(20, 20, 120, 80);
        cc.Rectangle(180, 20, 80, 80);
        cc.StrokePreserve();
        cc.SetSourceRGB(1, 1, 1);
        cc.Fill();

        cc.SetSourceRGB(0.2, 0.23, 0.9);
        cc.Arc(330, 60, 40, 0, 2*Math.PI);
        cc.StrokePreserve();
        cc.SetSourceRGB(1, 1, 1);
        cc.Fill();

        cc.SetSourceRGB(0.2, 0.23, 0.9);
        cc.Arc(90, 160, 40, Math.PI/4, Math.PI);
        cc.ClosePath();
        cc.StrokePreserve();
        cc.SetSourceRGB(1, 1, 1);
        cc.Fill();

        cc.SetSourceRGB(0.2, 0.23, 0.9);
        cc.Translate(220, 180);
        cc.Scale(1, 0.7);        
        cc.Arc(0, 0, 50, 0, 2*Math.PI);
        cc.StrokePreserve();
        cc.SetSourceRGB(1, 1, 1);
        cc.Fill();          

        ((IDisposable) cc.Target).Dispose ();                                      
        ((IDisposable) cc).Dispose ();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在此示例中,我们将创建一个矩形,一个正方形,一个圆形,一个弧形和一个椭圆形。 我们用蓝色绘制轮廓,内部用白色绘制。

cc.Rectangle(20, 20, 120, 80);
cc.Rectangle(180, 20, 80, 80);
cc.StrokePreserve();
cc.SetSourceRGB(1, 1, 1);
cc.Fill();

这些线绘制一个矩形和一个正方形。

cc.Arc(330, 60, 40, 0, 2*Math.PI);

此处Arc()方法绘制一个完整的圆。

cc.Scale(1, 0.7);        
cc.Arc(0, 0, 50, 0, 2*Math.PI);

如果要绘制椭圆形,请先进行一些缩放。 在这里Scale()方法缩小 y 轴。

Basic shapes

图:基本形状

色彩

颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 Cario 有效 RGB 值在 0 到 1 的范围内。

colors.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Colors")
    {
        SetDefaultSize(360, 100);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        cr.SetSourceRGB(0.2, 0.23, 0.9);
        cr.Rectangle(10, 15, 90, 60);
        cr.Fill();

        cr.SetSourceRGB(0.9, 0.1, 0.1);
        cr.Rectangle(130, 15, 90, 60);
        cr.Fill();

        cr.SetSourceRGB(0.4, 0.9, 0.4);
        cr.Rectangle(250, 15, 90, 60);
        cr.Fill();

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们用三种不同的颜色绘制三个矩形。

cr.SetSourceRGB(0.2, 0.23, 0.9);

SetSourceRGB()方法为 Cario 上下文设置颜色。 该方法的三个参数是颜色强度值。

cr.Rectangle(10, 15, 90, 60);
cr.Fill();

我们创建一个矩形形状,并用先前指定的颜色填充它。

Colors

图:颜色

透明矩形

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

在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)

transparentrectangles.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Transparent rectangles")
    {
        SetDefaultSize(590, 90);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); } ;

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        for ( int i = 1; i <= 10; i++) {
            cr.SetSourceRGBA(0, 0, 1, i*0.1);
            cr.Rectangle(50*i, 20, 40, 40);
            cr.Fill();  
        }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在示例中,我们将绘制十个具有不同透明度级别的矩形。

cr.SetSourceRGBA(0, 0, 1, i*0.1);

SetSourceRGBA()方法的最后一个参数是 alpha 透明度。

Transparent rectangles

图:透明矩形

灵魂伴侣

在下一个示例中,我们在窗口上绘制一些文本。

soulmate.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Soulmate")
    {
        SetDefaultSize(420, 250);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        cr.SetSourceRGB(0.1, 0.1, 0.1);

        cr.SelectFontFace("Purisa", FontSlant.Normal, FontWeight.Bold);
        cr.SetFontSize(13);

        cr.MoveTo(20, 30);
        cr.ShowText("Most relationships seem so transitory");
        cr.MoveTo(20, 60);
        cr.ShowText("They're all good but not the permanent one");
        cr.MoveTo(20, 120);
        cr.ShowText("Who doesn't long for someone to hold");
        cr.MoveTo(20, 150);
        cr.ShowText("Who knows how to love without being told");
        cr.MoveTo(20, 180);
        cr.ShowText("Somebody tell me why I'm on my own");
        cr.MoveTo(20, 210);
        cr.ShowText("If there's a soulmate for everyone");

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们显示 Natasha Bedingfields Soulmate 歌曲的部分歌词。

cr.SelectFontFace("Purisa", FontSlant.Normal, FontWeight.Bold);

在这里,我们指定使用的字体-粗体的 Purisa。

cr.SetFontSize(13);

我们指定字体的大小。

cr.MoveTo(20, 30);

我们移动到要绘制文本的位置。

cr.ShowText("Most relationships seem so transitory");

ShowText()方法将文本绘制到窗口上。

Soulmate

图:灵魂伴侣

在 GTK# 编程库的这一章中,我们使用 Cario 库进行绘制。

GTK# 中的 Cario 绘图 II

原文: http://zetcode.com/gui/gtksharp/drawingII/

在 GTK# 编程教程的这一部分中,我们将继续使用 Cairo 库进行绘制。

甜甜圈

在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。

donut.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Donut")
    {
        SetDefaultSize(350, 250);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        cr.LineWidth = 0.5;

        int width, height;
        width = Allocation.Width;
        height = Allocation.Height;

        cr.Translate(width/2, height/2);
        cr.Arc(0, 0, 120, 0, 2*Math.PI);
        cr.Stroke();

        cr.Save();

        for (int i = 0; i < 36; i++) {
            cr.Rotate( i*Math.PI/36);
            cr.Scale(0.3, 1);
            cr.Arc(0, 0, 120, 0, 2*Math.PI);
            cr.Restore();
            cr.Stroke();
            cr.Save();
        }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此称为甜甜圈。

cr.Translate(width/2, height/2);
cr.Arc(0, 0, 120, 0, 2*Math.PI);
cr.Stroke();

刚开始时有一个椭圆。

for (int i = 0; i < 36; i++) {
    cr.Rotate( i*Math.PI/36);
    cr.Scale(0.3, 1);
    cr.Arc(0, 0, 120, 0, 2*Math.PI);
    cr.Restore();
    cr.Stroke();
    cr.Save();
}

旋转几圈后,有一个甜甜圈。

Donut

图:多纳圈

渐变

在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers.com)

gradients.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    public SharpApp() : base("Gradients")
    {
        SetDefaultSize(340, 390);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        LinearGradient lg1 = new LinearGradient(0.0, 0.0, 350.0, 350.0);

        int count = 1;

        for (double j=0.1; j<1.0; j+= 0.1) {
            if (Convert.ToBoolean(count % 2)) {
                lg1.AddColorStop(j, new Color(0, 0, 0, 1));
            } else {
                lg1.AddColorStop(j, new Color(1, 0, 0, 1));
            }
        count++;
        }

        cr.Rectangle(20, 20, 300, 100);
        cr.Pattern = lg1;
        cr.Fill();

        LinearGradient lg2 = new LinearGradient(0.0, 0.0, 350.0, 0);

        count = 1;

        for (double i=0.05; i<0.95; i+= 0.025) {
            if (Convert.ToBoolean(count % 2)) {
                lg2.AddColorStop(i, new Color(0, 0, 0, 1));
            } else {
                lg2.AddColorStop(i, new Color(0, 0, 1, 1));
            }
        count++;
        }

        cr.Rectangle(20, 140, 300, 100);
        cr.Pattern = lg2;
        cr.Fill();

        LinearGradient lg3 = new LinearGradient(20.0, 260.0,  20.0, 360.0);
        lg3.AddColorStop(0.1, new Color (0, 0, 0, 1) );
        lg3.AddColorStop(0.5, new Color (1, 1, 0, 1) );
        lg3.AddColorStop(0.9, new Color (0, 0, 0, 1) );

        cr.Rectangle(20, 260, 300, 100);
        cr.Pattern = lg3;
        cr.Fill();

        lg1.Destroy();
        lg2.Destroy();
        lg3.Destroy();        

        ((IDisposable) cr.Target).Dispose ();                                      
        ((IDisposable) cr).Dispose ();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

在我们的示例中,我们绘制了三个具有三个不同渐变的矩形。

LinearGradient lg1 = new LinearGradient(0.0, 0.0, 350.0, 350.0);

在这里,我们创建一个线性渐变图案。 参数指定直线,沿着该直线绘制渐变。 在我们的情况下,这是一条垂直线。


LinearGradient lg3 = new LinearGradient(20.0, 260.0,  20.0, 360.0);
lg3.AddColorStop(0.1, new Color (0, 0, 0, 1) );
lg3.AddColorStop(0.5, new Color (1, 1, 0, 1) );
lg3.AddColorStop(0.9, new Color (0, 0, 0, 1) );

我们定义色标以产生渐变图案。 在这种情况下,渐变是黑色和黄色的混合。 通过添加两个黑色和一个黄色色标,我们创建了一个水平渐变图案。 这些停止实际上是什么意思? 在我们的情况下,我们从黑色开始,该颜色将以大小的 1/10 停止。 然后,我们开始逐渐涂成黄色,最终达到形状的中心。 黄色停在大小的 9/10,我们再次开始用黑色绘图,直到结束。

Gradients

图:渐变

泡泡

在以下示例中,我们创建一个粉扑效果。 该示例将显示一个不断增长的居中文本,该文本将从某个点逐渐淡出。 这是一个非常常见的效果,您经常可以在 Flash 动画中看到它。

puff.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    private bool timer = true;
    private double alpha = 1.0;
    private double size = 1.0;
    private DrawingArea darea;

    public SharpApp() : base("Puff")
    {
        SetDefaultSize(350, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        GLib.Timeout.Add(14, new GLib.TimeoutHandler(OnTimer));

        darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    bool OnTimer() 
    { 
        if (!timer) return false;

        darea.QueueDraw();
        return true;
    }      

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        int x = Allocation.Width / 2;
        int y = Allocation.Height / 2;

        cr.SetSourceRGB(0.5, 0, 0);
        cr.Paint();

        cr.SelectFontFace("Courier", FontSlant.Normal, FontWeight.Bold);

        size += 0.8;

        if (size > 20) {
            alpha -= 0.01;
        }

        cr.SetFontSize(size);
        cr.SetSourceRGB(1, 1, 1); 

        TextExtents extents = cr.TextExtents("ZetCode");

        cr.MoveTo(x - extents.Width/2, y);
        cr.TextPath("ZetCode");
        cr.Clip();
        cr.Stroke();
        cr.PaintWithAlpha(alpha);

        if (alpha <= 0) {
            timer = false;
        }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该示例在窗口上创建一个逐渐增长和褪色的文本。

GLib.Timeout.Add(14, new GLib.TimeoutHandler(OnTimer));

每隔 14 毫秒调用一次OnTimer()方法。

bool OnTimer() 
{ 
    if (!timer) return false;

    darea.QueueDraw();
    return true;
}      

OnTimer()方法中,我们在绘图区域上调用QueueDraw()方法,该方法会触发ExposeEvent

int x = Allocation.Width / 2;
int y = Allocation.Height / 2;

中间点的坐标。

cr.SetSourceRGB(0.5, 0, 0);
cr.Paint();

我们将背景色设置为深红色。

size += 0.8;

每个周期,字体大小将增加 0.8 个单位。

if (size > 20) {
    alpha -= 0.01;
}

字体大小大于 20 后开始淡出。

TextExtents extents = cr.TextExtents("ZetCode");

我们得到了文本指标。

cr.MoveTo(x - extents.Width/2, y);

我们使用文本指标将文本放在窗口的中心。

cr.TextPath("ZetCode");
cr.Clip();

我们获取文本的路径,并为其设置当前的片段区域。

cr.Stroke();
cr.PaintWithAlpha(alpha);

我们绘制当前路径并考虑 alpha 值。

Puff

图:粉扑

反射

在下一个示例中,我们显示反射图像。 这种美丽的效果使人产生幻觉,好像图像在水中被反射一样。

reflection.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    private ImageSurface surface;
    private int imageWidth;
    private int imageHeight;
    private int gap;
    private int border;

    public SharpApp() : base("Reflection")
    {

        try {
            surface = new ImageSurface("slanec.png");
        } catch {
            Console.WriteLine("File not found");
            Environment.Exit(1);
        } 

        imageWidth = surface.Width;
        imageHeight = surface.Height;
        gap = 40;
        border = 20;

        SetDefaultSize(300, 350);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        DrawingArea darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        int width = Allocation.Width;
        int height = Allocation.Height;

        LinearGradient lg = new LinearGradient(width/2, 0, width/2, height*3);
        lg.AddColorStop(0, new Color(0, 0, 0, 1));
        lg.AddColorStop(height, new Color(0.2, 0.2, 0.2, 1));

        cr.Pattern = lg;
        cr.Paint();

        cr.SetSourceSurface(surface, border, border);
        cr.Paint();

        double alpha = 0.7;
        double step = 1.0 / imageHeight;

        cr.Translate(0, 2 * imageHeight + gap);
        cr.Scale(1, -1);

        int i = 0;

        while(i < imageHeight) {
            cr.Rectangle(new Rectangle(border, imageHeight-i, imageWidth, 1));

            i++;

            cr.Clip();
            cr.SetSource(surface, border, border);

            cr.PaintWithAlpha(alpha-=step);
            cr.ResetClip();
        }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

该示例显示了一个反射的城堡。

LinearGradient lg = new LinearGradient(width/2, 0, width/2, height*3);
lg.AddColorStop(0, new Color(0, 0, 0, 1));
lg.AddColorStop(height, new Color(0.2, 0.2, 0.2, 1));

cr.Pattern = lg;
cr.Paint();

背景充满了渐变的油漆。 涂料是从黑色到深灰色的平滑混合。

cr.Translate(0, 2 * imageHeight + gap);
cr.Scale(1, -1);

此代码翻转图像并将其转换为原始图像下方。 平移操作是必需的,因为缩放操作会使图像上下颠倒并向上平移图像。 要了解发生了什么,只需拍摄一张照片并将其放在桌子上即可。 现在翻转它。

cr.Rectangle(new Rectangle(border, imageHeight-i, imageWidth, 1));

i++;

cr.Clip();
cr.SetSource(surface, border, border);

cr.PaintWithAlpha(alpha-=step);
cr.ResetClip();

代码的关键部分。 我们使第二个图像透明。 但是透明度不是恒定的。 图像逐渐淡出。 这是通过GradientPaint实现的。

Reflection

图:反射

等待

在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一条线在移动的错觉。 这种效果通常用于通知用户,一项艰巨的任务正在幕后进行。 一个示例是通过互联网流式传输视频。

waiting.cs

using Gtk;
using Cairo;
using System;

class SharpApp : Window {

    private double [,] trs = new double[,] {
        { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
        { 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
        { 0.9, 1.0,  0.0,  0.15, 0.3, 0.5, 0.65, 0.8 },
        { 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5, 0.65},
        { 0.65, 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5 },
        { 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15, 0.3 },
        { 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15 },
        { 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0, }
    };

    private short count = 0;
    private DrawingArea darea;

    public SharpApp() : base("Waiting")
    {
        SetDefaultSize(250, 150);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        GLib.Timeout.Add(100, new GLib.TimeoutHandler(OnTimer));

        darea = new DrawingArea();
        darea.ExposeEvent += OnExpose;

        Add(darea);

        ShowAll();
    }

    bool OnTimer() 
    { 
        count += 1;
        darea.QueueDraw();
        return true;
    }        

    void OnExpose(object sender, ExposeEventArgs args)
    {
        DrawingArea area = (DrawingArea) sender;
        Cairo.Context cr =  Gdk.CairoHelper.Create(area.GdkWindow);

        cr.LineWidth = 3;
        cr.LineCap = LineCap.Round;

        int width, height;
        width = Allocation.Width;
        height = Allocation.Height;

        cr.Translate(width/2, height/2);

        for (int i = 0; i < 8; i++) {
            cr.SetSourceRGBA(0, 0, 0, trs[count%8, i]);
            cr.MoveTo(0.0, -10.0);
            cr.LineTo(0.0, -40.0);
            cr.Rotate(Math.PI/4);
            cr.Stroke();
        }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们用八个不同的 alpha 值绘制八条线。

GLib.Timeout.Add(100, new GLib.TimeoutHandler(OnTimer));

我们使用计时器函数来创建动画。

private double [,] trs = new double[,] {
    { 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
    ...
};

这是此演示中使用的透明度值的二维数组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。

cr.LineWidth = 3;
cr.LineCap = LineCap.Round;

我们使线条更粗一些,以便更好地显示它们。 我们用带帽的线画线。

cr.SetSourceRGBA(0, 0, 0, trs[count%8, i]);

在这里,我们定义了一条线的透明度值。

cr.MoveTo(0.0, -10.0);
cr.LineTo(0.0, -40.0);
cr.Rotate(Math.PI/4);
cr.Stroke();

这些代码行将绘制八行中的每行。

Waiting

图:等待

在 GTK# 编程库的这一章中,我们使用 Cairo 库进行了一些更高级的绘制。

GTK# 中的自定义小部件

原文: http://zetcode.com/gui/gtksharp/customwidget/

工具箱通常仅提供最常见的窗口小部件,例如按钮,文本窗口小部件,滑块等。没有工具箱可以提供所有可能的窗口小部件。

客户端程序员可以创建更多专门的小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能:程序员可以修改或增强现有的小部件,或者可以从头开始创建自定义小部件。

刻录小部件

这是我们从头开始创建的小部件的示例。 可以在各种媒体刻录应用(例如 Nero 刻录 ROM)中找到此小部件。

burning.cs

using Gtk;
using Cairo;
using System;

class Burning : DrawingArea
{

    string[] num = new string[] { "75", "150", "225", "300", 
        "375", "450", "525", "600", "675" };

    public Burning() : base()
    {
        SetSizeRequest(-1, 30);
    }

    protected override bool OnExposeEvent(Gdk.EventExpose args)
    {

        Cairo.Context cr = Gdk.CairoHelper.Create(args.Window);
        cr.LineWidth = 0.8;

        cr.SelectFontFace("Courier 10 Pitch", 
            FontSlant.Normal, FontWeight.Normal);
        cr.SetFontSize(11);

        int width = Allocation.Width;

        SharpApp parent = (SharpApp) GetAncestor (Gtk.Window.GType);        
        int cur_width = parent.CurValue;

        int step = (int) Math.Round(width / 10.0);

        int till = (int) ((width / 750.0) * cur_width);
        int full = (int) ((width / 750.0) * 700);

        if (cur_width >= 700) {

            cr.SetSourceRGB(1.0, 1.0, 0.72);
            cr.Rectangle(0, 0, full, 30);
            cr.Clip();
            cr.Paint();
            cr.ResetClip();

            cr.SetSourceRGB(1.0, 0.68, 0.68);
            cr.Rectangle(full, 0, till-full, 30);    
            cr.Clip();
            cr.Paint();
            cr.ResetClip();

        } else { 

            cr.SetSourceRGB(1.0, 1.0, 0.72);
            cr.Rectangle(0, 0, till, 30);
            cr.Clip();
            cr.Paint();
            cr.ResetClip();
       }  

       cr.SetSourceRGB(0.35, 0.31, 0.24);

       for (int i=1; i<=num.Length; i++) {

           cr.MoveTo(i*step, 0);
           cr.LineTo(i*step, 5);    
           cr.Stroke();

           TextExtents extents = cr.TextExtents(num[i-1]);
           cr.MoveTo(i*step-extents.Width/2, 15);
           cr.TextPath(num[i-1]);
           cr.Stroke();
       }

        ((IDisposable) cr.Target).Dispose();                                      
        ((IDisposable) cr).Dispose();

        return true;
    }
}

class SharpApp : Window {

    int cur_value = 0;
    Burning burning;

    public SharpApp() : base("Burning")
    {
        SetDefaultSize(350, 200);
        SetPosition(WindowPosition.Center);
        DeleteEvent += delegate { Application.Quit(); };

        VBox vbox = new VBox(false, 2);

        HScale scale = new HScale(0, 750, 1);
        scale.SetSizeRequest(160, 35);
        scale.ValueChanged += OnChanged;

        Fixed fix = new Fixed();
        fix.Put(scale, 50, 50);

        vbox.PackStart(fix);

        burning = new Burning();
        vbox.PackStart(burning, false, false, 0);

        Add(vbox);

        ShowAll();
    }

    void OnChanged(object sender, EventArgs args)
    {
        Scale scale = (Scale) sender;
        cur_value = (int) scale.Value;
        burning.QueueDraw();
    }

    public int CurValue {
        get { return cur_value; }
    }

    public static void Main()
    {
        Application.Init();
        new SharpApp();
        Application.Run();
    }
}

我们在窗口底部放置一个DrawingArea并手动绘制整个窗口小部件。 所有重要的代码都位于Burning类的OnExposeEvent()方法中。 此小部件以图形方式显示了介质的总容量和可供我们使用的可用空间。 该小部件由比例小部件控制。 自定义窗口小部件的最小值为 0,最大值为 750。如果值达到 700,则开始绘制红色。 这通常表示过度燃烧。

string[] num = new string[] { "75", "150", "225", "300", 
    "375", "450", "525", "600", "675" };

这些数字显示在刻录小部件上。 它们显示了介质的容量。

SharpApp parent = (SharpApp) GetAncestor (Gtk.Window.GType);        
int cur_width = parent.CurValue;

这两行从刻度小部件获取当前数字。 我们获得父窗口小部件,并从父窗口小部件中获得当前值。

int till = (int) ((width / 750.0) * cur_width);
int full = (int) ((width / 750.0) * 700);

till参数确定要绘制的总大小。 该值来自滑块小部件。 它占整个面积的一部分。 full参数确定我们开始用红色绘制的点。

cr.SetSourceRGB(1.0, 1.0, 0.72);
cr.Rectangle(0, 0, full, 30);
cr.Clip();
cr.Paint();
cr.ResetClip();

此代码在此处绘制了一个黄色矩形,直到介质充满为止。

TextExtents extents = cr.TextExtents(num[i-1]);
cr.MoveTo(i*step-extents.Width/2, 15);
cr.TextPath(num[i-1]);
cr.Stroke();

这里的代码在刻录小部件上绘制数字。 我们计算TextExtents来正确定位文本。

void OnChanged(object sender, EventArgs args)
{
    Scale scale = (Scale) sender;
    cur_value = (int) scale.Value;
    burning.QueueDraw();
}

我们从小部件中获取值,并将其存储在cur_value变量中以备后用。 我们重新绘制刻录的小部件。

Burning widget

图:刻录小部件

在本章中,我们在 GTK# 中创建了一个自定义窗口小部件。

Visual Basic GTK# 教程

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

这是 Visual Basic GTK# 教程。 在本教程中,您将学习使用 Visual Basic 在 GTK# 中进行 GUI 编程的基础。 本教程适合初学者和中级程序员。

目录

GTK#

GTK# 是一个库,提供 GTK+ 与 Mono .NET 语言(如 C# 或 Visual Basic)的绑定。 GTK+ 是用于创建图形用户界面的领先工具包之一。 GTK# 和 Visual Basic 都是 Mono 开发平台的一部分。

相关教程

ZetCode 展示了完整的 Visual Basic 教程。 有用于姐妹语言的 GTK# 教程 C# GTK# 教程。 其他使用 Visual Basic 的工具包的教程包括 Qyoto Visual Basic 教程Visual Basic Winforms 教程

Visual Basic GTK# 简介

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

在 Visual Basic GTK# 编程教程的这一部分中,我们将介绍 GTK# 库,并使用 Visual Basic 编程语言创建第一个程序。

本教程的目的是使您开始使用 GTK# 和 Visual Basic。 可以在此处下载本教程中使用的图像。 我使用了 Gnome 项目的探戈图标包中的一些图标。

关于

GTK# 是一个库,提供 GTK+ 与 Mono .NET 语言(如 C# 或 Visual Basic)的绑定。 GTK+ 是用于创建图形用户界面的领先工具包之一。 GTK# 和 Visual Basic 都是 Mono 开发平台的一部分。

vbnc -r:/usr/lib/mono/gtk-sharp-2.0/gtk-sharp.dll quitbutton.vb

上面的命令显示了如何编译quitbutton示例。 mono VB 编译器的-r参数加载 GTK# 程序集。 这是一个动态库。 该命令显示了 Ubuntu 系统上 dll 库的路径。

简单的例子

在第一个示例中,我们创建一个简单的窗口。 窗口在屏幕上居中。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program centers a window on 
' the screen
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Center")
        Me.SetDefaultSize(250, 150)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDeleteEvent

        Me.Show

    End Sub

    Sub OnDeleteEvent(ByVal sender as Object, _
            ByVal args as DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

本示例在屏幕中央显示一个250x150像素的窗口。

Imports Gtk

Imports关键字导入我们将在应用中使用的必需品类型。

Public Class GtkVBApp
    Inherits Window

该示例继承自Window。 窗口是顶级容器。

MyBase.New("Center")

在这里,我们称为父级的构造器。

Me.SetDefaultSize(250, 150)

我们为应用窗口设置默认大小。

Me.SetPosition(WindowPosition.Center)

这条线使窗口在屏幕上居中。

AddHandler Me.DeleteEvent, AddressOf Me.OnDeleteEvent

我们将处理器插入DeleteEvent

Me.Show

一切准备就绪后,我们在屏幕上显示窗口。

Sub OnDeleteEvent(ByVal sender as Object, _
        ByVal args as DeleteEventArgs)
    Application.Quit
End Sub

当我们单击标题栏中的关闭按钮或按 Alt + F4 时,事件被触发。 该方法永久退出该应用。

Application.Init
Dim app As New GtkVBApp
Application.Run

这三行设置了应用。

创建工具提示

第二个示例将显示一个工具提示。 工具提示是一个小的矩形窗口,它提供有关对象的简短信息。 它通常是一个 GUI 组件。 它是应用帮助系统的一部分。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program shows a tooltip
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Tooltip")
        Me.SetDefaultSize(250, 150)
        Me.SetPosition(WindowPosition.Center)

        AddHandler Me.DeleteEvent, AddressOf Me.OnDeleteEvent
        Me.TooltipText = "This is a Window"

        Me.Show

    End Sub

    Sub OnDeleteEvent(ByVal sender as Object, _
            ByVal args as DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该示例创建一个窗口。 如果将鼠标指针悬停在窗口区域上方,则会弹出一个工具提示。

Me.TooltipText = "This is a Window"

我们通过TooltipText属性设置工具提示。

Tooltip

图:工具提示

退出按钮

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

' ZetCode Mono Visual Basic GTK# 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 Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Quit button")

        Me.InitUI

        Me.SetDefaultSize(250, 150)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll 

    End Sub

    Private Sub InitUI

        Dim quitButton As New Button("Quit")
        quitButton.SetSizeRequest(80, 30)

        AddHandler quitButton.Clicked, AddressOf Me.OnQuit

        Dim fix As New Fixed
        fix.Put(quitButton, 50, 50)

        Me.Add(fix)

    End Sub

    Sub OnQuit(ByVal sender As Object, _
            ByVal args As EventArgs)
        Application.Quit
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

我们使用Button小部件。 这是一个非常常见的小部件。 它显示文本标签,图像或两者。

Me.InitUI

我们将用户界面的创建委托给InitUI方法。

Me.ShowAll 

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

Dim quitButton As New Button("Quit")

在这里,我们创建一个按钮小部件。

quitButton.SetSizeRequest(80, 30)

我们为按钮设置大小。

AddHandler quitButton.Clicked, AddressOf Me.OnQuit

我们将OnQuit方法插入按钮Clicked事件。

Dim fix As New Fixed
fix.Put(quitButton, 50, 50)

我们将退出按钮放入x = 50y = 50的固定容器中。

Sub OnQuit(ByVal sender As Object, _
        ByVal args As EventArgs)
    Application.Quit
End Sub

OnQuit方法内部,我们终止了该应用。

Quit button

图:退出按钮

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

布局管理

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

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

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

Fixed

Fixed容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 在大多数应用中,我们不使用此容器。 我们在某些专业领域使用它。 例如游戏,使用图表的专用应用,可以移动的可调整大小的组件(如电子表格应用中的图表),小型教育示例。

' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program, we lay out widgets
' using absolute positioning
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Private Dim rotunda As Gdk.Pixbuf 
    Private Dim bardejov As Gdk.Pixbuf 
    Private Dim mincol As Gdk.Pixbuf

    Public Sub New

        MyBase.New("Fixed")

        Me.InitUI

        Me.SetDefaultSize(300, 280)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll 

    End Sub

    Private Sub InitUI

        Me.ModifyBg(StateType.Normal, New Gdk.Color(40, 40, 40))

        Try 
            bardejov = New Gdk.Pixbuf("bardejov.jpg")
            rotunda = New Gdk.Pixbuf("rotunda.jpg")
            mincol = New Gdk.Pixbuf("mincol.jpg")
        Catch e As Exception
            Console.WriteLine("Cannot load images")
            Console.WriteLine(e.Message)
            Environment.Exit(1)
        End Try

        Dim image1 As New Image(bardejov)
        Dim image2 As New Image(rotunda)
        Dim image3 As New Image(mincol)

        Dim fixed As New Fixed

        fixed.Put(image1, 20, 20)
        fixed.Put(image2, 40, 160)
        fixed.Put(image3, 170, 50)

        Me.Add(fixed)

    End Sub

    Sub OnDelete(ByVal sender As Object, _ 
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在我们的示例中,我们在窗口上显示了三个小图像。 我们明确指定放置这些图像的 x,y 坐标。

vbnc -r:/usr/lib/mono/gtk-sharp-2.0/gtk-sharp.dll -r:/usr/lib/mono/gtk-sharp-2.0/gdk-sharp.dll 
      absolute.vb

在此示例中,我们还使用gdk-sharp程序集。

Me.ModifyBg(StateType.Normal, New Gdk.Color(40, 40, 40))

为了获得更好的视觉体验,我们将背景色更改为深灰色。

bardejov = New Gdk.Pixbuf("bardejov.jpg")

我们将图像从磁盘加载到Gdk.Pixbuf对象。

Dim image1 As New Image(bardejov)
Dim image2 As New Image(rotunda)
Dim image3 As New Image(mincol)

Image是用于显示图像的小部件。 它在构造器中使用Gdk.Pixbuf对象。

Dim fixed As New Fixed

我们创建Fixed容器。

fixed.Put(image1, 20, 20)

我们将第一个图像放置在x = 20y = 20坐标处。

Me.Add(fixed)

最后,我们将Fixed容器添加到窗口中。

Fixed

图:固定

Alignment

Alignment容器控制其子窗口小部件的对齐方式和大小。

' ZetCode Mono Visual Basic 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
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Buttons")

        Me.InitUI

        Me.SetDefaultSize(260, 150)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim vbox As New VBox(False, 5)
        Dim hbox As New HBox(True, 3)

        Dim valign As New Alignment(0, 1, 0, 0)
        vbox.PackStart(valign)

        Dim ok As New Button("OK")
        ok.SetSizeRequest(70, 30)
        Dim close As New Button("Close")

        hbox.Add(ok)
        hbox.Add(close)

        Dim halign As New Alignment(1, 0, 0, 0)
        halign.Add(hbox)

        vbox.PackStart(halign, False, False, 3)

        Me.Add(vbox)

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在代码示例中,我们在窗口的右下角放置了两个按钮。 为此,我们使用一个水平框和一个垂直框以及两个对齐容器。

Dim valign As New Alignment(0, 1, 0, 0)

这会将子窗口小部件置于底部。

vbox.PackStart(valign)

在这里,我们将Alignment小部件放置到垂直框中。

Dim hbox As New HBox(True, 3)
...
Dim ok As New Button("OK")
ok.SetSizeRequest(70, 30)
Dim close As New Button("Close")

hbox.Add(ok)
hbox.Add(close)

我们创建一个水平框,并在其中放置两个按钮。

Dim halign As New Alignment(1, 0, 0, 0)
halign.Add(hbox)

vbox.PackStart(halign, False, False, 3)

这将创建一个对齐容器,它将其子窗口小部件放在右侧。 我们将水平框添加到对齐容器中,然后将对齐容器包装到垂直框中。 我们必须记住,对齐容器仅包含一个子窗口小部件。 这就是为什么我们必须使用盒子。

Buttons

图:按钮

计算器骨架

Table小部件按行和列排列小部件。

' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program we create a skeleton of
' a calculator. We use the Table widget. 
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Calculator")

        Me.InitUI

        Me.SetDefaultSize(300, 250)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll 

    End Sub

    Private Sub InitUI

        Dim vbox As New VBox(False, 2)

        Dim mb As New MenuBar
        Dim filemenu As New Menu
        Dim file As MenuItem = New MenuItem("File")
        file.Submenu = filemenu
        mb.Append(file)

        vbox.PackStart(mb, False, False, 0)

        Dim table As New Table(5, 4, True)

        table.Attach(New Button("Cls"), 0, 1, 0, 1)
        table.Attach(New Button("Bck"), 1, 2, 0, 1)
        table.Attach(New Label(), 2, 3, 0, 1)
        table.Attach(New Button("Close"), 3, 4, 0, 1)

        table.Attach(New Button("7"), 0, 1, 1, 2)
        table.Attach(New Button("8"), 1, 2, 1, 2)
        table.Attach(New Button("9"), 2, 3, 1, 2)
        table.Attach(New Button("/"), 3, 4, 1, 2)

        table.Attach(New Button("4"), 0, 1, 2, 3)
        table.Attach(New Button("5"), 1, 2, 2, 3)
        table.Attach(New Button("6"), 2, 3, 2, 3)
        table.Attach(New Button("*"), 3, 4, 2, 3)

        table.Attach(New Button("1"), 0, 1, 3, 4)
        table.Attach(New Button("2"), 1, 2, 3, 4)
        table.Attach(New Button("3"), 2, 3, 3, 4)
        table.Attach(New Button("-"), 3, 4, 3, 4)

        table.Attach(New Button("0"), 0, 1, 4, 5)
        table.Attach(New Button("."), 1, 2, 4, 5)
        table.Attach(New Button("="), 2, 3, 4, 5)
        table.Attach(New Button("+"), 3, 4, 4, 5)

        vbox.PackStart(New Entry, False, False, 0)

        vbox.PackEnd(table, True, True, 0)

        Me.Add(vbox)

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

我们使用Table小部件创建一个计算器框架。

Dim table As New Table(5, 4, True)

我们创建一个具有 5 行 4 列的表小部件。 第三个参数是同质参数。 如果设置为true,则表中的所有小部件都具有相同的大小。 所有窗口小部件的大小等于表容器中最大的窗口小部件。

table.Attach(New Button("Cls"), 0, 1, 0, 1)

我们在表格容器上附加一个按钮。 到表格的左上方单元格。 前两个参数是单元格的左侧和右侧,后两个参数是单元格的顶部和左侧。

vbox.PackEnd(table, True, True, 0)

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

Calculator skeleton

图:计算机骨架

窗口

接下来,我们将创建一个更高级的示例。 我们显示一个窗口,可以在 JDeveloper IDE 中找到它。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This is a more complicated layout example.
' We use Alignment and Table widgets. 
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Windows")

        Me.InitUI

        Me.SetDefaultSize(300, 250)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll 

    End Sub

    Private Sub InitUI

        Me.BorderWidth = 15

        Dim table As New Table(8, 4, False)
        table.ColumnSpacing = 3

        Dim title As New Label("Windows")

        Dim halign As New Alignment(0, 0, 0, 0)
        halign.Add(title)

        table.Attach(halign, 0, 1, 0, 1, AttachOptions.Fill, _
            AttachOptions.Fill, 0, 0)

        Dim frame As New Frame
        table.Attach(frame, 0, 2, 1, 3, AttachOptions.Fill Or AttachOptions.Expand, _
            AttachOptions.Fill Or AttachOptions.Expand, 1, 1)

        Dim activate As New Button("Activate")
        activate.SetSizeRequest(50, 30)
        table.Attach(activate, 3, 4, 1, 2, AttachOptions.Fill, _
            AttachOptions.Shrink, 1, 1)

        Dim valign As New Alignment(0, 0, 0, 0)
        Dim close As New Button("Close")
        close.SetSizeRequest(70, 30)
        valign.Add(close)
        table.SetRowSpacing(1, 3)
        table.Attach(valign, 3, 4, 2, 3, AttachOptions.Fill, _
            AttachOptions.Fill Or AttachOptions.Expand, 1, 1)

        Dim halign2 As New Alignment(0, 1, 0, 0)
        Dim help As New Button("Help")
        help.SetSizeRequest(70, 30)
        halign2.Add(help)
        table.SetRowSpacing(3, 6)
        table.Attach(halign2, 0, 1, 4, 5, AttachOptions.Fill, _
            AttachOptions.Fill, 0, 0)

        Dim ok As New Button("OK")
        ok.SetSizeRequest(70, 30)
        table.Attach(ok, 3, 4, 4, 5, AttachOptions.Fill, _
            AttachOptions.Fill, 0, 0)

        Me.Add(table)

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该代码示例显示了如何在 GTK# 中创建类似的窗口。

Dim table As New Table(8, 4, False)
table.ColumnSpacing = 3

该示例基于Table容器。 列之间将有 3px 的间隔。

Dim title As New Label("Windows")

Dim halign As New Alignment(0, 0, 0, 0)
halign.Add(title)

table.Attach(halign, 0, 1, 0, 1, AttachOptions.Fill, _
    AttachOptions.Fill, 0, 0)

这段代码创建了一个向左对齐的标签。 标签放置在Table容器的第一行中。

Dim frame As New Frame
table.Attach(frame, 0, 2, 1, 3, AttachOptions.Fill Or AttachOptions.Expand, _
    AttachOptions.Fill Or AttachOptions.Expand, 1, 1)

框架视图小部件跨越两行两列。 我们使小部件不可编辑并隐藏光标。

Dim valign As New Alignment(0, 0, 0, 0)
Dim close As New Button("Close")
close.SetSizeRequest(70, 30)
valign.Add(close)
table.SetRowSpacing(1, 3)
table.Attach(valign, 3, 4, 2, 3, AttachOptions.Fill, _
    AttachOptions.Fill Or AttachOptions.Expand, 1, 1)

我们将关闭按钮放在框架小部件旁边,进入第四列。 (我们从零开始计数)将按钮添加到对齐小部件中,以便可以将其对齐到顶部。

Windows

图:窗口

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

小部件

原文: http://zetcode.com/gui/vbgtk/widgets/

在 Visual Basic GTK# 编程教程的这一部分中,我们将介绍一些小部件。

小部件是 GUI 应用的基本构建块。 多年来,几个小部件已成为所有 OS 平台上所有工具包中的标准。 例如,按钮,复选框或滚动条。 GTK# 工具箱的理念是将小部件的数量保持在最低水平。 将创建更多专门的小部件作为自定义 GTK# 小部件。

CheckButton

CheckButton是具有两种状态的窗口小部件:打开和关闭。 接通状态通过复选标记显示。 它用来表示一些布尔属性。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program toggles the title of the
' window with the CheckButton widget
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("CheckButton")

        Me.InitUI

        Me.SetDefaultSize(250, 150)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim cb As New CheckButton("Show title")
        cb.Active = True

        AddHandler cb.Toggled, AddressOf Me.OnToggle

        Dim fix As New Fixed
        fix.Put(cb, 50, 50)

        Me.Add(fix)

    End Sub

    Sub OnToggle(ByVal sender As Object, ByVal args As EventArgs)

        If sender.Active
            Me.Title = "CheckButton"
        Else
            Title = " "
        End If

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

根据CheckButton的状态,我们将在窗口的标题栏中显示标题。

Dim cb As New CheckButton("Show title")

CheckButton小部件已创建。

cb.Active = True

默认情况下标题是可见的,因此我们默认情况下选中复选按钮。

If sender.Active
    Me.Title = "CheckButton" 
Else
    Title = " "
End If

根据CheckButtonActive属性,我们显示或隐藏窗口的标题。

CheckButton

图:CheckButton

Label

Label小部件显示文本。

' ZetCode Mono Visual Basic GTK# tutorial

' This program shows lyrics on 
' the window in a label widget
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim text 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

        MyBase.New("You know I'm No Good")

        Me.InitUI

        Me.SetPosition(WindowPosition.Center)
        Me.BorderWidth = 10
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim lyrics As New Label(text)
        Me.Add(lyrics)

    End Sub

    Sub OnDelete(ByVal sender As Object, ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该代码示例在窗口上显示了一些歌词。

    Dim text 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打印常量,+连接字符和_行终止字符。

Me.BorderWidth = 10

Label周围有一些空白。

Dim lyrics As New Label(text)
Me.Add(lyrics)

Label小部件已创建并添加到窗口。

Label Widget

图:Label小部件

Entry

Entry是单行文本输入字段。 该小部件用于输入文本数据。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program demonstrates the 
' Entry widget. Text entered in the Entry
' widget is shown in a Label widget.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim label As Label

    Public Sub New

        MyBase.New("Entry")

        Me.InitUI

        Me.SetDefaultSize(250, 150)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim fixed As New Fixed

        label = New Label("...")
        fixed.put(label, 60, 40)

        Dim entry As New Entry
        fixed.put(entry, 60, 100)

        AddHandler entry.Changed, AddressOf Me.OnTextChanged

        Me.Add(fixed)

    End Sub

    Sub OnTextChanged(ByVal sender As Object, _
            ByVal args As EventArgs)
        label.Text = sender.Text
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

此示例显示了条目小部件和标签。 我们输入的文本将立即显示在标签小部件中。

Dim entry As New Entry

Entry小部件已创建。

AddHandler entry.Changed, AddressOf Me.OnTextChanged

如果Entry小部件中的文本被更改,我们将调用OnTextChanged方法。

Sub OnTextChanged(ByVal sender As Object, _
        ByVal args As EventArgs)
    label.Text = sender.Text
End Sub

我们从Entry小部件获取文本并将其设置为标签。

Entry Widget

图:Entry小部件

ToggleButton

ToggleButton是具有两种状态的按钮。 已按下但未按下。 通过单击可以在这两种状态之间切换。 在某些情况下此功能非常合适。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program uses toggle buttons to
' change the background color of
' a widget.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim darea As DrawingArea
    Dim color As Gdk.Color

    Public Sub New

        MyBase.New("Togggle buttons")

        Me.InitUI

        Me.SetDefaultSize(350, 240)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll 

    End Sub

    Private Sub InitUI

        color = New Gdk.Color(0, 0, 0)

        Dim redb As New ToggleButton("Red")
        redb.SetSizeRequest(80, 35)
        AddHandler redb.Toggled, AddressOf Me.OnToggled

        Dim greenb As New ToggleButton("Green")
        greenb.SetSizeRequest(80, 35)
        AddHandler greenb.Toggled, AddressOf Me.OnToggled

        Dim blueb As New ToggleButton("Blue")
        blueb.SetSizeRequest(80, 35)
        AddHandler blueb.Toggled, AddressOf Me.OnToggled

        darea = New DrawingArea
        darea.SetSizeRequest(150, 150)
        darea.ModifyBg(StateType.Normal, color)

        Dim fixed As New Fixed
        fixed.Put(redb, 30, 30)
        fixed.Put(greenb, 30, 80)
        fixed.Put(blueb, 30, 130)
        fixed.Put(darea, 150, 30)

        Me.Add(fixed)

    End Sub

    Private Sub OnToggled(ByVal sender As Object, _
            ByVal args As EventArgs)

        Dim red As Integer = color.Red
        Dim green As Integer = color.Green
        Dim blue As Integer = color.Blue

        If sender.Label.Equals("Red") 
            If sender.Active 
                color.Red = 65535
            Else 
                color.Red = 0
            End If
        End If

        If sender.Label.Equals("Green")
            If sender.Active 
                color.Green = 65535
            Else 
                color.Green = 0
            End If 
        End If

        If sender.Label.Equals("Blue") 
            If sender.Active 
                color.Blue = 65535
            Else 
                color.Blue = 0
            End If
        End If     

        darea.ModifyBg(StateType.Normal, color)

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在我们的示例中,我们显示了三个切换按钮和一个DrawingArea。 我们将区域的背景色设置为黑色。 切换按钮将切换颜色值的红色,绿色和蓝色部分。 背景颜色取决于我们按下的切换按钮。

color = New Gdk.Color(0, 0, 0)

这是将使用切换按钮更新的颜色值。

Dim redb As New ToggleButton("Red")
redb.SetSizeRequest(80, 35)
AddHandler redb.Toggled, AddressOf Me.OnToggled

ToggleButton小部件已创建。 我们将其大小设置为80x35像素。 每个切换按钮具有相同的处理器方法。

darea = New DrawingArea
darea.SetSizeRequest(150, 150)
darea.ModifyBg(StateType.Normal, color)

DrawingArea小部件是显示颜色的小部件,由切换按钮混合。 开始时,它显示为黑色。

If sender.Label.Equals("Red") 
    If sender.Active 
        color.Red = 65535
    Else 
        color.Red = 0
    End If
End If

我们根据Active属性的值更新颜色的红色部分。

darea.ModifyBg(StateType.Normal, color)

我们更新DrawingArea小部件的颜色。

ToggleButton widget

图:ToggleButton widget

ComboBox

ComboBox是一个小部件,允许用户从选项列表中进行选择。

' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program, we use the ComboBox
' widget to select an option. 
' The selected option is shown in the
' Label widget
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim lbl As Label

    Public Sub New

        MyBase.New("ComboBox")

        Me.InitUI

        Me.SetDefaultSize(350, 240)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

       Dim distros() As String = New String() { _
            "Ubuntu", _
            "Mandriva", _
            "Red Hat", _
            "Fedora", _
            "Gentoo" _
       } 

        Dim fixed As New Fixed

        Dim cb As New ComboBox(distros)
        AddHandler cb.Changed, AddressOf Me.OnChanged
        lbl = New Label("...")

        fixed.Put(cb, 50, 40)
        fixed.Put(lbl, 50, 140)

        Me.Add(fixed)

    End Sub

    Private Sub OnChanged(ByVal sender As Object, _
           ByVal args As EventArgs)
       lbl.Text = sender.ActiveText
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该示例显示了一个组合框和一个标签。 组合框具有六个选项的列表。 这些是 Linux Distros 的名称。 标签窗口小部件显示了从组合框中选择的选项。

Dim distros() As String = New String() { _
    "Ubuntu", _
    "Mandriva", _
    "Red Hat", _
    "Fedora", _
    "Gentoo" _
} 

这是一个字符串数组,将显示在ComboBox小部件中。

Dim cb As New ComboBox(distros)

ComboBox小部件已创建。 构造器将字符串数组作为参数。

Private Sub OnChanged(ByVal sender As Object, _
        ByVal args As EventArgs)
    lbl.Text = sender.ActiveText
End Sub

OnChanged方法内部,我们从组合框中获取选定的文本并将其设置为标签。

ComboBox

图:ComboBox

在本章中,我们使用 Visual Basic 语言展示了 GTK# 编程库的一些基本小部件。

菜单和工具栏

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

在 Visual Basic GTK# 编程教程的这一部分中,我们将使用菜单和工具栏。

菜单栏是 GUI 应用中最常见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,我们必须记住许多奥术命令,在这里,我们将大多数命令分组为逻辑部分。 这些公认的标准可进一步减少学习新应用的时间。

简单菜单

在第一个示例中,我们将创建一个带有一个文件菜单的菜单栏。 该菜单将只有一个菜单项。 通过选择项目,应用退出。

' ZetCode Mono Visual Basic GTK# 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 Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Simple menu")

        Me.InitUI

        Me.SetDefaultSize(250, 200)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim mb As New MenuBar

        Dim filemenu As New Menu
        Dim fileItem As New MenuItem("File")
        fileItem.Submenu = filemenu

        Dim exitItem As New MenuItem("Exit")
        AddHandler exitItem.Activated, AddressOf Me.OnActivated
        filemenu.Append(exitItem)

        mb.Append(fileItem)

        Dim vbox As New VBox(False, 2)
        vbox.PackStart(mb, False, False, 0)

        Me.Add(vbox)

    End Sub

    Sub OnActivated(ByVal sender As Object, _ 
            ByVal args As EventArgs)
        Application.Quit
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

这是一个最小的菜单栏功能示例。

Dim mb As New MenuBar

MenuBar小部件已创建。 这是菜单的容器。

Dim filemenu As New Menu
Dim fileItem As New MenuItem("File")
fileItem.Submenu = filemenu

创建顶层MenuItem

Dim exitItem As New MenuItem("Exit")
AddHandler exitItem.Activated, AddressOf Me.OnActivated
filemenu.Append(exitItem)

将创建出口MenuItem,并将其附加到文件MenuItem中。

mb.Append(fileItem)

顶级MenuItem被附加到MenuBar小部件。

Dim vbox As New VBox(False, 2)
vbox.PackStart(mb, False, False, 0)

与其他工具包不同,我们必须自己照顾菜单栏的布局管理。 我们将菜单栏放入垂直框中。

Simple menu

图:简单菜单

子菜单

我们的最后一个示例演示了如何在 GTK# 中创建一个子菜单。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program creates a submenu
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Submenu")

        Me.InitUI

        Me.SetDefaultSize(250, 200)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll    

    End Sub

    Private Sub InitUI

        Dim mb As New MenuBar

        Dim filemenu As New Menu
        Dim ifile As New MenuItem("File")
        ifile.Submenu = filemenu

        // submenu creation
        Dim imenu As New Menu

        Dim iimport As New MenuItem("Import")
        iimport.Submenu = imenu

        Dim inews As New MenuItem("Import news feed...")
        Dim ibookmarks As New MenuItem("Import bookmarks...")
        Dim imail As New MenuItem("Import mail...")

        imenu.Append(inews)
        imenu.Append(ibookmarks)
        imenu.Append(imail)

        // exit menu item
        Dim iexit As New MenuItem("Exit")
        AddHandler iexit.Activated, AddressOf Me.OnActivated

        filemenu.Append(iimport)
        filemenu.Append(iexit)
        mb.Append(ifile)

        Dim vbox As New VBox(False, 2)
        vbox.PackStart(mb, False, False, 0)

        Me.Add(vbox)

    End Sub

    Sub OnActivated(ByVal sender As Object, _
            ByVal args As EventArgs)
        Application.Quit
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

子菜单创建。

Dim imenu As New Menu

子菜单是Menu

Dim iimport As New MenuItem("Import")
iimport.Submenu = imenu

它是菜单项的子菜单,它会登录到顶级文件菜单。

Dim inews As New MenuItem("Import news feed...")
Dim ibookmarks As New MenuItem("Import bookmarks...")
Dim imail As New MenuItem("Import mail...")

imenu.Append(inews)
imenu.Append(ibookmarks)
imenu.Append(imail)

子菜单有其自己的菜单项。

Submenu

图:子菜单

图像菜单

在下一个示例中,我们将进一步探索菜单。 我们将图像和加速器添加到我们的菜单项中。 Accelerators是用于激活菜单项的键盘快捷键。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program shows image
' menu items, a shorcut and a separator
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Image menu")

        Me.InitUI

        Me.SetDefaultSize(250, 200)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim mb As New MenuBar

        Dim filemenu As New Menu
        Dim ifile As New MenuItem("File")
        ifile.Submenu = filemenu

        Dim agr As New AccelGroup
        Me.AddAccelGroup(agr)

        Dim inew As New ImageMenuItem("gtk-new", agr)
        filemenu.Append(inew)

        Dim iopen As New ImageMenuItem(Stock.Open, agr)
        filemenu.Append(iopen)

        Dim isep As New SeparatorMenuItem
        filemenu.Append(isep)

        Dim iexit As New ImageMenuItem(Stock.Quit, agr)
        AddHandler iexit.Activated, AddressOf Me.OnActivated
        filemenu.Append(iexit)

        mb.Append(ifile)

        Dim vbox As New VBox(False, 2)
        vbox.PackStart(mb, False, False, 0)

        Me.Add(vbox)

    End Sub

    Sub OnActivated(ByVal sender As Object, _
            ByVal args As EventArgs)
        Application.Quit
    End Sub

    Sub OnDelete(ByVal sender As Object, _ 
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

我们的示例显示了具有三个子菜单项的顶级菜单项。 每个菜单项都有一个图像和一个加速器。 退出菜单项的加速器处于活动状态。

Dim agr As New AccelGroup
Me.AddAccelGroup(agr)

要使用加速器,我们创建一个全局AccelGroup对象。 稍后将使用。

Dim inew As New ImageMenuItem("gtk-new", agr)
filemenu.Append(inew)

ImageMenuItem已创建。 图片来自图片库。 GTK# 中有一个错误。 Stock.New与 Visual Basic New关键字冲突。

Dim isep As New SeparatorMenuItem
filemenu.Append(isep)

这些行创建一个分隔符。 它用于将菜单项放入逻辑组。

Image menu

图:图像 menu

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。

简单的工具栏

接下来,我们创建一个简单的工具栏。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program creates a 
' toolbar
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Toolbar")

        Me.InitUI

        Me.SetDefaultSize(250, 200)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim toolbar As New Toolbar
        toolbar.ToolbarStyle = ToolbarStyle.Icons

        Dim newtb As New ToolButton("gtk-new")
        Dim opentb As New ToolButton(Stock.Open)
        Dim savetb As New ToolButton(Stock.Save)
        Dim sep As New SeparatorToolItem
        Dim quittb As New ToolButton(Stock.Quit)

        toolbar.Insert(newtb, 0)
        toolbar.Insert(opentb, 1)
        toolbar.Insert(savetb, 2)
        toolbar.Insert(sep, 3)
        toolbar.Insert(quittb, 4)

        AddHandler quittb.Clicked, AddressOf Me.OnClicked

        Dim vbox As New VBox(False, 2)
        vbox.PackStart(toolbar, False, False, 0)

        Me.Add(vbox)

    End Sub

    Sub OnClicked(ByVal sender As Object, _
            ByVal args As EventArgs)
        Application.Quit
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该示例显示了一个工具栏和四个工具按钮。

Dim toolbar As New Toolbar

Toolbar小部件已创建。

toolbar.ToolbarStyle = ToolbarStyle.Icons

在工具栏上,我们仅显示图标。 没有文字。

Dim opentb As New ToolButton(Stock.Open)

创建带有库存图像的ToolButton

Dim sep As New SeparatorToolItem 

这是一个分隔符。 它可用于将工具栏按钮放入逻辑组。

toolbar.Insert(newtb, 0)
toolbar.Insert(opentb, 1)
...

工具栏按钮插入到工具栏小部件中。

Toolbar

图:工具栏

撤销重做

以下示例演示了如何停用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法。 例如,保存按钮。 如果我们将文档的所有更改都保存到磁盘上,则在大多数文本编辑器中,“保存”按钮将被停用。 这样,应用会向用户指示所有更改都已保存。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program disables/enables
' toolbuttons on a toolbar
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Private Dim clicks As Integer = 2
    Private Dim undo As ToolButton
    Private Dim redo As ToolButton

    Public Sub New

        MyBase.New("Undo redo")

        Me.InitUI

        Me.SetDefaultSize(250, 200)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim toolbar As New Toolbar
        toolbar.ToolbarStyle = ToolbarStyle.Icons

        undo = New ToolButton(Stock.Undo)
        undo.Label = "Undo"
        redo = New ToolButton(Stock.Redo)
        redo.Label = "Redo"

        Dim sep As New SeparatorToolItem
        Dim quit As New ToolButton(Stock.Quit)

        toolbar.Insert(undo, 0)
        toolbar.Insert(redo, 1)
        toolbar.Insert(sep, 2)
        toolbar.Insert(quit, 3)

        AddHandler undo.Clicked, AddressOf Me.OnCount
        AddHandler redo.Clicked, AddressOf Me.OnCount
        AddHandler quit.Clicked, AddressOf Me.OnClicked

        Dim vbox As New VBox(False, 2)
        vbox.PackStart(toolbar, False, False, 0)

        Me.Add(vbox)

    End Sub

    Private Sub OnCount(ByVal sender As Object, _
            ByVal args As EventArgs)

        If "Undo".Equals(sender.Label)
            clicks += 1
        Else 
            clicks -= 1
        End If

        If clicks <= 0 
            undo.Sensitive = True
            redo.Sensitive = False
        End If

        If clicks >= 5 
            undo.Sensitive = False
            redo.Sensitive = True
        End If

    End Sub

    Sub OnClicked(ByVal sender As Object, _
            ByVal args As EventArgs)
        Application.Quit
    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

我们的示例从 GTK# 库存资源创建撤消和重做按钮。 单击几下后,每个按钮均被停用。 按钮显示为灰色。

Private Dim clicks As Integer = 2

clicks变量决定哪个按钮被激活或禁用。

undo = New ToolButton(Stock.Undo)
undo.Label = "Undo"
redo = New ToolButton(Stock.Redo)
redo.Label = "Redo"

我们有两个工具按钮。 撤消和重做工具按钮。 图片来自库存资源。

AddHandler undo.Clicked, AddressOf Me.OnCount
AddHandler redo.Clicked, AddressOf Me.OnCount

我们为两个工具按钮都插入了Clicked事件的方法。

If clicks <= 0 
    undo.Sensitive = True
    redo.Sensitive = False
End If

要激活小部件,我们将其Sensitive属性设置为true。 要停用它,我们将其设置为false

Undo redo

图:撤销和重做

在 Visual Basic GTK# 教程的这一章中,我们展示了如何使用菜单和工具栏。

对话框

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

在 Visual Basic GTK# 编程教程的这一部分中,我们将介绍对话框。

对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。对话框是用户与计算机程序之间进行通信的重要手段。

MessageDialog

消息对话框是方便的对话框,可向应用的用户提供消息。 该消息包含文本和图像数据。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program shows message dialogs.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Message dialogs")

        Me.InitUI

        Me.SetDefaultSize(250, 100)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll  

    End Sub

    Private Sub InitUI

        Dim table As New Table(2, 2, True)

        Dim info As New Button("Information")
        Dim warn As New Button("Warning")
        Dim ques As New Button("Question")
        Dim erro As New Button("Error")

        AddHandler info.Clicked, AddressOf Me.OnInfo
        AddHandler warn.Clicked, AddressOf Me.OnWarning
        AddHandler ques.Clicked, AddressOf Me.OnQuestion
        AddHandler erro.Clicked, AddressOf Me.OnError

        table.Attach(info, 0, 1, 0, 1)
        table.Attach(warn, 1, 2, 0, 1)
        table.Attach(ques, 0, 1, 1, 2)
        table.Attach(erro, 1, 2, 1, 2)

        Me.Add(table)

    End Sub

    Private Sub OnInfo(ByVal sender As Object, ByVal args As EventArgs)
        Dim md As MessageDialog = New MessageDialog(Me, _
            DialogFlags.DestroyWithParent, MessageType.Info, _ 
            ButtonsType.Close, "Download completed")
        md.Run
        md.Destroy
    End Sub

    Private Sub OnWarning(ByVal sender As Object, ByVal args As EventArgs)
        Dim md As MessageDialog = New MessageDialog(Me, _
            DialogFlags.DestroyWithParent, MessageType.Warning, _
            ButtonsType.Close, "Unallowed operation")
        md.Run
        md.Destroy
    End Sub

    Private Sub OnQuestion(ByVal sender As Object, ByVal args As EventArgs)
        Dim md As MessageDialog = New MessageDialog(Me, _
            DialogFlags.DestroyWithParent, MessageType.Question, _
            ButtonsType.Close, "Are you sure to quit?")
        md.Run
        md.Destroy
    End Sub

    Private Sub OnError(ByVal sender As Object, ByVal args As EventArgs)
        Dim md As MessageDialog = New MessageDialog(Me, _
            DialogFlags.DestroyWithParent, MessageType.Error, _
            ButtonsType.Close, "Error loading file")
        md.Run
        md.Destroy
    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在我们的示例中,我们将显示四种消息对话框。 信息,警告,问题和错误消息对话框。

Dim info As New Button("Information")
Dim warn As New Button("Warning")
Dim ques As New Button("Question")
Dim erro As New Button("Error")

我们有四个按钮。 这些按钮中的每个按钮都会显示不同类型的消息对话框。

Private Sub OnInfo(ByVal sender As Object, ByVal args As EventArgs)
    Dim md As MessageDialog = New MessageDialog(Me, _
        DialogFlags.DestroyWithParent, MessageType.Info, _ 
        ButtonsType.Close, "Download completed")
    md.Run
    md.Destroy
End Sub

如果单击信息按钮,将显示“信息”对话框。 MessageType.Info指定对话框的类型。 ButtonsType.Close指定要在对话框中显示的按钮。 最后一个参数是已分发的消息。 该对话框使用Run方法显示。 程序员还必须调用DestroyHide方法。

Information message dialog

Warning message dialog

Question message dialog

Error message dialog

AboutDialog

AboutDialog显示有关应用的信息。 AboutDialog可以显示徽标,应用名称,版本,版权,网站或许可证信息。 也有可能对作者,文档撰写者,翻译者和艺术家予以赞扬。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program shows the about
' dialog
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("About dialog")

        Me.InitUI

        Me.SetDefaultSize(350, 300)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim button As New Button("About")

        AddHandler button.Clicked, AddressOf Me.ShowDialog

        Dim fixed As New Fixed
        fixed.Put(button, 20, 20)
        Me.Add(fixed)

    End Sub

    Private Sub ShowDialog(ByVal sender As Object, _
            ByVal args As EventArgs)

        Dim about As New AboutDialog
        about.ProgramName = "Battery"
        about.Version = "0.1"
        about.Copyright = "(c) Jan Bodnar"
        about.Comments = "Battery is a simple tool for battery checking"
        about.Website = "http://www.zetcode.com"
        about.Logo = New Gdk.Pixbuf("battery.png")
        about.Run
        about.Destroy

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该代码示例使用具有某些功能的AboutDialog

Dim about As New AboutDialog

我们创建一个AboutDialog

Dim about As New AboutDialog
about.ProgramName = "Battery"
about.Version = "0.1"
about.Copyright = "(c) Jan Bodnar"

通过设置对话框的属性,我们指定名称,版本和版权。

about.Logo = New Gdk.Pixbuf("battery.png")

此行创建徽标。

AboutDialog

图:AboutDialog

FontSelectionDialog

FontSelectionDialog是用于选择字体的对话框。 它通常用于进行一些文本编辑或格式化的应用中。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program shows the FontSelectionDialog
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim label As Label 

    Public Sub New

        MyBase.New("Font dialog")

        Me.InitUI

        Me.SetDefaultSize(350, 300)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll  

    End Sub

    Private Sub InitUI 

        label = New Label("The only victory over love is flight.")
        Dim button As New Button("Select font")

        AddHandler button.Clicked, AddressOf Me.ShowDialog

        Dim fixed As New Fixed
        fixed.Put(button, 100, 30)
        fixed.Put(label, 30, 90)
        Me.Add(fixed)

    End Sub

    Private Sub ShowDialog(ByVal sender As Object, _
            ByVal args As EventArgs)

        Dim fdia As New FontSelectionDialog("Select font name")

        AddHandler fdia.Response, AddressOf Me.SelectFont

        fdia.Run
        fdia.Destroy

    End Sub

    Private Sub SelectFont(ByVal sender As Object, _
            ByVal args As ResponseArgs)

        If args.ResponseId = ResponseType.Ok
            Dim fontdesc As Pango.FontDescription = _
                Pango.FontDescription.FromString(sender.FontName)
            label.ModifyFont(fontdesc)
        End If

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在代码示例中,我们有一个按钮和一个标签。 单击按钮显示FontSelectionDialog

Dim fdia As New FontSelectionDialog("Select font name")

我们创建FontSelectionDialog

If args.ResponseId = ResponseType.Ok
    Dim fontdesc As Pango.FontDescription = _
        Pango.FontDescription.FromString(sender.FontName)
    label.ModifyFont(fontdesc)
End If

如果单击“确定”按钮,则标签小部件的字体将更改为我们在对话框中选择的字体。

FontSelectionDialog

图:FontSelectionDialog

ColorSelectionDialog

ColorSelectionDialog是用于选择颜色的对话框。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program shows the ColorSelectionDialog
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim label As Label 

    Public Sub New

        MyBase.New("Color dialog")

        Me.InitUI

        Me.SetDefaultSize(350, 300)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll    

    End Sub

    Private Sub InitUI

        label = New Label("The only victory over love is flight.")
        Dim button As New Button("Select color")

        AddHandler button.Clicked, AddressOf Me.ShowDialog

        Dim fixed As New Fixed
        fixed.Put(button, 100, 30)
        fixed.Put(label, 30, 90)
        Me.Add(fixed)

    End Sub

    Private Sub ShowDialog(ByVal sender As Object, _
            ByVal args As EventArgs)

        Dim cdia As New ColorSelectionDialog("Select color")

        AddHandler cdia.Response, AddressOf Me.SelectColor

        cdia.Run
        cdia.Destroy

    End Sub

    Private Sub SelectColor(ByVal sender As Object, _
            ByVal args As ResponseArgs)

        If args.ResponseId = ResponseType.Ok
            label.ModifyFg(StateType.Normal, _
                sender.ColorSelection.CurrentColor)
        End If

    End Sub

    Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

该示例与上一个示例非常相似。 这次我们更改标签的颜色。

Dim cdia As New ColorSelectionDialog("Select color")

我们创建ColorSelectionDialog

If args.ResponseId = ResponseType.Ok
    label.ModifyFg(StateType.Normal, _
        sender.ColorSelection.CurrentColor)
End If

如果用户按下 OK,我们将获得颜色值并修改标签的颜色。

ColorSelectionDialog

图:颜色 electionDialog

在 Visual Basic GTK# 教程的这一部分中,我们介绍了对话框。

Cario 绘图

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

在 Visual Basic GTK# 教程的这一部分中,我们将使用 Cairo 库进行一些绘图。

Cairo 是用于创建 2D 矢量图形的库。 我们可以使用它来绘制自己的小部件,图表或各种效果或动画。

色彩

在第一个示例中,我们将使用颜色。 颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 Cario 有效 RGB 值在 0 到 1 的范围内。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program draws three rectangles.
' The interiors are filled with
' different colors.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Colors")

        Me.InitUI

        Me.SetDefaultSize(360, 100)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll 

    End Sub

    Private Sub InitUI

        Dim darea As New DrawingArea
        AddHandler darea.ExposeEvent, AddressOf Me.OnExpose
        Me.Add(darea)

    End Sub

    Private Sub OnExpose(ByVal sender As Object, ByVal args As ExposeEventArgs)

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawColors(cc)

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub

    Private Sub DrawColors(ByVal cc As Cairo.Context)

        cc.SetSourceRGB(0.2, 0.23, 0.9)
        cc.Rectangle(10, 15, 90, 60)
        cc.Fill

        cc.SetSourceRGB(0.9, 0.1, 0.1)
        cc.Rectangle(130, 15, 90, 60)
        cc.Fill

        cc.SetSourceRGB(0.4, 0.9, 0.4)
        cc.Rectangle(250, 15, 90, 60)
        cc.Fill    

    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在我们的示例中,我们将绘制三个矩形,并用三种不同的颜色填充它们。

vbnc -r:/usr/lib/mono/gtk-sharp-2.0/gtk-sharp.dll 
 -r:/usr/lib/mono/gtk-sharp-2.0/gdk-sharp.dll
 -r:/usr/lib/mono/2.0/Mono.Cairo.dll colors.vb

这是我们编译示例的方式。

Dim darea As New DrawingArea

我们将在DrawingArea小部件上进行绘制操作。

AddHandler darea.ExposeEvent, AddressOf Me.OnExpose

所有绘图都是通过我们插入ExposeEvent的方法完成的。

Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

我们从绘图区域的GdkWindow创建Cairo.Context对象。 上下文是我们绘制所有图纸的对象。

Me.DrawColors(cc)

实际图形委托给DrawColors方法。

Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
disposeTarget.Dispose

Dim disposeContext As IDisposable = CType(cc, IDisposable)
disposeContext.Dispose

在这里,我们处理在绘制过程中使用的资源。

cc.SetSourceRGB(0.2, 0.23, 0.9)

SetSourceRGB方法为 Cario 上下文设置颜色。 该方法的三个参数是颜色强度值。

cc.Rectangle(10, 15, 90, 60)

我们画一个矩形。 前两个参数是矩形左上角的 x,y 坐标。 最后两个参数是矩形的宽度和高度。

cc.Fill

我们用当前颜色填充矩形的内部。

Colors

图:颜色

基本形状

下一个示例将一些基本形状绘制到窗口上。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program draws basic shapes
' available in Cairo
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Basic Shapes")

        Me.InitUI

        Me.SetDefaultSize(400, 250)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim darea As New DrawingArea
        AddHandler darea.ExposeEvent, AddressOf Me.OnExpose
        Me.Add(darea)

    End Sub

    Private Sub OnExpose(ByVal sender As Object, ByVal args As ExposeEventArgs)

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawShapes(cc)

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub

    Private Sub DrawShapes(ByVal cc As Cairo.Context)

        cc.SetSourceRGB(0.5, 0.5, 0.5)

        cc.Rectangle(20, 20, 120, 80)
        cc.Rectangle(180, 20, 80, 80)
        cc.Fill

        cc.Arc(330, 60, 40, 0, 2*Math.PI)
        cc.Fill

        cc.Arc(90, 160, 40, Math.PI/4, Math.PI)
        cc.ClosePath
        cc.Fill

        cc.Translate(220, 180)
        cc.Scale(1, 0.7)
        cc.Arc(0, 0, 50, 0, 2*Math.PI)
        cc.Fill

    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在此示例中,我们将创建一个矩形,正方形,圆形,弧形和椭圆形。

cc.Rectangle(20, 20, 120, 80)
cc.Rectangle(180, 20, 80, 80)
cc.Fill

这些线绘制一个矩形和一个正方形。

cc.Arc(330, 60, 40, 0, 2*Math.PI)
cc.Fill

此处Arc方法绘制一个完整的圆。

cc.Translate(220, 180)
cc.Scale(1, 0.7)
cc.Arc(0, 0, 50, 0, 2*Math.PI)
cc.Fill

Translate方法将对象移动到特定点。 如果要绘制椭圆形,请先进行一些缩放。 在这里Scale方法缩小 y 轴。

Basic shapes

图:基本形状

透明矩形

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

在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 Alpha 通道。 (wikipedia.org,answers.com)

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program draws ten
' rectangles with different
' levels of transparency
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Transparent rectangles")

        Me.InitUI

        Me.SetDefaultSize(590, 90)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim darea As New DrawingArea
        AddHandler darea.ExposeEvent, AddressOf Me.OnExpose
        Me.Add(darea)

    End Sub

    Private Sub OnExpose(ByVal sender As Object, ByVal args As ExposeEventArgs)

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawRectangles(cc)

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose()

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose()

    End Sub

    Private Sub DrawRectangles(ByVal cc As Cairo.Context)

        For i As Integer = 1 To 10        
            cc.SetSourceRGBA(0, 0, 1, i*0.1)
            cc.Rectangle(50*i, 20, 40, 40)
            cc.Fill  
        Next

    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在示例中,我们将绘制十个具有不同透明度级别的矩形。

cc.SetSourceRGBA(0, 0, 1, i*0.1)

SetSourceRGBA方法的最后一个参数是 alpha 透明度。

Transparent rectangles

图:透明矩形

甜甜圈

在下面的示例中,我们通过旋转一堆椭圆来创建 n 个复杂形状。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program draws basic shapes
' available in Cairo
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Donut")

        Me.InitUI

        Me.SetDefaultSize(400, 250)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim darea As New DrawingArea
        AddHandler darea.ExposeEvent, AddressOf Me.OnExpose
        Me.Add(darea)

    End Sub

    Private Sub OnExpose(ByVal sender As Object, ByVal args As ExposeEventArgs)

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawDonut(cc)

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub

    Private Sub DrawDonut(ByVal cc As Cairo.Context)

        cc.LineWidth = 0.5

        Dim width, height As Integer
        width = Allocation.Width
        height = Allocation.Height

        cc.Translate(width/2, height/2)
        cc.Arc(0, 0, 120, 0, 2*Math.PI)
        cc.Stroke

        cc.Save

        For i As Integer = 0 To 35
            cc.Rotate( i*Math.PI/36)
            cc.Scale(0.3, 1)
            cc.Arc(0, 0, 120, 0, 2*Math.PI)
            cc.Restore
            cc.Stroke
            cc.Save
        Next

    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

在此示例中,我们创建一个甜甜圈。 形状类似于曲奇,因此得名“甜甜圈”。

cc.Translate(width/2, height/2)
cc.Arc(0, 0, 120, 0, 2*Math.PI)
cc.Stroke

刚开始时有一个椭圆。

For i As Integer = 0 To 35
    cc.Rotate( i*Math.PI/36)
    cc.Scale(0.3, 1)
    cc.Arc(0, 0, 120, 0, 2*Math.PI)
    cc.Restore
    cc.Stroke
    cc.Save
Next

旋转几圈后,有一个甜甜圈。

Donut

图:多纳圈

绘制文字

在下一个示例中,我们在窗口上绘制一些文本。

' ZetCode Mono Visual Basic GTK# tutorial
'
' This program draws text
' on the window
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Public Sub New

        MyBase.New("Soulmate")

        Me.InitUI

        Me.SetDefaultSize(400, 250)
        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll    

    End Sub

    Private Sub InitUI

        Dim darea As New DrawingArea
        AddHandler darea.ExposeEvent, AddressOf Me.OnExpose
        Me.Add(darea)

    End Sub

    Private Sub OnExpose(ByVal sender As Object, ByVal args As ExposeEventArgs)

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawLyrics(cc)

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub

    Private Sub DrawLyrics(ByVal cc As Cairo.Context)

        cc.SetSourceRGB(0.1, 0.1, 0.1)

        cc.SelectFontFace("Purisa", Cairo.FontSlant.Normal, Cairo.FontWeight.Bold)
        cc.SetFontSize(13)

        cc.MoveTo(20, 30)
        cc.ShowText("Most relationships seem so transitory")
        cc.MoveTo(20, 60)
        cc.ShowText("They're all good but not the permanent one")
        cc.MoveTo(20, 120)
        cc.ShowText("Who doesn't long for someone to hold")
        cc.MoveTo(20, 150)
        cc.ShowText("Who knows how to love without being told")
        cc.MoveTo(20, 180)
        cc.ShowText("Somebody tell me why I'm on my own")
        cc.MoveTo(20, 210)
        cc.ShowText("If there's a soulmate for everyone")

    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

我们显示 Natasha Bedingfields Soulmate 歌曲的部分歌词。

cc.SelectFontFace("Purisa", Cairo.FontSlant.Normal, Cairo.FontWeight.Bold)

在这里,我们指定使用的字体。 Purisa 粗体。

cc.SetFontSize(13)

我们指定字体的大小。

cc.MoveTo(20, 30)

我们移动到要绘制文本的位置。

cc.ShowText("Most relationships seem so transitory")

ShowText方法将文本绘制到窗口上。

Soulmate

图:灵魂伴侣

在 Visual Basic GTK# 教程的这一章中,我们使用 Cario 库进行绘图。

PyQt5 中的对话框

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

对话框窗口或对话框是大多数现代 GUI 应用必不可少的部分。 对话被定义为两个或更多人之间的对话。 在计算机应用中,对话框是一个窗口,用于与应用“对话”。 对话框用于输入数据,修改数据,更改应用设置等。

QInputDialog

QInputDialog提供了一个简单的便捷对话框,可从用户那里获取单个值。 输入值可以是字符串,数字或列表中的项目。

inputdialog.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we receive data from
a QInputDialog dialog. 

Aauthor: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit, 
    QInputDialog, QApplication)
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        self.btn = QPushButton('Dialog', self)
        self.btn.move(20, 20)
        self.btn.clicked.connect(self.showDialog)

        self.le = QLineEdit(self)
        self.le.move(130, 22)

        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Input dialog')
        self.show()

    def showDialog(self):

        text, ok = QInputDialog.getText(self, 'Input Dialog', 
            'Enter your name:')

        if ok:
            self.le.setText(str(text))

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

该示例具有一个按钮和一个行编辑小部件。 该按钮显示用于获取文本值的输入对话框。 输入的文本将显示在行编辑小部件中。

text, ok = QInputDialog.getText(self, 'Input Dialog', 
    'Enter your name:')

这行显示输入对话框。 第一个字符串是对话框标题,第二个字符串是对话框中的消息。 对话框返回输入的文本和布尔值。 如果单击“确定”按钮,则布尔值为true

if ok:
    self.le.setText(str(text))

我们从对话框中收到的文本通过setText()设置为行编辑小部件。

Input dialog

图:输入对话框

QColorDialog

QColorDialog提供了一个对话框小部件,用于选择颜色值。

colordialog.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we select a color value
from the QColorDialog and change the background
color of a QFrame widget. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QPushButton, QFrame, 
    QColorDialog, QApplication)
from PyQt5.QtGui import QColor
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        col = QColor(0, 0, 0) 

        self.btn = QPushButton('Dialog', self)
        self.btn.move(20, 20)

        self.btn.clicked.connect(self.showDialog)

        self.frm = QFrame(self)
        self.frm.setStyleSheet("QWidget { background-color: %s }" 
            % col.name())
        self.frm.setGeometry(130, 22, 100, 100)            

        self.setGeometry(300, 300, 250, 180)
        self.setWindowTitle('Color dialog')
        self.show()

    def showDialog(self):

        col = QColorDialog.getColor()

        if col.isValid():
            self.frm.setStyleSheet("QWidget { background-color: %s }"
                % col.name())

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

该应用示例显示了一个按钮和一个QFrame。 窗口小部件背景设置为黑色。 使用QColorDialog,我们可以更改其背景。

col = QColor(0, 0, 0) 

这是QFrame背景的初始颜色。

col = QColorDialog.getColor()

这行弹出QColorDialog

if col.isValid():
    self.frm.setStyleSheet("QWidget { background-color: %s }"
        % col.name())

我们检查颜色是否有效。 如果单击“取消”按钮,则不会返回有效的颜色。 如果颜色有效,我们将使用样式表更改背景颜色。

QFontDialog

QFontDialog是用于选择字体的对话框小部件。

fontdialog.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we select a font name
and change the font of a label. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QPushButton, 
    QSizePolicy, QLabel, QFontDialog, QApplication)
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        vbox = QVBoxLayout()

        btn = QPushButton('Dialog', self)
        btn.setSizePolicy(QSizePolicy.Fixed,
            QSizePolicy.Fixed)

        btn.move(20, 20)

        vbox.addWidget(btn)

        btn.clicked.connect(self.showDialog)

        self.lbl = QLabel('Knowledge only matters', self)
        self.lbl.move(130, 20)

        vbox.addWidget(self.lbl)
        self.setLayout(vbox)          

        self.setGeometry(300, 300, 250, 180)
        self.setWindowTitle('Font dialog')
        self.show()

    def showDialog(self):

        font, ok = QFontDialog.getFont()
        if ok:
            self.lbl.setFont(font)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们有一个按钮和一个标签。 使用QFontDialog,我们更改标签的字体。

font, ok = QFontDialog.getFont()

在这里我们弹出字体对话框。 getFont()方法返回字体名称和ok参数。 如果用户单击“确定”,则它等于True; 否则为False

if ok:
    self.label.setFont(font)

如果单击“确定”,标签的字体将更改为setFont()

QFileDialog

QFileDialog是允许用户选择文件或目录的对话框。 可以选择打开和保存文件。

filedialog.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we select a file with a
QFileDialog and display its contents
in a QTextEdit.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QMainWindow, QTextEdit, 
    QAction, QFileDialog, QApplication)
from PyQt5.QtGui import QIcon
import sys

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        self.textEdit = QTextEdit()
        self.setCentralWidget(self.textEdit)
        self.statusBar()

        openFile = QAction(QIcon('open.png'), 'Open', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open new File')
        openFile.triggered.connect(self.showDialog)

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(openFile)       

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('File dialog')
        self.show()

    def showDialog(self):

        fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')

        if fname[0]:
            f = open(fname[0], 'r')

            with f:
                data = f.read()
                self.textEdit.setText(data)        

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

该示例显示了一个菜单栏,集中设置的文本编辑小部件和一个状态栏。 菜单项显示用于选择文件的QFileDialog。 文件的内容被加载到文本编辑小部件中。

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

该示例基于QMainWindow小部件,因为我们集中设置了文本编辑小部件。

fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')

我们弹出QFileDialoggetOpenFileName()方法中的第一个字符串是标题。 第二个字符串指定对话框的工作目录。 默认情况下,文件过滤器设置为All Files (*)

if fname[0]:
    f = open(fname[0], 'r')

    with f:
        data = f.read()
        self.textEdit.setText(data)        

读取所选文件名,并将文件内容设置为文本编辑小部件。

在 PyQt5 教程的这一部分中,我们使用了对话框。

自定义小部件

原文: http://zetcode.com/gui/vbgtk/customwidget/

您是否曾经看过某个应用,并想知道如何创建特定的 GUI 项目? 可能每个想成为程序员的人都有。 然后,您正在查看您最喜欢的 gui 库提供的小部件列表。 但是你找不到。 工具箱通常仅提供最常见的窗口小部件,例如按钮,文本窗口小部件,滑块等。没有工具箱可以提供所有可能的窗口小部件。

实际上有两种工具箱。 Spartan 工具包和重量级工具包。 FLTK 工具包是一种 spartan 工具包。 它仅提供了非常基本的小部件,并假定程序员将自己创建更复杂的小部件。 wxWidgets 是重量级的。 它有很多小部件。 但是,它不提供更专业的小部件。 例如,速度表小部件,即测量要刻录 CD 容量的小部件(例如,在 nero 中找到)。 工具箱通常也没有图表。

程序员必须自己创建此类小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能性。 程序员可以修改或增强现有的小部件。 或者,他可以从头开始创建自定义窗口小部件。

刻录小部件

这是我们从头开始创建的小部件的示例。 可以在各种媒体刻录应用(例如 Nero 刻录 ROM)中找到此小部件。

custom.vb

Imports Gtk

NameSpace BurningWidget

Public Class Burning 
    Inherits DrawingArea 

    Const PANEL_HEIGHT As Integer = 30
    Const DIVISIONS As Integer = 10
    Const FULL_CAPACITY As Double = 700
    Const MAX_CAPACITY As Double = 750

    Dim redColor As New Gdk.Color(1, 0.7, 0.7)
    Dim yellowColor As New Gdk.Color(1, 1, 0.7)

    Dim parent As Widget 

    Dim num() As String = { _
        "75", "150", "225", "300", _
        "375", "450", "525", "600", _
        "675" _
    }

    Public Sub New(ByVal parent As Widget) 

        Me.SetSizeRequest(1, PANEL_HEIGHT)

        Me.parent = parent
        AddHandler Me.ExposeEvent, AddressOf Me.OnExpose

    End Sub

    Private Sub OnExpose(ByVal sender As Object, ByVal e As ExposeEventArgs) 

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        Me.DrawCustomWidget(cc)

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub

    Private Sub DrawCustomWidget(ByVal cc As Cairo.Context)

        cc.LineWidth = 0.8

        cc.SelectFontFace("Courier 10 Pitch", _
            Cairo.FontSlant.Normal, Cairo.FontWeight.Normal)
        cc.SetFontSize(11)

        Dim burn As Custom.GtkVBApp = CType(parent, Custom.GtkVBApp)

        Dim slid_width As Double = burn.GetCurrentWidth
        Dim width As Double = Allocation.Width
        Dim move As Double = width / DIVISIONS

        Dim till As Double = (width / MAX_CAPACITY) * slid_width
        Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY

        If slid_width >= FULL_CAPACITY

            cc.SetSourceRGB(1.0, 1.0, 0.72)
            cc.Rectangle(0, 0, full, PANEL_HEIGHT)
            cc.Clip
            cc.Paint
            cc.ResetClip

            cc.SetSourceRGB(1.0, 0.68, 0.68)
            cc.Rectangle(full, 0, till-full, PANEL_HEIGHT)  
            cc.Clip
            cc.Paint
            cc.ResetClip

        Else 

            cc.SetSourceRGB(1.0, 1.0, 0.72)
            cc.Rectangle(0, 0, till, PANEL_HEIGHT)
            cc.Clip
            cc.Paint
            cc.ResetClip
       End If

       cc.SetSourceRGB(0.35, 0.31, 0.24)

       For i As Integer = 1 To num.Length

           cc.MoveTo(i*move, 0)
           cc.LineTo(i*move, 5)    
           cc.Stroke

           Dim extents As Cairo.TextExtents = cc.TextExtents(num(i-1))
           cc.MoveTo(i*move-extents.Width/2, 15)
           cc.TextPath(num(i-1))
           cc.Stroke
       Next   

    End Sub

End Class

End Namespace

我们在窗口底部放置一个DrawingArea并手动绘制整个窗口小部件。 所有重要的代码都驻留在DrawCustomWidget中,这是从 Burning 类的OnExpose方法调用的。 此小部件以图形方式显示了介质的总容量和可供我们使用的可用空间。 该小部件由比例小部件控制。 自定义窗口小部件的最小值为 0,最大值为 750。如果值达到 700,则开始绘制红色。 这通常表示过度燃烧。

Dim num() As String = { _
    "75", "150", "225", "300", _
    "375", "450", "525", "600", _
    "675" _
}

这些数字显示在刻录小部件上。 它们显示了介质的容量。

Dim burn As Custom.GtkVBApp = CType(parent, Custom.GtkVBApp)

Dim slid_width As Double = burn.GetCurrentWidth

这两行从 scale 小部件获取当前数字。 我们获得父窗口小部件,并从父窗口小部件中获得当前值。

Dim till As Double = (width / MAX_CAPACITY) * slid_width
Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY

我们使用width变量进行转换。 在比例尺值和自定义小部件的度量之间。 请注意,我们使用浮点值。 我们在绘图中获得了更高的精度。 till参数确定要绘制的总大小。 该值来自滑块小部件。 它占整个面积的一部分。 full参数确定了我们开始绘制红色的点。

cc.SetSourceRGB(1.0, 1.0, 0.72)
cc.Rectangle(0, 0, till, PANEL_HEIGHT)
cc.Clip
cc.Paint
cc.ResetClip

此代码在此处绘制了一个黄色矩形,直到介质充满为止。

Dim extents As Cairo.TextExtents = cc.TextExtents(num(i-1))
cc.MoveTo(i*move-extents.Width/2, 15)
cc.TextPath(num(i-1))
cc.Stroke

这里的代码在刻录小部件上绘制数字。 我们计算TextExtents来正确定位文本。

burning.vb

' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program, we create
' a custom widget
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

NameSpace Custom

Public Class GtkVBApp
    Inherits Window

    Const MAX_CAPACITY As Integer = 750

    Dim cur_value As Integer
    Dim burning As BurningWidget.Burning

    Public Sub New

        MyBase.New("Burning")

        Me.InitUI

        Me.SetDefaultSize(350, 200)

        Me.SetPosition(WindowPosition.Center)
        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.ShowAll

    End Sub

    Private Sub InitUI

        Dim vbox As New VBox(False, 2)

        Dim scale As New HScale(0, MAX_CAPACITY, 1)
        scale.SetSizeRequest(160, 35)

        AddHandler scale.ValueChanged, AddressOf Me.OnChanged

        Dim fixed As New Fixed
        fixed.Put(scale, 50, 50)

        vbox.PackStart(fixed)

        burning = New BurningWidget.Burning(Me)
        vbox.PackStart(burning, False, False, 0)

        Me.Add(vbox)

    End Sub

    Private Sub OnChanged(ByVal sender As Object, ByVal args As EventArgs)
        cur_value = sender.Value
        burning.QueueDraw
    End Sub

    Public Function GetCurrentWidth As Integer
        Return cur_value
    End Function

    Sub OnDelete(ByVal sender As Object, ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

End Namespace

这是主要的类。

Private Sub OnChanged(ByVal sender As Object, ByVal args As EventArgs)
    cur_value = sender.Value
    burning.QueueDraw
End Sub

我们从小部件中获取值,并将其存储在cur_value变量中以备后用。 我们重新绘制刻录的小部件。

Burning widget

图:刻录小部件

在本章中,我们使用 GTK# 和 Visual Basic 创建了一个自定义窗口小部件。

贪食蛇

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

在 Visual Basic GTK# 编程教程的这一部分中,我们将创建贪食蛇游戏克隆。

贪食蛇是较旧的经典视频游戏。 它最初是在 70 年代后期创建的。 后来它被带到 PC 上。 在这个游戏中,玩家控制蛇。 目的是尽可能多地吃苹果。 蛇每次吃一个苹果,它的身体就会长大。 蛇必须避开墙壁和自己的身体。

开发

蛇的每个关节的大小为 10px。 蛇由光标键控制。 最初,蛇具有三个关节。 游戏立即开始。 游戏结束后,我们在窗口中心显示"Game Over"消息。

board.vb

Imports Gtk
Imports Cairo

NameSpace BoardSpace

Public Class Board 
    Inherits DrawingArea 

    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 dot As ImageSurface
    Dim apple As ImageSurface
    Dim head As ImageSurface

    Public Sub New

        MyBase.New

        ModifyBg(StateType.Normal, New Gdk.Color(0, 0, 0))

        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

        Try 
            dot = New ImageSurface("dot.png")
            head = New ImageSurface("head.png")
            apple = New ImageSurface("apple.png")
        Catch 
            Console.WriteLine("Images not found")
            Environment.Exit(1)
        End Try

        Me.LocateApple

        Dim timer As New GLib.TimeoutHandler(AddressOf Me.OnTimer)

        GLib.Timeout.Add(100, timer)
        AddHandler Me.ExposeEvent, AddressOf Me.OnExpose

    End Sub

    Protected Sub OnExpose(ByVal sender As Object, ByVal e As ExposeEventArgs) 

        Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

        If inGame
            Me.DrawObjects(cc)
        Else 
            Me.GameOver(cc)
        End If

        Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
        disposeTarget.Dispose

        Dim disposeContext As IDisposable = CType(cc, IDisposable)
        disposeContext.Dispose

    End Sub

    Private Sub DrawObjects(ByVal cc As Cairo.Context) 

        cc.SetSourceSurface(apple, apple_x, apple_y)
        cc.Paint

        For z As Integer = 0 to dots - 1
            If z = 0
                cc.SetSourceSurface(head, x(z), y(z))
                cc.Paint
            Else 
                cc.SetSourceSurface(dot, x(z), y(z))
                cc.Paint
            End If 
        Next

    End Sub

    Private Sub GameOver(ByVal cc As Cairo.Context) 

        Dim message As String = "Game Over"

        Dim x As Integer = Allocation.Width / 2
        Dim y As Integer = Allocation.Height / 2

        cc.SetSourceRGB(1, 1, 1)
        cc.SetFontSize(18)

        Dim extents As TextExtents = cc.TextExtents(message)

        cc.MoveTo(x - extents.Width/2, y)
        cc.ShowText(message)
        inGame = False

    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

    Private Function OnTimer As Boolean

        If inGame

            Me.CheckApple
            Me.CheckCollision
            Me.Move
            Me.QueueDraw

            Return True

         Else 
            Return False
        End If

    End Function

    Public Sub OnKeyDown(ByVal e As Gdk.EventKey)

        Dim key As Integer = e.KeyValue

        If key = Gdk.Key.Left AndAlso Not right
            left = True
            up = False
            down = False
        End If

        If key = Gdk.Key.Right AndAlso Not left
            right = True
            up = False
            down = False
        End If

        If key = Gdk.Key.Up AndAlso Not down
            up = True
            right = False
            left = False
        End If

        If key = Gdk.Key.Down AndAlso 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(cc)
Else 
    Me.GameOver(cc)
End If

OnExpose方法内部,我们检查inGame变量。 如果为真,则绘制对象。 苹果和蛇的关节。 否则,我们显示"Game Over"文本。

Private Sub DrawObjects(ByVal cc As Cairo.Context) 

     cc.SetSourceSurface(apple, apple_x, apple_y)
     cc.Paint

     For z As Integer = 0 to dots - 1
         If z = 0
             cc.SetSourceSurface(head, x(z), y(z))
             cc.Paint
         Else 
             cc.SetSourceSurface(dot, x(z), y(z))
             cc.Paint
         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
    Me.QueueDraw

    Return True

Else 
    Return False
End If

每 140 毫秒,将调用OnTimer方法。 如果我们参与了游戏,我们将调用三种构建游戏逻辑的方法。 否则,我们返回False,它将停止计时器事件。

Board类的OnKeyDown方法中,我们确定按下的键。

If key = Gdk.Key.Left AndAlso Not right
    left = True
    up = False
    down = False
End If

如果单击左光标键,则将left变量设置为true。 在Move方法中使用此变量来更改蛇对象的坐标。 还要注意,当蛇向右行驶时,我们不能立即向左转。

nibbles.vb

' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program, we create
' a Nibbles game clone
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

Public Class GtkVBApp
    Inherits Window

    Dim WIDTH As Integer = 250
    Dim HEIGHT As Integer = 150
    Dim board As BoardSpace.Board

    Public Sub New

        MyBase.New("Nibbles")

        board = New BoardSpace.Board
        Me.Add(board)

        AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

        Me.Resize(310, 310)
        Me.Move(300, 300)
        Me.ShowAll

    End Sub

    Private Sub OnDelete(ByVal sender As Object, _
            ByVal args As DeleteEventArgs)
        Application.Quit
    End Sub

    Protected Overrides Function OnKeyPressEvent(ByVal e As Gdk.EventKey) As Boolean
        board.OnKeyDown(e)
        Return True
    End Function

    Public Shared Sub Main

        Application.Init
        Dim app As New GtkVBApp
        Application.Run

    End Sub

End Class

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

Protected Overrides Function OnKeyPressEvent(ByVal e As Gdk.EventKey) As Boolean
    board.OnKeyDown(e)
    Return True
End Function

在这个类中,我们捕获按键事件。 并将处理委托给板类的OnKeyDown方法。

Nibbles

图:贪食蛇

以下命令编译游戏。

vbnc -r:/usr/lib/mono/gtk-sharp-2.0/gtk-sharp.dll 
  -r:/usr/lib/mono/gtk-sharp-2.0/gdk-sharp.dll -r:/usr/lib/mono/2.0/Mono.Cairo.dll 
  -r:/usr/lib/mono/gtk-sharp-2.0/glib-sharp.dll nibbles.vb board.vb

这是使用 GTK# 库和 Visual Basic 编程语言编写的贪食蛇电脑游戏。

PyGTK 教程

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

这是 PyGTK 教程。 在本教程中,我们将学习 PyGTK 中 GUI 编程的基础。 PyGTK 教程适合初学者和更高级的程序员。

PyGTK 已过时,请参阅 Python Gtk 教程

目录

PyGTK

PyGTK 是 GTK+ GUI 库的一组 Python 包装器。 它提供了一套全面的图形元素和其他有用的编程工具,可用于创建桌面应用。 它是 GNOME 项目的一部分。 PyGTK 是免费软件,并根据 LGPL 许可。

Tweet

相关教程

您可能想通过 Python 教程来重新了解 Python 语言。 其他 Python GUI 绑定的教程包括 wxPython 教程PyQt4 教程PySide 教程Tkinter 教程

PyGTK 简介

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

在 PyGTK 编程教程的这一部分中,我们将大体讨论 PyGTK GUI 库和 Python 编程语言。

关于本教程

这是 PyGTK 编程教程。 它已在 Linux 上创建并经过测试。 PyGTK 编程教程适合新手和更高级的程序员。

PyGTK

PyGTK 是 GTK+ GUI 库的一组 Python 包装器。 它提供了一套全面的图形元素和其他有用的编程工具,可用于创建桌面应用。 它是 GNOME 项目的一部分。 PyGTK 是免费软件,并根据 LGPL 许可。 原始作者或 PyGTK 是 James Henstridge 。 PyGTK 非常易于使用,是快速原型制作的理想选择。 当前,PyGTK 是 GTK+ 库最流行的绑定之一。

PyGTK 由几个模块组成。

PyGTK modules

GObject是一个基类,为 PyGTK 类提供通用的属性和功能。 ATK是辅助功能工具包。 该工具包提供的工具可帮助残障人士使用计算机。 GTK是用户界面模块。 Pango是一个用于处理文本和国际化的库。 Cairo是用于创建 2D 矢量图形的库。 Glade用于从 XML 描述构建 GUI 界面。

Python

python logo Python 是一种通用的,动态的,面向对象的编程语言。 Python 语言的设计目的强调程序员的生产力和代码可读性。 Python 最初是由 Guido van Rossum 开发的。 它于 1991 年首次发布。Python 受 ABC,Haskell,Java,Lisp,Icon 和 Perl 编程语言的启发。 Python 是高级通用多平台解释型语言。 Python 是一种简约语言。 它最明显的功能之一是它不使用分号或方括号。 Python 使用缩进代替。 目前,Python 有两个主要分支。 Python 2.x 和 Python3.x。 Python 3.x 打破了与早期版本 Python 的向后兼容性。 它的创建是为了纠正该语言的某些设计缺陷并使该语言更简洁。 本教程使用 Python 2.x 编写。如今,Python 由世界各地的一大批志愿者维护。

GTK+

GTK+ 是用于创建图形用户界面的库。 该库是用 C 编程语言创建的。 GTK+ 库也称为 GIMP 工具包。 最初,该库是在开发 GIMP 图像处理器时创建的。 从那时起,GTK+ 成为 Linux 和 BSD Unix 下最受欢迎的工具包之一。 如今,开源世界中的大多数 GUI 软件都是在 Qt 或 GTK+ 中创建的。 GTK+ 是面向对象的应用编程接口。 面向对象的系统是使用 Glib 对象系统创建的,该系统是 GTK+ 库的基础。 GObject还可以为其他各种编程语言创建语言绑定。 存在用于 C++ ,Python,Perl,Java,C# 和其他编程语言的语言绑定。

Gnome 和 XFce 桌面环境已使用 GTK+ 库创建。 SWT 和 wxWidgets 是使用 GTK+ 的众所周知的编程框架。 使用 GTK+ 的著名软件应用包括 Firefox 或 Inkscape。

数据来源

PyGTK 的第一步

原文: http://zetcode.com/gui/pygtk/firststeps/

在 PyGTK 编程教程的这一部分中,我们将进行编程的第一步。 我们将创建简单的程序。

简单的例子

第一个代码示例是一个非常简单的示例。

center.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This is a trivial PyGTK example
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()

        self.connect("destroy", gtk.main_quit)
        self.set_size_request(250, 150)
        self.set_position(gtk.WIN_POS_CENTER)
        self.show()

PyApp()
gtk.main()

此代码显示一个居中的窗口。

import gtk

我们导入gtk模块。 在这里,我们有创建 GUI 应用的对象。

class PyApp(gtk.Window):

我们的应用基于PyApp类。 它继承自Window

def __init__(self):
    super(PyApp, self).__init__()

这是构造器。 它构建了我们的应用。 它还通过super()调用调用其父构造器。

self.connect("destroy", gtk.main_quit)

我们将destroy信号连接到main_quit()函数。 当单击标题栏中的关闭按钮或按 Alt + F4 时,将调用destroy信号。 窗口被破坏,但应用未被破坏。 如果从命令行启动示例,则可以看到它。 通过调用main_quit(),我们永久退出了该应用。

self.set_size_request(250, 150)

我们将窗口的大小设置为250x150px

self.set_position(gtk.WIN_POS_CENTER)

这条线使窗口在屏幕上居中。

self.show()

现在我们显示窗口。 在调用show()方法之前,该窗口不可见。

PyApp()
gtk.main()

我们创建程序实例并启动主循环。

图标

在下一个示例中,我们显示应用图标。 大多数窗口管理器在标题栏的左上角以及任务栏上都显示图标。

icon.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows an icon
# in the titlebar of the window 
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk, sys

class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Icon")
        self.set_size_request(250, 150)
        self.set_position(gtk.WIN_POS_CENTER)

        try:
            self.set_icon_from_file("web.png")
        except Exception, e:
            print e.message
            sys.exit(1)

        self.connect("destroy", gtk.main_quit)

        self.show()

PyApp()
gtk.main()

该代码示例显示了应用图标。

self.set_title("Icon")

我们为窗口设置标题。

self.set_icon_from_file("web.png")

set_icon_from_file()方法为窗口设置图标。 从当前工作目录中的磁盘加载映像。

Icon

图:图标

按钮

在下一个示例中,我们将使用 PyGTK 库进一步增强我们的编程技能。

buttons.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows four buttons
# in various modes 
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Buttons")
        self.set_size_request(250, 200)
        self.set_position(gtk.WIN_POS_CENTER)

        btn1 = gtk.Button("Button")
        btn1.set_sensitive(False)
        btn2 = gtk.Button("Button")
        btn3 = gtk.Button(stock=gtk.STOCK_CLOSE)
        btn4 = gtk.Button("Button")
        btn4.set_size_request(80, 40)

        fixed = gtk.Fixed()

        fixed.put(btn1, 20, 30)
        fixed.put(btn2, 100, 30)
        fixed.put(btn3, 20, 80)
        fixed.put(btn4, 100, 80)

        self.connect("destroy", gtk.main_quit)

        self.add(fixed)
        self.show_all()

PyApp()
gtk.main()

我们在窗口上显示四个不同的按钮。 我们将看到容器窗口小部件和子窗口小部件之间的区别,并将更改子窗口小部件的某些属性。

btn1 = gtk.Button("Button")

Button是子窗口小部件。 子窗口小部件放置在容器内。

btn1.set_sensitive(False)

我们使此按钮不敏感。 这意味着我们无法单击它。 也不能选择,聚焦等。图形化的小部件为灰色。

btn3 = gtk.Button(stock=gtk.STOCK_CLOSE)

第三个按钮在其区域内显示图像。 PyGTK 库具有我们可以使用的内置图像库。

btn4.set_size_request(80, 40)

在这里,我们更改按钮的大小。

fixed = gtk.Fixed()

Fixed小部件是不可见的容器小部件。 其目的是包含其他子窗口小部件。

fixed.put(btn1, 20, 30)
fixed.put(btn2, 100, 30)
...

在这里,我们将按钮小部件放置在固定容器小部件内。

self.add(fixed)

我们将Fixed容器设置为Window小部件的主要容器。

self.show_all()

我们可以调用show_all()方法,也可以在每个小部件上调用show()方法。 包括容器。

Buttons

图:按钮

工具提示

工具提示是应用中小部件上的提示。 可用于提供其他帮助。

tooltips.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This code shows a tooltip on 
# a window and a button
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Tooltips")
        self.set_size_request(250, 200)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        self.fixed = gtk.Fixed()
        self.add(self.fixed)

        button = gtk.Button("Button")
        button.set_size_request(80, 35)      

        self.fixed.put(button, 50, 50)

        self.set_tooltip_text("Window widget")
        button.set_tooltip_text("Button widget")

        self.show_all()

PyApp()
gtk.main()

在此示例中,我们为窗口和按钮设置工具提示。

self.set_tooltip_text("Window widget")
button.set_tooltip_text("Button widget")

set_tooltip_text()完成工作。

Tooltips

图:工具提示 s

在本章中,我们在 PyGTK 编程库中创建了第一个程序。

PyGTK 中的布局管理

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

在 PyGTK 教程的这一章中,我们将展示如何在窗口或对话框中布置窗口小部件。

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

Fixed

Fixed容器将子窗口小部件放置在固定位置并具有固定大小。 此容器不执行自动布局管理。 在大多数应用中,我们不使用此容器。 我们在某些专业领域使用它。 例如游戏,使用图表的专用应用,可以移动的可调整大小的组件(如电子表格应用中的图表),小型教育示例。

fixed.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example demonstrates a Fixed
# container widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk
import sys

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Fixed")
        self.set_size_request(300, 280)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        try:
            self.bardejov = gtk.gdk.pixbuf_new_from_file("bardejov.jpg")
            self.rotunda = gtk.gdk.pixbuf_new_from_file("rotunda.jpg")
            self.mincol = gtk.gdk.pixbuf_new_from_file("mincol.jpg")
        except Exception, e:
            print e.message
            sys.exit(1)

        image1 = gtk.Image()
        image2 = gtk.Image()
        image3 = gtk.Image()

        image1.set_from_pixbuf(self.bardejov)
        image2.set_from_pixbuf(self.rotunda)
        image3.set_from_pixbuf(self.mincol)

        fix = gtk.Fixed()

        fix.put(image1, 20, 20)
        fix.put(image2, 40, 160)
        fix.put(image3, 170, 50)

        self.add(fix)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

在我们的示例中,我们在窗口上显示了三个小图像。 我们明确指定放置这些图像的 x,y 坐标。

self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))

为了获得更好的视觉体验,我们将背景色更改为深灰色。

self.bardejov = gtk.gdk.pixbuf_new_from_file("bardejov.jpg")

我们从磁盘上的文件加载映像。

image1 = gtk.Image()
image2 = gtk.Image()
image3 = gtk.Image()

image1.set_from_pixbuf(self.bardejov)
image2.set_from_pixbuf(self.rotunda)
image3.set_from_pixbuf(self.mincol)

Image是用于显示图像的小部件。 它在构造器中使用一个Pixbuf对象。

fix = gtk.Fixed()

我们创建Fixed容器。

fix.put(image1, 20, 20)

我们将第一个图像放置在x = 20y = 20坐标处。

self.add(fix)

最后,我们将Fixed容器添加到窗口中。

Fixed

图:固定

Alignment

Alignment容器控制其子窗口小部件的对齐方式和大小。

alignment.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows how to use
# the Alignment widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Alignment")
        self.set_size_request(260, 150)
        self.set_position(gtk.WIN_POS_CENTER)

        vbox = gtk.VBox(False, 5)
        hbox = gtk.HBox(True, 3)

        valign = gtk.Alignment(0, 1, 0, 0)
        vbox.pack_start(valign)

        ok = gtk.Button("OK")
        ok.set_size_request(70, 30)
        close = gtk.Button("Close")

        hbox.add(ok)
        hbox.add(close)

        halign = gtk.Alignment(1, 0, 0, 0)
        halign.add(hbox)

        vbox.pack_start(halign, False, False, 3)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

在代码示例中,我们在窗口的右下角放置了两个按钮。 为此,我们使用一个水平框和一个垂直框以及两个对齐容器。

valign = gtk.Alignment(0, 1, 0, 0)

这会将子窗口小部件置于底部。

vbox.pack_start(valign)

在这里,我们将Alignment小部件放置到垂直框中。

hbox = gtk.HBox(True, 3)
...
ok = gtk.Button("OK")
ok.set_size_request(70, 30)
close = gtk.Button("Close")

hbox.add(ok)
hbox.add(close)

我们创建一个水平框,并在其中放置两个按钮。

halign = gtk.Alignment(1, 0, 0, 0)
halign.add(hbox)

vbox.pack_start(halign, False, False, 3)

这将创建一个对齐容器,它将其子窗口小部件放在右侧。 我们将水平框添加到对齐容器中,然后将对齐容器包装到垂直框中。 我们必须记住,对齐容器仅包含一个子窗口小部件。 这就是为什么我们必须使用盒子。

Alignment

图:对齐

Table

Table小部件按行和列排列小部件。

calculator.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows how to use
# the Table container widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Calculator")
        self.set_size_request(250, 230)
        self.set_position(gtk.WIN_POS_CENTER)

        vbox = gtk.VBox(False, 2)

        mb = gtk.MenuBar()
        filemenu = gtk.Menu()
        filem = gtk.MenuItem("File")
        filem.set_submenu(filemenu)
        mb.append(filem)

        vbox.pack_start(mb, False, False, 0)

        table = gtk.Table(5, 4, True)

        table.attach(gtk.Button("Cls"), 0, 1, 0, 1)
        table.attach(gtk.Button("Bck"), 1, 2, 0, 1)
        table.attach(gtk.Label(), 2, 3, 0, 1)
        table.attach(gtk.Button("Close"), 3, 4, 0, 1)

        table.attach(gtk.Button("7"), 0, 1, 1, 2)
        table.attach(gtk.Button("8"), 1, 2, 1, 2)
        table.attach(gtk.Button("9"), 2, 3, 1, 2)
        table.attach(gtk.Button("/"), 3, 4, 1, 2)

        table.attach(gtk.Button("4"), 0, 1, 2, 3)
        table.attach(gtk.Button("5"), 1, 2, 2, 3)
        table.attach(gtk.Button("6"), 2, 3, 2, 3)
        table.attach(gtk.Button("*"), 3, 4, 2, 3)

        table.attach(gtk.Button("1"), 0, 1, 3, 4)
        table.attach(gtk.Button("2"), 1, 2, 3, 4)
        table.attach(gtk.Button("3"), 2, 3, 3, 4)
        table.attach(gtk.Button("-"), 3, 4, 3, 4)

        table.attach(gtk.Button("0"), 0, 1, 4, 5)
        table.attach(gtk.Button("."), 1, 2, 4, 5)
        table.attach(gtk.Button("="), 2, 3, 4, 5)
        table.attach(gtk.Button("+"), 3, 4, 4, 5)

        vbox.pack_start(gtk.Entry(), False, False, 0)
        vbox.pack_end(table, True, True, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

我们使用Table小部件创建一个计算器框架。

table = gtk.Table(5, 4, True)

我们创建一个具有 5 行 4 列的表小部件。 第三个参数是齐次参数。 如果设置为true,则表中的所有小部件都具有相同的大小。 所有窗口小部件的大小等于表容器中最大的窗口小部件。

table.attach(gtk.Button("Cls"), 0, 1, 0, 1)

我们在表格容器上附加一个按钮。 到表格的左上方单元格。 前两个参数是单元格的左侧和右侧,后两个参数是单元格的顶部和底部。

vbox.pack_end(table, True, True, 0)

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

Calculator skeleton

图:计算机骨架

窗口

接下来,我们将创建一个更高级的示例。 我们显示一个可以在 JDeveloper IDE 中找到的窗口。

windows.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This is a more complicated layout
# example
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk
import sys

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Windows")
        self.set_size_request(300, 250)
        self.set_border_width(8)
        self.set_position(gtk.WIN_POS_CENTER)

        table = gtk.Table(8, 4, False)
        table.set_col_spacings(3)

        title = gtk.Label("Windows")

        halign = gtk.Alignment(0, 0, 0, 0)
        halign.add(title)

        table.attach(halign, 0, 1, 0, 1, gtk.FILL, 
            gtk.FILL, 0, 0);

        wins = gtk.TextView()
        wins.set_editable(False)
        wins.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(5140, 5140, 5140))
        wins.set_cursor_visible(False)
        table.attach(wins, 0, 2, 1, 3, gtk.FILL | gtk.EXPAND,
            gtk.FILL | gtk.EXPAND, 1, 1)

        activate = gtk.Button("Activate")
        activate.set_size_request(50, 30)
        table.attach(activate, 3, 4, 1, 2, gtk.FILL, 
            gtk.SHRINK, 1, 1)

        valign = gtk.Alignment(0, 0, 0, 0)
        close = gtk.Button("Close")
        close.set_size_request(70, 30)
        valign.add(close)
        table.set_row_spacing(1, 3)
        table.attach(valign, 3, 4, 2, 3, gtk.FILL,
            gtk.FILL | gtk.EXPAND, 1, 1)

        halign2 = gtk.Alignment(0, 1, 0, 0)
        help = gtk.Button("Help")
        help.set_size_request(70, 30)
        halign2.add(help)
        table.set_row_spacing(3, 6)
        table.attach(halign2, 0, 1, 4, 5, gtk.FILL, 
            gtk.FILL, 0, 0)

        ok = gtk.Button("OK")
        ok.set_size_request(70, 30)
        table.attach(ok, 3, 4, 4, 5, gtk.FILL, 
            gtk.FILL, 0, 0);

        self.add(table)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

该代码示例显示了如何在 PyGTK 中创建类似的窗口。

table = gtk.Table(8, 4, False)
table.set_col_spacings(3)

该示例基于Table容器。 列之间将有 3px 的间距。

title = gtk.Label("Windows")

halign = gtk.Alignment(0, 0, 0, 0)
halign.add(title)

table.attach(halign, 0, 1, 0, 1, gtk.FILL, 
    gtk.FILL, 0, 0);

这段代码创建了一个向左对齐的标签。 标签放置在Table容器的第一行中。

wins = gtk.TextView()
wins.set_editable(False)
wins.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(5140, 5140, 5140))
wins.set_cursor_visible(False)
table.attach(wins, 0, 2, 1, 3, gtk.FILL | gtk.EXPAND,
    gtk.FILL | gtk.EXPAND, 1, 1)

文本视图小部件跨越两行两列。 我们使小部件不可编辑并隐藏光标。

valign = gtk.Alignment(0, 0, 0, 0)
close = gtk.Button("Close")
close.set_size_request(70, 30)
valign.add(close)
table.set_row_spacing(1, 3)
table.attach(valign, 3, 4, 2, 3, gtk.FILL,
    gtk.FILL | gtk.EXPAND, 1, 1)

我们将关闭按钮放在文本视图小部件旁边的第四列中。 (我们从零开始计数)将按钮添加到对齐小部件中,以便可以将其对齐到顶部。

Windows

图:窗口

PyGTK 编程教程的这一章是有关布局管理的。

PyGTK 中的菜单

原文: http://zetcode.com/gui/pygtk/menus/

在 PyGTK 编程教程的这一部分中,我们将使用菜单。

菜单栏是 GUI 应用中最常见的部分之一。 它是位于各个菜单中的一组命令。 在控制台应用中,您必须记住所有这些神秘命令,在这里,我们将大多数命令分组为逻辑部分。 这些公认的标准可进一步减少学习新应用所花费的时间。

简单菜单

在第一个示例中,我们将创建一个带有一个文件菜单的菜单栏。 该菜单将只有一个菜单项。 通过选择项目,应用退出。

simplemenu.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows a simple menu
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Simple menu")
        self.set_size_request(250, 200)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        mb = gtk.MenuBar()

        filemenu = gtk.Menu()
        filem = gtk.MenuItem("File")
        filem.set_submenu(filemenu)

        exit = gtk.MenuItem("Exit")
        exit.connect("activate", gtk.main_quit)
        filemenu.append(exit)

        mb.append(filem)

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(mb, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

这是一个最小的菜单栏功能示例。

mb = gtk.MenuBar()

MenuBar小部件已创建。

filemenu = gtk.Menu()
filem = gtk.MenuItem("File")
filem.set_submenu(filemenu)

创建顶层MenuItem

exit = gtk.MenuItem("Exit")
exit.connect("activate", gtk.main_quit)
filemenu.append(exit)

将创建出口MenuItem,并将其附加到文件MenuItem中。

mb.append(filem)

顶级MenuItem被附加到MenuBar小部件。

vbox = gtk.VBox(False, 2)
vbox.pack_start(mb, False, False, 0)

与其他工具包不同,我们必须自己照顾菜单栏的布局管理。 我们将菜单栏放入垂直框。

Simple menu

图:简单菜单

图像菜单

在下一个示例中,我们将进一步探索菜单。 我们将图像和加速器添加到我们的菜单项中。 加速器是用于激活菜单项的键盘快捷键。

imagemenu.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows a menu with
# images, accelerators and a separator
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Image menu")
        self.set_size_request(250, 200)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        mb = gtk.MenuBar()

        filemenu = gtk.Menu()
        filem = gtk.MenuItem("_File")
        filem.set_submenu(filemenu)

        agr = gtk.AccelGroup()
        self.add_accel_group(agr)

        newi = gtk.ImageMenuItem(gtk.STOCK_NEW, agr)
        key, mod = gtk.accelerator_parse("<Control>N")
        newi.add_accelerator("activate", agr, key, 
            mod, gtk.ACCEL_VISIBLE)
        filemenu.append(newi)

        openm = gtk.ImageMenuItem(gtk.STOCK_OPEN, agr)
        key, mod = gtk.accelerator_parse("<Control>O")
        openm.add_accelerator("activate", agr, key, 
            mod, gtk.ACCEL_VISIBLE)
        filemenu.append(openm)

        sep = gtk.SeparatorMenuItem()
        filemenu.append(sep)

        exit = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr)
        key, mod = gtk.accelerator_parse("<Control>Q")
        exit.add_accelerator("activate", agr, key, 
            mod, gtk.ACCEL_VISIBLE)

        exit.connect("activate", gtk.main_quit)

        filemenu.append(exit)

        mb.append(filem)

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(mb, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

我们的示例显示了具有三个子菜单项的顶级菜单项。 每个菜单项都有一个图像和一个加速器。 退出菜单项的加速器处于活动状态。

agr = gtk.AccelGroup()
self.add_accel_group(agr)

要使用加速器,我们创建一个全局AccelGroup对象。 稍后将使用。

newi = gtk.ImageMenuItem(gtk.STOCK_NEW, agr)

ImageMenuItem已创建。 图片来自图片库。

key, mod = gtk.accelerator_parse("<Control>N")

gtk.accelerator_parse()函数解析指定的加速器字符串,并返回一个 2 元组,其中包含与加速器相对应的键值和修饰符掩码。

newi.add_accelerator("activate", agr, key, 
    mod, gtk.ACCEL_VISIBLE)

这将为退出菜单项创建 Ctrl + Q 加速器。

sep = gtk.SeparatorMenuItem()
filemenu.append(sep)

这些行创建一个分隔符。 它用于将菜单项分组为逻辑组。

Image menu

图:图像 menu

CheckMenuItem

CheckMenuItem是带有复选框的菜单项。 它可以用于布尔属性。

checkmenuitem.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows how to 
# use a CheckMenuItem
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Check menu item")
        self.set_size_request(250, 200)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        mb = gtk.MenuBar()

        filemenu = gtk.Menu()
        filem = gtk.MenuItem("File")
        filem.set_submenu(filemenu)

        viewmenu = gtk.Menu()
        view = gtk.MenuItem("View")
        view.set_submenu(viewmenu)

        stat = gtk.CheckMenuItem("View Statusbar")
        stat.set_active(True)
        stat.connect("activate", self.on_status_view)
        viewmenu.append(stat)

        exit = gtk.MenuItem("Exit")
        exit.connect("activate", gtk.main_quit)
        filemenu.append(exit)

        mb.append(filem)
        mb.append(view)

        self.statusbar = gtk.Statusbar()
        self.statusbar.push(1, "Ready")

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(mb, False, False, 0)
        vbox.pack_start(gtk.Label(), True, False, 0)
        vbox.pack_start(self.statusbar, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

    def on_status_view(self, widget):
        if widget.active: 
            self.statusbar.show()
        else:
            self.statusbar.hide()

PyApp()
gtk.main()

在我们的代码示例中,我们显示一个检查菜单项。 如果该复选框已激活,则显示状态栏小部件。 如果不是,状态栏将被隐藏。

stat = gtk.CheckMenuItem("View Statusbar")

CheckMenuItem小部件已创建。

stat.set_active(True)

set_active()方法选中/取消选中检查菜单项。

if widget.active: 
    self.statusbar.show()
else:
    self.statusbar.hide()

根据CheckMenuItem的活动属性,我们显示或隐藏状态栏小部件。

CheckMenuItem

图:CheckMenuItem

子菜单

我们的最后一个示例演示了如何在 PyGTK 中创建子菜单。

submenu.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows a submenu
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Submenu")
        self.set_size_request(250, 200)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        mb = gtk.MenuBar()

        filemenu = gtk.Menu()
        filem = gtk.MenuItem("File")
        filem.set_submenu(filemenu)

        mb.append(filem)

        imenu = gtk.Menu()

        importm = gtk.MenuItem("Import")
        importm.set_submenu(imenu)

        inews = gtk.MenuItem("Import news feed...")
        ibookmarks = gtk.MenuItem("Import bookmarks...")
        imail = gtk.MenuItem("Import mail...")

        imenu.append(inews)
        imenu.append(ibookmarks)
        imenu.append(imail)

        filemenu.append(importm)

        exit = gtk.MenuItem("Exit")
        exit.connect("activate", gtk.main_quit)
        filemenu.append(exit)

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(mb, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

子菜单创建。

imenu = gtk.Menu()

子菜单是Menu

importm = gtk.MenuItem("Import")
importm.set_submenu(imenu)

它是菜单项的子菜单,它会登录到顶级文件菜单。

inews = gtk.MenuItem("Import news feed...")
ibookmarks = gtk.MenuItem("Import bookmarks...")
imail = gtk.MenuItem("Import mail...")

imenu.append(inews)
imenu.append(ibookmarks)
imenu.append(imail)

子菜单有其自己的菜单项。

Submenu

图:子菜单

在 PyGTK 编程库的这一章中,我们展示了如何使用菜单。

PyGTK 中的工具栏

原文: http://zetcode.com/gui/pygtk/toolbars/

在 PyGTK 编程教程的这一部分中,我们将使用工具栏。

菜单将我们可以在应用中使用的命令分组。 使用工具栏可以快速访问最常用的命令。

简单的工具栏

接下来,我们创建一个简单的工具栏。

toolbar.py


#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows a toolbar
# widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Toolbar")
        self.set_size_request(250, 200)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        toolbar = gtk.Toolbar()
        toolbar.set_style(gtk.TOOLBAR_ICONS)

        newtb = gtk.ToolButton(gtk.STOCK_NEW)
        opentb = gtk.ToolButton(gtk.STOCK_OPEN)
        savetb = gtk.ToolButton(gtk.STOCK_SAVE)
        sep = gtk.SeparatorToolItem()
        quittb = gtk.ToolButton(gtk.STOCK_QUIT)

        toolbar.insert(newtb, 0)
        toolbar.insert(opentb, 1)
        toolbar.insert(savetb, 2)
        toolbar.insert(sep, 3)
        toolbar.insert(quittb, 4)

        quittb.connect("clicked", gtk.main_quit)

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(toolbar, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

该示例显示了一个工具栏和四个工具按钮。

toolbar = gtk.Toolbar()

Toolbar小部件已创建。

toolbar.set_style(gtk.TOOLBAR_ICONS)

在工具栏上,我们仅显示图标。 没有文字。

newtb = gtk.ToolButton(gtk.STOCK_NEW)

创建带有库存图像的ToolButton

sep = gtk.SeparatorToolItem()

这是一个分隔符。 它可用于将工具栏按钮分组为逻辑组。

toolbar.insert(newtb, 0)
toolbar.insert(opentb, 1)
...

工具栏按钮插入到工具栏小部件中。

Toolbar

图:工具栏

工具栏

在第二个示例中,我们显示了两个工具栏。 许多应用具有多个工具栏。 我们展示了如何在 PyGTK 中做到这一点。

toolbars.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows two toolbars
# in the application window
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Toolbars")
        self.set_size_request(350, 300)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        upper = gtk.Toolbar()
        upper.set_style(gtk.TOOLBAR_ICONS)

        newtb = gtk.ToolButton(gtk.STOCK_NEW)
        opentb = gtk.ToolButton(gtk.STOCK_OPEN)
        savetb = gtk.ToolButton(gtk.STOCK_SAVE)

        upper.insert(newtb, 0)
        upper.insert(opentb, 1)
        upper.insert(savetb, 2)

        lower = gtk.Toolbar()
        lower.set_style(gtk.TOOLBAR_ICONS)

        quittb = gtk.ToolButton(gtk.STOCK_QUIT)
        quittb.connect("clicked", gtk.main_quit)
        lower.insert(quittb, 0)

        vbox = gtk.VBox(False, 0)
        vbox.pack_start(upper, False, False, 0)
        vbox.pack_start(lower, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

PyApp()
gtk.main()

我们的应用显示了两个工具栏。

upper = gtk.Toolbar()
...
lower = gtk.Toolbar()

我们创建两个Toolbar小部件。

upper.insert(newtb, 0)
...
lower.insert(quittb, 0)

它们每个都有自己的工具按钮。

vbox = gtk.VBox(False, 0)
vbox.pack_start(upper, False, False, 0)
vbox.pack_start(lower, False, False, 0)

工具栏一个接一个地包装在垂直盒中。

Toolbars

图:工具栏 s

撤销重做

以下示例演示了如何停用工具栏上的工具栏按钮。 这是 GUI 编程中的常见做法。 例如,保存按钮。 如果我们将文档的所有更改都保存到磁盘上,则在大多数文本编辑器中,“保存”按钮将被禁用。 这样,应用会向用户指示所有更改都已保存。

undoredo.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows how to 
# activate/deactivate a ToolButton
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Toolbar")
        self.set_size_request(250, 200)
        self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(6400, 6400, 6440))
        self.set_position(gtk.WIN_POS_CENTER)

        self.count = 2

        toolbar = gtk.Toolbar()
        toolbar.set_style(gtk.TOOLBAR_ICONS)

        self.undo = gtk.ToolButton(gtk.STOCK_UNDO)
        self.redo = gtk.ToolButton(gtk.STOCK_REDO)
        sep = gtk.SeparatorToolItem()
        quit = gtk.ToolButton(gtk.STOCK_QUIT)

        toolbar.insert(self.undo, 0)
        toolbar.insert(self.redo, 1)
        toolbar.insert(sep, 2)
        toolbar.insert(quit, 3)

        self.undo.connect("clicked", self.on_undo)
        self.redo.connect("clicked", self.on_redo)
        quit.connect("clicked", gtk.main_quit)

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(toolbar, False, False, 0)

        self.add(vbox)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

    def on_undo(self, widget):
        self.count = self.count - 1

        if self.count <= 0:
            self.undo.set_sensitive(False)
            self.redo.set_sensitive(True)

    def on_redo(self, widget):
        self.count = self.count + 1

        if self.count >= 5: 
            self.redo.set_sensitive(False)
            self.undo.set_sensitive(True)

PyApp()
gtk.main()

我们的示例从 PyGTK 库存资源创建撤消和重做按钮。 单击几下后,每个按钮均被禁用。 按钮显示为灰色。

self.count = 2

self.count变量决定哪个按钮被激活和禁用。

self.undo = gtk.ToolButton(gtk.STOCK_UNDO)
self.redo = gtk.ToolButton(gtk.STOCK_REDO)

我们有两个工具按钮。 撤消和重做工具按钮。 图片来自库存资源。

self.undo.connect("clicked", self.on_undo)
self.redo.connect("clicked", self.on_redo)

我们为两个工具按钮都插入了clicked信号的方法。

if self.count <= 0:
    self.undo.set_sensitive(False)
    self.redo.set_sensitive(True)

要激活小部件,我们使用set_sensitive()方法。

Undo redo

图:撤销和重做

在 PyGTK 编程库的这一章中,我们提到了工具栏。

PyGTK 中的事件和信号

原文: http://zetcode.com/gui/pygtk/signals/

在 PyGTK 编程教程的这一部分中,我们将讨论信号&事件。

所有 GUI 应用都是事件驱动的。 PyGTK 应用也不例外。 应用通过gtk.main()调用启动主循环,该循环不断检查新生成的事件。 如果没有事件,则应用将等待并且不执行任何操作。

事件是从 X 服务器到应用的消息。 当我们单击按钮小部件时,单击的信号将以发出。 有所有小部件都继承的信号,例如销毁,有特定于小部件的信号,例如在切换按钮上切换。

程序员使用信号处理器来响应各种信号。 这些处理器在 GTK 程序员中称为回调。

handler_id = button.connect("clicked", self.on_clicked)

在这里,我们使用GObject类的connect()方法((GtkButtonGObject))将回调on_clicked()连接到名为clicked的信号。

connect()方法返回一个处理器 ID,用于唯一标识回调方法。 该 ID 可以与以下方法一起使用:

def disconnect(handler_id)
def handler_disconnect(handler_id)
def handler_is_connected(handler_id)
def handler_block(handler_id)
def handler_unblock(handler_id)

这些方法使处理器可以与GObject断开连接,也可以对其进行阻止/取消阻止。

信号与事件

关于两者之间的差异,通常会有很多困惑。

信号和事件是两件事。 事件是窗口系统事件的几乎一对一的映射。 按键,调整窗口大小或按键是典型的窗口系统事件。 窗口系统事件将报告给应用主循环。 Gdk 解释窗口系统事件并通过信号传递它们。

信号就是回调机制。 如果一个对象希望收到有关另一对象的动作或状态更改的通知,它将注册一个回调。 当对象发出信号时,它会在已向其注册的回调列表中查找并调用特定信号的回调。 它可以选择发送一些预定义的数据。

信号是一个通用的通知框架。 它们不仅用于有关 UI 更改的通知。 它们可用于有关应用状态更改的通知。 信号是通用的,有力的,其用途非常广泛。 任何GObject都可以发射和接收信号。 一个类型可以具有一个或多个信号,每个信号可以具有一个参数列表和返回值。 然后可以将处理器连接到该类型的实例。 在实例上发出信号时,将调用每个连接的处理器。

信号和事件之间的唯一联系是信号用于从 X 服务器发送有关事件的通知。

信号是gtk.Object及其子类的功能,事件是 Gdk/Xlib 概念。

简单的例子

下一个示例显示了我们如何对两个基本信号做出反应。

quitbutton.py


#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# The example shows how to work with 
# destroy and clicked signals
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Quit Button")
        self.set_size_request(250, 200)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect("destroy", self.on_destroy)

        fixed = gtk.Fixed()

        quit = gtk.Button("Quit")
        quit.connect("clicked", self.on_clicked)
        quit.set_size_request(80, 35)

        fixed.put(quit, 50, 50)

        self.add(fixed)
        self.show_all()

    def on_destroy(self, widget):
        gtk.main_quit()

    def on_clicked(self, widget):
        gtk.main_quit()

PyApp()
gtk.main()

当我们关闭窗口时,破坏信号被触发。 默认情况下,当我们单击标题栏中的关闭按钮时,应用不会退出。

self.connect("destroy", self.on_destroy)

connect()方法将on_destroy()方法插入destroy信号。

quit.connect("clicked", self.on_clicked)

按下退出按钮,将触发clicked信号。 当单击退出按钮时,我们将调用on_clicked()方法。

def on_destroy(self, widget):
    gtk.main_quit()

on_destroy()方法中,我们对destroy信号做出反应。 我们调用gtk.main_quit()方法,该方法将终止应用。

def on_clicked(self, widget):
    gtk.main_quit()

这是on_clicked()方法。 它有两个参数。 widget参数是触发该信号的对象。 在我们的例子中,它是退出按钮。 不同的对象发送不同的信号。 发送到方法的信号和参数可以在 PyGTK 库的参考手册中找到。 pygtk.org/docs/pygtk/index.html

创建自定义信号

在下面的代码示例中,我们创建并发送一个自定义信号。

customsignal.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows how to create
# and send a custom singal
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gobject

class Sender(gobject.GObject):
    def __init__(self):
        self.__gobject_init__()

gobject.type_register(Sender)
gobject.signal_new("z_signal", Sender, gobject.SIGNAL_RUN_FIRST,
                   gobject.TYPE_NONE, ())

class Receiver(gobject.GObject):
    def __init__(self, sender):
        self.__gobject_init__()

        sender.connect('z_signal', self.report_signal)

    def report_signal(self, sender):
        print "Receiver reacts to z_signal"

def user_callback(object):
    print "user callback reacts to z_signal"

if __name__ == '__main__':

    sender = Sender()
    receiver = Receiver(sender)

    sender.connect("z_signal", user_callback)
    sender.emit("z_signal")

我们创建两个GObjects。 发送方和接收方对象。 发送方发出一个信号,该信号被接收方对象接收。 我们还会在信号中插入回调。

class Sender(gobject.GObject):
    def __init__(self):
        self.__gobject_init__()

这是一个发送者对象。 它是使用默认构造器创建的。

gobject.type_register(Sender)
gobject.signal_new("z_signal", Sender, gobject.SIGNAL_RUN_FIRST,
                  gobject.TYPE_NONE, ())

我们注册一个新对象和一个新信号。 signal_new()函数为发件人对象注册一个名为z_signal的信号。 SIGNAL_RUN_FIRST参数意味着接收信号的对象的默认处理器首先调用。 最后两个参数是返回值类型和参数类型。 在我们的示例中,我们不返回任何值,也不发送任何参数。

sender.connect('z_signal', self.report_signal)

接收器监听z_signal

sender = Sender()
receiver = Receiver(sender)

实例化发送者和接收者对象。 接收方将发送方作为参数,以便可以监听其信号。

sender.connect("z_signal", user_callback)

在这里,我们将信号插入用户回调。

sender.emit("z_signal")

z_signal正在发射。

class Sender(gobject.GObject):

    __gsignals__ = {
        'z_signal': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
    }

    def __init__(self):
        self.__gobject_init__() 

gobject.type_register(Sender)

我们还可以使用__gsignals__类属性来注册新信号。

预定义的信号处理器

PyGTK 中的对象可能具有预定义的信号处理器。 这些处理器以do_*开头。 例如do_expose()do_show()do_clicked()

move.py


#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example overrides predefined
# do_configure_event() signal handler
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk
import gobject

class PyApp(gtk.Window):
    __gsignals__ = {
        "configure-event" : "override"
        }

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_size_request(200, 150)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        self.show_all()

    def do_configure_event(self, event):

        title = "%s, %s" % (event.x, event.y)
        self.set_title(title)
        gtk.Window.do_configure_event(self, event)

PyApp()
gtk.main()

当我们移动窗口或调整窗口大小时,X 服务器将发送配置事件。 然后将它们转换为configure-event信号。

在我们的代码示例中,我们在标题栏中显示窗口左上角的 x,y 坐标。 我们可以简单地将信号处理器连接到configure-event信号。 但是我们采取了不同的策略。 我们重写默认的类处理器,在其中实现所需的逻辑。

__gsignals__ = {
    "configure-event" : "override"
    }

这表明我们将覆盖默认的on_configure_event()方法。

def do_configure_event(self, event):

    title = "%s, %s" % (event.x, event.y)
    self.set_title(title)
    gtk.Window.do_configure_event(self, event)

在这里,我们将覆盖预定义的do_configure_event()方法。 我们将窗口的 x,y 坐标设置为窗口的标题。 另请注意最后一行。 它显式调用超类do_configure_event()方法。 这是因为它做了一些重要的工作。 尝试对此行添加注释以查看发生了什么。 调整窗口大小无法正常工作。 如果重写默认处理器,则可能会或可能不会调用超类方法。 就我们而言,我们必须这样做。

Configure signal

图:配置信号

按钮的信号

以下示例显示了各种按钮信号。

buttonsignals.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This program shows various signals 
# of a button widget
# It emits a button-release-event which
# triggers a released singal
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Signals")
        self.set_size_request(250, 200)
        self.set_position(gtk.WIN_POS_CENTER)
        self.connect("destroy", gtk.main_quit)

        fixed = gtk.Fixed()

        self.quit = gtk.Button("Quit")

        self.quit.connect("pressed", self.on_pressed)
        self.quit.connect("released", self.on_released)
        self.quit.connect("clicked", self.on_clicked)

        self.quit.set_size_request(80, 35)

        fixed.put(self.quit, 50, 50)

        self.add(fixed)
        self.show_all()
        self.emit_signal()

    def emit_signal(self):

        event = gtk.gdk.Event(gtk.gdk.BUTTON_RELEASE)
        event.button = 1
        event.window = self.quit.window
        event.send_event = True

        self.quit.emit("button-release-event", event)

    def on_clicked(self, widget):
        print "clicked"

    def on_released(self, widget):
        print "released"

    def on_pressed(self, widget):
        print "pressed"

PyApp()
gtk.main()

一个按钮不仅可以发出一种信号。 我们与其中三个一起工作。 clickedpressedreleased信号。 我们还展示了事件信号如何触发另一个信号。

self.quit.connect("pressed", self.on_pressed)
self.quit.connect("released", self.on_released)
self.quit.connect("clicked", self.on_clicked)

我们为所有三个信号注册回调。

self.emit_signal()

在应用启动时,我们发出特定信号。

def emit_signal(self):

    event = gtk.gdk.Event(gtk.gdk.BUTTON_RELEASE)
    event.button = 1
    event.window = self.quit.window
    event.send_event = True

    self.quit.emit("button-release-event", event)

我们发出button-release-event信号。 它以Event对象为参数。 应用启动后,我们应该在控制台窗口中看到"released"文本。 当我们点击按钮时,所有三个信号都被触发。

阻止事件处理器

我们可以阻止信号处理器。 下一个示例显示了这一点。

block.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example shows how to block/unblock
# a signal handler
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Blocking a callback")
        self.set_size_request(250, 180)
        self.set_position(gtk.WIN_POS_CENTER)

        fixed = gtk.Fixed()
        button = gtk.Button("Click")
        button.set_size_request(80, 35)
        self.id = button.connect("clicked", self.on_clicked)
        fixed.put(button, 30, 50)

        check = gtk.CheckButton("Connect")
        check.set_active(True)
        check.connect("clicked", self.toggle_blocking, button)
        fixed.put(check, 130, 50)

        self.connect("destroy", gtk.main_quit)

        self.add(fixed)
        self.show_all()

    def on_clicked(self, widget):
        print "clicked"

    def toggle_blocking(self, checkbox, button):
        if checkbox.get_active():
           button.handler_unblock(self.id)
        else:
           button.handler_block(self.id)

PyApp()
gtk.main()

在代码示例中,我们有一个按钮和一个复选框。 当我们单击按钮并且复选框处于活动状态时,我们在控制台中显示"clicked"文本。 复选框从按钮clicked信号中阻止/取消处理器方法。

self.id = button.connect("clicked", self.on_clicked)

connect()方法返回处理器 ID。 此 ID 用于阻止和取消阻止处理器。

def toggle_blocking(self, checkbox, button):
    if checkbox.get_active():
       button.handler_unblock(self.id)
    else:
       button.handler_block(self.id)

这些行使用适当的方法阻止和取消阻止回调。

blocking a callback

图:阻止回调

在 PyGTK 教程的这一章中,我们处理了信号。

PyGTK 中的小部件

原文: http://zetcode.com/gui/pygtk/widgets/

在 PyGTK 编程教程的这一部分中,我们将介绍一些 PyGTK 小部件。

小部件是 GUI 应用的基本构建块。 多年来,几个小部件已成为所有 OS 平台上所有工具包中的标准。 例如,按钮,复选框或滚动条。 PyGTK 工具箱的理念是将小部件的数量保持在最低水平。 将创建更多专门的小部件作为自定义 PyGTK 小部件。

Label

Label小部件显示有限数量的只读文本。

label.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example demonstrates the Label widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

lyrics = """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 sniff 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 men 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"""

class PyApp(gtk.Window): 
    def __init__(self):
        super(PyApp, self).__init__()

        self.set_position(gtk.WIN_POS_CENTER)
        self.set_border_width(8)
        self.connect("destroy", gtk.main_quit)
        self.set_title("You know I'm no Good")

        label = gtk.Label(lyrics)
        self.add(label)
        self.show_all()

PyApp()
gtk.main()

该代码示例在窗口上显示了一些歌词。

lyrics = """Meet you downstairs in the bar and heard
your rolled up sleeves and your skull t-shirt
..."""

这是我们显示的文本。

self.set_border_width(8)

Label周围有一些空白。

label = gtk.Label(lyrics)
self.add(label)

Label小部件已创建并添加到窗口。

Label Widget

图:Label小部件

CheckButton

CheckButton是具有两种状态的窗口小部件:打开和关闭。 n 状态通过复选标记显示。 它用来表示一些布尔属性。

checkbutton.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example demonstrates the CheckButton widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()
        self.set_title("Check Button")
        self.set_position(gtk.WIN_POS_CENTER)
        self.set_default_size(250, 200)

        fixed = gtk.Fixed()
        button = gtk.CheckButton("Show title")
        button.set_active(True)
        button.unset_flags(gtk.CAN_FOCUS)
        button.connect("clicked", self.on_clicked)

        fixed.put(button, 50, 50)

        self.connect("destroy", gtk.main_quit)
        self.add(fixed)
        self.show_all()

    def on_clicked(self, widget):
        if widget.get_active():
            self.set_title("Check Button")
        else:
           self.set_title("")

PyApp()
gtk.main()

根据CheckButton的状态,我们将在窗口的标题栏中显示标题。

button = gtk.CheckButton("Show title")

CheckButton小部件已创建。

button.set_active(True)

默认情况下标题是可见的,因此我们默认情况下选中复选按钮。

if widget.get_active():
    self.set_title("Check Button")
else:
    self.set_title("")

如果选中CheckButton,我们将显示标题。 否则,我们将在标题栏中放置空白文本。

CheckButton

图:CheckButton

ComboBox

ComboBox是一个小部件,允许用户从选项列表中进行选择。

combobox.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example demonstrates the ComboBox widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):
    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("ComboBox")
        self.set_default_size(250, 200)
        self.set_position(gtk.WIN_POS_CENTER)

        cb = gtk.combo_box_new_text()
        cb.connect("changed", self.on_changed)

        cb.append_text('Ubuntu')
        cb.append_text('Mandriva')
        cb.append_text('Redhat')
        cb.append_text('Gentoo')
        cb.append_text('Mint')

        fixed = gtk.Fixed()
        fixed.put(cb, 50, 30)
        self.label = gtk.Label("-")
        fixed.put(self.label, 50, 140)
        self.add(fixed)

        self.connect("destroy", gtk.main_quit)
        self.show_all()

    def on_changed(self, widget):
        self.label.set_label(widget.get_active_text()) 

PyApp()
gtk.main()

该示例显示了一个组合框和一个标签。 组合框具有六个选项的列表。 这些是 Linux 发行版的名称。 标签窗口小部件显示了从组合框中选择的选项。

cb = gtk.combo_box_new_text()

gtk.combo_box_new_text()函数是一种便捷函数,可构造一个新的文本组合框。 它是只显示字符串的ComboBox

cb.append_text('Ubuntu')
cb.append_text('Mandriva')
cb.append_text('Redhat')
cb.append_text('Gentoo')
cb.append_text('Mint')

ComboBox充满了文本数据。

self.label.set_label(widget.get_active_text()) 

on_changed()方法内部,我们从组合框中获取选定的文本并将其设置为标签。

ComboBox

图:ComboBox

Image

下一个示例介绍Image小部件。 此小部件显示图片。

image.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This example demonstrates the Image widget
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()

        self.set_title("Red Rock")
        self.set_position(gtk.WIN_POS_CENTER)
        self.set_border_width(2)

        image = gtk.Image()
        image.set_from_file("redrock.png")

        self.connect("destroy", gtk.main_quit)
        self.add(image)
        self.show_all()

PyApp()
gtk.main()

我们在窗口中显示红色岩石城堡。

image = gtk.Image()

Image小部件已创建。

image.set_from_file("redrock.png")

我们将 PNG 图像设置为Image小部件。 图片是从磁盘上的文件加载的。

Image

图:图像

在本章中,我们展示了 PyGTK 编程库的第一组基本小部件。

PyQt5 小部件

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

小部件是应用的基本构建块。 PyQt5 有各种各样的小部件,包括按钮,复选框,滑块或列表框。 在本教程的这一部分中,我们将描述几个有用的小部件:QCheckBox,处于切换模式的QPushButtonQSliderQProgressBarQCalendarWidget

QCheckBox

QCheckBox是具有两种状态的窗口小部件:开和关。 这是一个带有标签的盒子。 复选框通常用于表示可以启用或禁用的应用中的功能。

checkbox.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, a QCheckBox widget
is used to toggle the title of a window.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import QWidget, QCheckBox, QApplication
from PyQt5.QtCore import Qt
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        cb = QCheckBox('Show title', self)
        cb.move(20, 20)
        cb.toggle()
        cb.stateChanged.connect(self.changeTitle)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('QCheckBox')
        self.show()

    def changeTitle(self, state):

        if state == Qt.Checked:
            self.setWindowTitle('QCheckBox')
        else:
            self.setWindowTitle(' ')

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们将创建一个复选框,以切换窗口标题。

cb = QCheckBox('Show title', self)

这是一个QCheckBox构造器。

cb.toggle()

我们已经设置了窗口标题,因此我们也选中了该复选框。

cb.stateChanged.connect(self.changeTitle)

我们将用户定义的changeTitle()方法连接到stateChanged信号。 changeTitle()方法将切换窗口标题。

def changeTitle(self, state):

    if state == Qt.Checked:
        self.setWindowTitle('QCheckBox')
    else:
        self.setWindowTitle(' ')

小部件的状态在state变量中提供给changeTitle()方法。 如果小部件被选中,我们设置窗口的标题。 否则,我们在标题栏设置一个空字符串。

QCheckBox

图:QCheckBox

ToggleButton

切换按钮是特殊模式下的QPushButton。 它是具有两种状态的按钮:已按下和未按下。 我们通过单击在这两种状态之间切换。 在某些情况下此功能非常合适。

togglebutton.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

In this example, we create three toggle buttons.
They will control the background color of a 
QFrame. 

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QPushButton, 
    QFrame, QApplication)
from PyQt5.QtGui import QColor
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        self.col = QColor(0, 0, 0)       

        redb = QPushButton('Red', self)
        redb.setCheckable(True)
        redb.move(10, 10)

        redb.clicked[bool].connect(self.setColor)

        greenb = QPushButton('Green', self)
        greenb.setCheckable(True)
        greenb.move(10, 60)

        greenb.clicked[bool].connect(self.setColor)

        blueb = QPushButton('Blue', self)
        blueb.setCheckable(True)
        blueb.move(10, 110)

        blueb.clicked[bool].connect(self.setColor)

        self.square = QFrame(self)
        self.square.setGeometry(150, 20, 100, 100)
        self.square.setStyleSheet("QWidget { background-color: %s }" %  
            self.col.name())

        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle('Toggle button')
        self.show()

    def setColor(self, pressed):

        source = self.sender()

        if pressed:
            val = 255
        else: val = 0

        if source.text() == "Red":
            self.col.setRed(val)                
        elif source.text() == "Green":
            self.col.setGreen(val)             
        else:
            self.col.setBlue(val) 

        self.square.setStyleSheet("QFrame { background-color: %s }" %
            self.col.name())  

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们创建了三个切换按钮和一个QWidget。 我们将QWidget的背景色设置为黑色。 切换按钮将切换颜色值的红色,绿色和蓝色部分。 背景颜色取决于按下的切换按钮。

self.col = QColor(0, 0, 0)    

这是初始的黑色值。

redb = QPushButton('Red', self)
redb.setCheckable(True)
redb.move(10, 10)

要创建一个切换按钮,我们创建一个QPushButton并通过调用setCheckable()方法使其可检查。

redb.clicked[bool].connect(self.setColor)

我们将clicked信号连接到用户定义的方法。 我们使用以布尔值操作的clicked信号。

source = self.sender()

我们得到被切换的按钮。

if source.text() == "Red":
    self.col.setRed(val)   

如果它是红色按钮,我们将相应地更新颜色的红色部分。

self.square.setStyleSheet("QFrame { background-color: %s }" %
    self.col.name())   

我们使用样式表来更改背景颜色。 样式表使用setStyleSheet()方法更新。

Toggle button

图:开关按钮

QSlider

QSlider是具有简单句柄的小部件。 该手柄可以前后拉动。 这样,我们可以为特定任务选择一个值。 有时使用滑块比输入数字或使用旋转框更自然。

在我们的示例中,我们将显示一个滑块和一个标签。 标签将显示图像。 滑块将控制标签。

slider.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This example shows a QSlider widget.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QSlider, 
    QLabel, QApplication)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        sld = QSlider(Qt.Horizontal, self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setGeometry(30, 40, 100, 30)
        sld.valueChanged[int].connect(self.changeValue)

        self.label = QLabel(self)
        self.label.setPixmap(QPixmap('mute.png'))
        self.label.setGeometry(160, 40, 80, 30)

        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle('QSlider')
        self.show()

    def changeValue(self, value):

        if value == 0:
            self.label.setPixmap(QPixmap('mute.png'))
        elif value > 0 and value <= 30:
            self.label.setPixmap(QPixmap('min.png'))
        elif value > 30 and value < 80:
            self.label.setPixmap(QPixmap('med.png'))
        else:
            self.label.setPixmap(QPixmap('max.png'))

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())             

在我们的示例中,我们模拟了音量控制。 通过拖动滑块的手柄,我们可以更改标签上的图像。

sld = QSlider(Qt.Horizontal, self)

在这里,我们创建一个水平QSlider

self.label = QLabel(self)
self.label.setPixmap(QPixmap('mute.png'))

我们创建一个QLabel小部件并为其设置初始静音图像。

sld.valueChanged[int].connect(self.changeValue)

我们将valueChanged信号连接到用户定义的changeValue()方法。

if value == 0:
    self.label.setPixmap(QPixmap('mute.png'))
...

基于滑块的值,我们将图像设置为标签。 在上面的代码中,如果滑块等于 0,则将mute.png图像设置为标签。

QSlider widget

图:QSlider小部件

QProgressBar

进度条是在处理冗长的任务时使用的小部件。 它具有动画效果,以便用户知道任务正在进行中。 QProgressBar小部件在 PyQt5 工具包中提供了水平或垂直进度条。 程序员可以为进度条设置最小值和最大值。 默认值为 0 和 99。

progressbar.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This example shows a QProgressBar widget.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QProgressBar, 
    QPushButton, QApplication)
from PyQt5.QtCore import QBasicTimer
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        self.pbar = QProgressBar(self)
        self.pbar.setGeometry(30, 40, 200, 25)

        self.btn = QPushButton('Start', self)
        self.btn.move(40, 80)
        self.btn.clicked.connect(self.doAction)

        self.timer = QBasicTimer()
        self.step = 0

        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle('QProgressBar')
        self.show()

    def timerEvent(self, e):

        if self.step >= 100:

            self.timer.stop()
            self.btn.setText('Finished')
            return

        self.step = self.step + 1
        self.pbar.setValue(self.step)

    def doAction(self):

        if self.timer.isActive():
            self.timer.stop()
            self.btn.setText('Start')
        else:
            self.timer.start(100, self)
            self.btn.setText('Stop')

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在我们的示例中,我们有一个水平进度条和一个按钮。 该按钮将启动和停止进度条。

self.pbar = QProgressBar(self)

这是一个QProgressBar构造器。

self.timer = QBasicTimer()

要激活进度条,我们使用一个计时器对象。

self.timer.start(100, self)

要启动计时器事件,我们调用其start()方法。 此方法有两个参数:超时和将接收事件的对象。

def timerEvent(self, e):

    if self.step >= 100:

        self.timer.stop()
        self.btn.setText('Finished')
        return

    self.step = self.step + 1
    self.pbar.setValue(self.step)

每个QObject及其子代都有一个timerEvent()事件处理器。 为了对计时器事件做出反应,我们重新实现了事件处理器。

def doAction(self):

    if self.timer.isActive():
        self.timer.stop()
        self.btn.setText('Start')

    else:
        self.timer.start(100, self)
        self.btn.setText('Stop')

doAction()方法中,我们启动和停止计时器。

QProgressBar

图:QProgressBar

QCalendarWidget

QCalendarWidget提供基于月度的日历小部件。 它允许用户以简单直观的方式选择日期。

calendar.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
ZetCode PyQt5 tutorial 

This example shows a QCalendarWidget widget.

Author: Jan Bodnar
Website: zetcode.com 
Last edited: August 2017
"""

from PyQt5.QtWidgets import (QWidget, QCalendarWidget, 
    QLabel, QApplication, QVBoxLayout)
from PyQt5.QtCore import QDate
import sys

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        vbox = QVBoxLayout(self)

        cal = QCalendarWidget(self)
        cal.setGridVisible(True)
        cal.clicked[QDate].connect(self.showDate)

        vbox.addWidget(cal)

        self.lbl = QLabel(self)
        date = cal.selectedDate()
        self.lbl.setText(date.toString())

        vbox.addWidget(self.lbl)

        self.setLayout(vbox)

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Calendar')
        self.show()

    def showDate(self, date):     

        self.lbl.setText(date.toString())

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

该示例具有日历小部件和标签小部件。 当前选择的日期显示在标签窗口小部件中。

cal = QCalendarWidget(self)

QCalendarWidget已创建。

cal.clicked[QDate].connect(self.showDate)

如果我们从小部件中选择一个日期,则会发出clicked[QDate]信号。 我们将此信号连接到用户定义的showDate()方法。

def showDate(self, date):     

    self.lbl.setText(date.toString())

我们通过调用selectedDate()方法检索所选日期。 然后,我们将日期对象转换为字符串并将其设置为标签小部件。

在 PyQt5 教程的这一部分中,我们介绍了以下小部件:QCheckBoxQPushButton(处于切换模式),QSliderQProgressBarQCalendarWidget

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