加载中

TL;DR — Writing native cross-platform desktop GUIs using Red.

After trying (and failing) to wrap “executable” ducktape around a Python script using PyInstaller on Windows, The class of programming languages which now usually pique my interest have the following attributes:

  • Static binary compilation. (producing a binary, not running bytecode i.e Java, Python)

  • Fast.

  • Cross Platform. (Windows, Mac, Linux, *BSD and/or Haiku (Bonus) )

Having picked up Go three years ago for this purpose, (started developing an app with it)I can guarantee that Go binaries work across multiple platforms through cross compilation. Other languages such as Crystal and Rust also tick the boxes above, but both were not stable at the time. Today, I’m exploring options for native cross platform GUIs1.

There have been lots of “modern desktop applications” being built with Electron. This allows programmers to build desktop applications with HTML, CSS and JavaScript. Sounds awesome right? Well…

While Electron is easier, faster to market and cost effective for programmers, Electron is notorious for the sheer amount of bloat it carries in binary size and in RAM usage2. You may have come across posts lamenting the use of it, to the point that bashing Electron is almost guaranteed in an online tech forum if any application dares to use it.

To your disappointment, this is not one of these posts. Qt and GTK+ are interesting alternatives, I even plan to use them myself. However, both are not as quick to develop in as Electron is. Can we have both? A native cross platform GUI that is also faster to develop in? Enter Red.

Too long; Don't read(太长,请不要看) — 使用 Red 语言编写原生跨平台桌面 GUI。

当尝试(并失败)在 Windows 上使用 PyInstaller 将“可执行”的 ducktape 封装在 Python 脚本之后,现在通常激起我兴趣的编程语言类具有以下属性:

  • 静态二进制编译(产生一个二进制,不是运行时的字节码,例如 Java、Python)

  • 快速

  • 跨平台(Windows,Mac,Linux,* BSD 和/或 Haiku (Bonus))

为了达到这个目的,在三年前我学会了 Go开始使用 Go 开发一个应用程序),我可以确定 Go 的二进制文件可以通过交叉编译在多个平台上运行。其他语言如 Crystal 和 Rust 也支持类似机制,但当时都不稳定。今天,我正在探索原生跨平台GUIs1的可选方案。

基于 Electron 构建的“现代桌面应用”已经有很多了。它允许程序员用 HTML、CSS 和 JavaScript 来构建桌面应用程序。听起来真棒吧?好吧…

虽然 Electron 比较容易,快速面世并且对程序员而言成本效益更高,Electron 以二进制大小和内存使用量庞大2而臭名昭着。你可能遇到过唱衰使用它的帖子,基本上到了这种程度:如果任何应用程序敢于使用它,几乎可以保证它会在在线技术论坛上遭受抨击。

令你失望的是,这不是其中抨击帖子之一。Qt 和 GTK+ 是有趣的选择,我甚至打算自己使用它们。但是,两者都不如 Electron 那样快速发展。我们可以兼用二者吗?一个原生的可快速开发的跨平台GUI?不妨看看 Red

Red

Red is a next-generation programming language strongly inspired by Rebol.”

Red was made largely because Rebol was proprietary, closed source software (That is until in 2012, Rebol 3 was made open source). Both languages have a great GUI system (which we will get into), the difference Red makes is that it can be used for systems programmming with a DSL called Red/System and the compiler is amazingly less than 2MB. You can find out more about Red here and you can download it here.

Red

Red 是受到 Rebol 强烈影响的下一代编程语言。”

Red 的诞生很大程度是受 Rebol 的影响,它是专有的封闭源码软件(直到2012年,Rebol 3才成为开源软件)。这两种语言都有一个很好的图形用户界面系统(下面我们将介绍),Red 的不同之处在于它可以用于系统编程,使用名为 Red/System 的小于2MB DSL 编译器。你可以在这里找到Red,并下载

GUI

Here is a “Hello World” using Red’s GUI system.

Red comes with an interpreter, so you can go ahead and run the code below by typing red hello_world_gui.red

Red []
view [ text "Hello World" ]

Red will interpret this code and run it without compiling it to a binary. If you want to produce a binary, amend the code to the following:

Red [ needs: 'View ]
view [ text "Hello World" ]

and run:

red -r -c hello_world_gui.red

The result of this compilation is a single binary that shows the words “Hello World” in a GUI. GUI support is not currently available on Linux yet (it’s experimental), but compiling this works on Windows and Mac. Red supports cross compilation so that you can compile binaries for a different platform, We can get a list of the available cross compilation targets that Red can compile to.

hako ~ red -h
...
Cross-compilation targets:

    MSDOS        : Windows, x86, console (+ GUI) applications
    Windows      : Windows, x86, GUI applications
    WindowsXP    : Windows, x86, GUI applications, no touch API
    Linux        : GNU/Linux, x86
    Linux-ARM    : GNU/Linux, ARMv5, armel (soft-float)
    RPi          : GNU/Linux, ARMv5, armhf (hard-float)
    Darwin       : macOS Intel, console or GUI applications
    macOS        : macOS Intel, GUI-only, applications bundles
    Syllable     : Syllable OS, x86
    FreeBSD      : FreeBSD, x86
    Android      : Android, ARMv5
    Android-x86  : Android, x86

GUI

这有一个使用 Red GUI 系统的 “Hello World”。

Red 带有解释器,所以你可以输入 red hello_world_gui.red 来运行下面的代码

Red []
view [ text "Hello World" ]

Red 会解释运行这段代码,不需要编译成二进制。如果你希望能生成二进制,就把代码修改成下面这样:

Red [ needs: 'View ]
view [ text "Hello World" ]

然后运行:

red -r -c hello_world_gui.red

编译结果是一个单独的二进制文件,运行它会在 GUI 中显示“Hello World”。目前在 Linux 下还不支持 GUI还在实验阶段),但是在 Windows 和 Mac 都是可以编译的。Red 支持交叉编译,以便你为不同的平台编译二进制文件,我们可以看到 Red 能编译出可用于哪些平台的结果。

hako ~ red -h
...
Cross-compilation targets:

    MSDOS        : Windows, x86, console (+ GUI) applications
    Windows      : Windows, x86, GUI applications
    WindowsXP    : Windows, x86, GUI applications, no touch API
    Linux        : GNU/Linux, x86
    Linux-ARM    : GNU/Linux, ARMv5, armel (soft-float)
    RPi          : GNU/Linux, ARMv5, armhf (hard-float)
    Darwin       : macOS Intel, console or GUI applications
    macOS        : macOS Intel, GUI-only, applications bundles
    Syllable     : Syllable OS, x86
    FreeBSD      : FreeBSD, x86
    Android      : Android, ARMv5
    Android-x86  : Android, x86

A “Hello World” isn’t that useful though, let’s go further with a simple image viewer where we get a random image, here is the code:

Red [
	title: "Simple Image Viewer"
	author: "Wesley Hill"
	version: 0.1
	needs: 'View
]

rand_img: https://source.unsplash.com/random/310x200
message: "Press Random for a random image!"

view/options [title "Simple Image Viewer"
	 below center
	 pictures: image 310x200 message
	 across
	 random_btn: button "Random" [
		new_image: load rand_img
		pictures/image: new_image
		pictures/text: ""
	]
	 text "A Simple Image Viewer in Red"
][]

The two variables rand_img and message are created above, and we use the view function to invoke Red’s VID dialect and View Engine, we set our title and layout to being in the center of the window. Our images will be of size 310x200 using the pair datatype and we set our button random_btn and text to be across from one another. Finally, once we click the “Random” button, we use the load function to retrieve data from a source. The source needs to be a path, which can be a filestringbinary or url datatype.

In this case we are using the url datatype from rand_img and we set our image with pictures/image: to our new image held in new_image. We only want our image to be displayed, so we set the text in the image to be empty.

We can inspect the datatype with the type? built-in function. Note that Red has over 50+ types.

>> print type? rand_img
url
>> print type? message
string
>>

Once we run the code above we get our simple image viewer!

…and it also works on Windows

Red can be used for more advanced applications, here are a few:

编写一个“Hello World”并没什么用,让我们通过实现一个简单的图像查看器以进一步了解 Red 语言,在这里我们将得到一个随机的图像,这里是代码:

Red [
	title: "Simple Image Viewer"
	author: "Wesley Hill"
	version: 0.1
	needs: 'View
]

rand_img: https://source.unsplash.com/random/310x200
message: "Press Random for a random image!"

view/options [title "Simple Image Viewer"
	 below center
	 pictures: image 310x200 message
	 across
	 random_btn: button "Random" [
		new_image: load rand_img
		pictures/image: new_image
		pictures/text: ""
	]
	 text "A Simple Image Viewer in Red"
][]

上面的代码创建了两个变量rand_img和message,我们使用view函数来调用Red的 VID dialect 和 View Engine,我们把标题和布局设置在窗口的中心。我们的图像大小为310x200,使用了 pair 数据类型,并且将我们的按钮 random_btn 和文本设置为相互响应的。 最后,一旦我们点击“随机”按钮,我们会使用加载函数从源检索数据。 源需要提供一个路径,可以是文件,字符串,二进制或url数据类型。

在这种情况下,我们使用rand_img中的url数据类型,并通过将 pictures/image: 的值设置为新的图像存放地址 new_image 以使用图片。因为我们只希望显示图像,所以我们将图像中的文本设置为空。

我们可以通过内置的 type? 功能检查数据类型。请注意,Red有超过50多种类型

>> print type? rand_img
url
>> print type? message
string
>>

一旦我们运行上面的代码,我们将得到简单的图像浏览器

...它也能运行于 Windows 上

Red 可用于更高级的应用程序,这里有几个例子:

Automated Photo Copier

For a more advanced application using Red, I wrote a program for my Dad to deal with moving hundreds of photos. Below is the GUI of the program in action, I’m using a collection of random photos to demonstrate the program.

These demo photos are tiny (~5KB on average), and the photos my Dad works with are usually around ~14MB per photo in a sea of thousands of photos.

自动照片复印机

对于使用 Red 的更高级的应用程序,我给我父亲编写了一个程序来处理数百张照片。下面是程序的 GUI,我使用了一组随机照片来演示程序

这些演示照片很小(平均约 5KB),我父亲的照片,其大小通常在 14MB 左右,数量上千。

Parsing the data

The input data is actually an email containing a list of photos that the client has selected.

Selected the following images:

BIRTHDAY PARTY > ABC 7453
TIF_FILE_ID: 0123456789

BIRTHDAY PARTY > ABC 7454
TIF_FILE_ID: 0123456790

...

BIRTHDAY PARTY > ABC 8217
TIF_FILE_ID: 0123456899

The filename of the selected photos takes the form of ABC_1234.jpg. So it would make sense to use Regular Expressions for this problem. Instead, Red has it’s own pattern matching parser originally invented by Rebol called PARSE.

The following regular expression below…

[A-Z]{3,}\s\d\S{3,}

…is similar to the following PARSE rule:

[some letter space 4 digit opt letter]

Given the following rules of letter and digit:

letter: charset [#"A" - #"Z"]
digit:  charset "0123456789"

The reason why opt letter is included in the parse rule, is to catch the ABC 1234A optional edgecase which sometimes appears in the email. Below is how the parser sees this rule:

Using the Red interpreter, we can test that the parse rule is working. For successful matches PARSE returns true and false otherwise.

>> parse "ABC 1234" [some letter space 4 digit]
== true
>> parse "ABC 1234A" [some letter space 4 digit opt letter]
== true
>> parse "ABC 1234AB" [some letter space 4 digit opt letter]
== false

PARSE is a Rebol/Red specific alternative to the now universal regular expression syntax which most if not all experienced programmers are familiar with. It took me a while to learn the syntax and I appreciate the fact that the rules are in plain readable english unlike regex.

Writing this app took me a weekend after prototyping and testing it in Python, it is < 220 lines of code. My Dad uses a Mac, so generating a binary is as easy as compiling the source in release mode for macOS.

red -r -o "APC" -t macOS apc.red --red-only -v 4

and it’s not even 2MB and it. just. works.™

解析数据

输入的数据实际上是一封电子邮件,其中包含客户选择的照片列表。

Selected the following images:

BIRTHDAY PARTY > ABC 7453
TIF_FILE_ID: 0123456789

BIRTHDAY PARTY > ABC 7454
TIF_FILE_ID: 0123456790

...

BIRTHDAY PARTY > ABC 8217
TIF_FILE_ID: 0123456899

所选照片的文件名采用 ABC_1234.jpg 的形式。所以对这个问题使用正则表达式是有用的。此外,Red 有自己的模式匹配解析器,最初由 Rebol 发明,叫做 PARSE

下面的正则表达式...

[A-Z]{3,}\s\d\S{3,}

...类似于下面的 PARSE 规则:

[some letter space 4 digit opt letter]

给予以下字母和数字的规则:

letter: charset [#"A" - #"Z"]
digit:  charset "0123456789"

解析规则中包含了选择字母的原因是要捕获有时出现在电子邮件中的 ABC 1234A 可选的边框。 以下是介绍解析器如何看到这个规则:

使用 Red 解释器,我们可以测试解析规则是否正在工作。对于成功的匹配返回 true,否则返回 false。

>> parse "ABC 1234" [some letter space 4 digit]
== true
>> parse "ABC 1234A" [some letter space 4 digit opt letter]
== true
>> parse "ABC 1234AB" [some letter space 4 digit opt letter]
== false

PARSE 是 Rebol/Red 特有的替代现在通用的正则表达式语法,大多数程序员(如果不是有经验的程序员)都不太熟悉。 我花了一段时间来学习语法,我很欣赏这种语法,这些规则是用普通的英文,而不像正则表达式。

在用 Python 编写原型和测试之后花费了我一个周末的时间编写这个应用程序,它的代码<220行。我的父亲使用 Mac,所以生成一个二进制文件就像在 macOS 的发行版中编译源代码一样简单。

red -r -o "APC" -t macOS apc.red --red-only -v 4

它的大小甚至不到 2MB。

Lines Clone

To demonstrate Red’s networking and serialisation capabilities, I re-created an app I had on my phone called Lines, which is a simple London tube status app. Unfortunately it’s not available on the App Store anymore, but I still have it on my old iPhone. The image on the right shows what it looks like.

Below is the same app implemented in Red. Clicking on the “Lines” will pop open information about a particular tube line.

Currently Red has no built-in JSON parser. I found a JSON parser floating around that was good enough for this demonstration. Building this app took around 3 days of free time to make and is around 897KB in size.

Lines 克隆

为了演示 Red 的网络和序列化功能,我重新创建了一个名为 Lines 的应用程序,它是一个简单的伦敦地铁状态应用程序。不幸的是,它在 App Store 上已经无法访问了,但我仍旧在旧的 iPhone 上安装过。下面的图片显示了它的样子。

下面是用 Red 实现的相同的应用程序。 点击“Lines”将弹出打开关于特定路线的信息。

目前 Red 没有内置的 JSON 解析器。我发现一个在这个示例中已经足够用的 JSON 解析器。构建这个应用程序需要大约 3 天的空闲时间,大小约为 897 KB。

Closing Thoughts

Apart from the GUI system which could do with a Linux implementation (A GTK3 backend is in the works for Red), be aware that Red currently compiles binaries in 32bit. With the Automated Photo Copier app I had problems when I was copying large photos using Red and often the program just crashed all of a sudden. I switched to using cp to copy the photos (Since I knew that my Dad was only using a Mac) by calling call/console to the cpcommand. At that point it copies the images flawlessly. This would be a different story had he used Windows instead.

Is Red a good alternative to Electron?, If you’re building a small internal tool that needs a quick GUI or need a cross platform GUI with less development headaches, then yes. It isn’t production ready yet, but Red has a lot of huge potential in the future looking at their roadmap. I have to admit the learning curve is slightly moderate if you are not familiar with Rebol (resources and links below). An alternative I have yet to try out is Lazarus(maybe in a future blogpost!), but the speed of developing GUI’s in Red is very promising.

The Lines clone code is open source on GitHub.

If you’re interested learning more about Red, check out these links:

Rebol Documentation — (Recommended)

The Red Language Notebook — (Recommended)

RedProgramming.com — (Recommended)

Rebol Core Users Guide

Red Official Documentation — (Slightly incomplete)

Red guides and community material

Red by Example

  1. I used the tried and tested ones, you know: Python, Ruby, Java, C/++/# and JS, but wanted to try something new. In terms of GUI’s for Go, shiny is experimental, there is therecipe/qt and my personal favorite, andlabs/ui.

  2. There exists some performant Electron apps (Visual Studio Code), but this is the exception and not the rule.

最后的想法

除了可以用 Linux 实现(一个可在 Red 工作的 GTK3 后端)的 GUI 系统之外,请注意,Red 目前可编译 32 位二进制文件。使用“Automated Photo Copier”应用程序时,我使用 Red 复制大图片时遇到了问题,而且程序经常突然崩溃。我切换到使用 cp 复制照片(因为我知道我的爸爸只使用 Mac)通过在 cp 命令上调用 call/console。这样,它就能完美地复制图像。如果他用 Windows,这将是一个不同的故事。

Red 是 Electron 的一个很好的替代吗?如果你正在构建一个小型的内部工具,需要一个快速的图形用户界面或需要一个开发难度较低的跨平台的 GUI,那么答案是 yes。虽然现在 Red 还没有达到产品级,但从其路线图来看,Red 在未来有很大的潜力。我不得不承认,如果你不熟悉 Rebol(下面有对应的资源和链接),学习曲线会略微适中。另一个我还没有尝试的替代方案是 Lazarus(也许在未来的博客会介绍),但是用 Red 开发 GUI 的速度是非常有前途的。

Lines clone 代码已在 GitHub 上开源。

如果你有兴趣了解关于 Red 的更多信息,请查看以下链接:

Rebol Documentation — (Recommended)

The Red Language Notebook — (Recommended)

RedProgramming.com — (Recommended)

Rebol Core Users Guide

Red Official Documentation — (Slightly incomplete)

Red guides and community material

Red 示例

  1. 我已经使用了久经考验的 Python、Ruby、Java、C/++/# 和 JS,但是我想尝试新的东西。就用 Go 编写 GUI 而言,shiny 是实验性的,这还有 therecipe/qt,以及我个人的最爱,andlabs/ui

  2. 也有一些高性能的 Electron 程序(Visual Studio Code),但这只是例外的,而不是常规的。

返回顶部
顶部