Gox語言——支持跨平臺原生GUI開發的輕量級全功能腳本語言 - GX1

Gox語言是以Go語言(Golang)爲基礎的解釋型/腳本語言,它除了具有一般腳本語言所具有的編寫快捷、語言簡潔、易於理解等特點外,還支持其他語言所不具備的跨平臺原生圖形界面(GUI)開發,並且代碼寫起來非常舒暢。

用Gox語言編程


Gox語言的主要特點包括:

  • 跨平臺,目前支持Windows、Mac和Linux等主流平臺;
  • 完全免費和開源,使用MIT授權協議;
  • 代碼基於Go語言(Golang),但做了一些優化,因此Go語言、C語言、C++、C#、Java及類似語言的開發者編寫起來幾乎沒有任何壓力,稍加了解就可以開始編寫代碼;
  • 相較於Go語言嚴格的語法書寫要求和靜態數據類型限制,Gox語言實現了動態類型,並做了許多更接近主流高級語言的改進,使得代碼書寫上方便了很多;
  • 基本支持所有Go語言主要的標準庫,並加以擴充,並且理論上可以支持任意多的第三方擴展庫,依託Go語言社區已有的海量代碼庫,Gox語言具備成爲全棧語言的潛力;
  • 具備嵌套執行腳本的能力,支持模塊化編程,支持比Go語言更方便的面向對象編程;
  • 與一般的解釋性/腳本語言不同,Gox語言自帶代碼加密功能,支持對發佈的代碼進行加密,以及對加密代碼的解密執行功能,可以有效地保護開發人員的工作;
  • 語法基於Qlang這種與Go語言能夠緊密互操作的語言,天然繼承了很多Go語言的優秀特性,例如goroutine和chan等;
  • 多腳本之間可以共享全局數據;
  • 支持跨平臺的原生圖形界面(GUI)的開發,並且界面佈局代碼書寫簡單易懂;
  • 綠色,無安裝文件或安裝包,無需安裝和任何環境配置過程,沒有任何依賴,只需下載一個可執行文件,即可實現所有Gox代碼開發和程序執行的任務;
  • 更爲神奇的是,這一個主程序文件竟然能夠同時支持命令行模式的開發和圖形界面(GUI)的開發,並且還自帶交互式編程環境(REPL),甚至還內置了一個圖形化的、支持代碼高亮和語法檢查的代碼編輯器!

Gox語言內置的代碼編輯器


下面用一個例子來介紹一下用Gox編寫代碼的方便程度,這是一個實現了簡易計算器功能的代碼:

expression = getInput("Please enter the expression: ")

result = eval(expression)

println("result:", result)


僅僅4行代碼而已,實現的效果如下:

Gox語言實現的計算器

可以看到,4行語言就實現了一個命令行版本的簡易表達式計算器。


如果用GUI圖形界面來實現怎麼樣呢?同樣的,只需要相對很簡單的代碼就能實現:

text1 = new(string)

onButton1Click = func() {
	// evaluate the expression in the text input
	rs = eval(*text1)

	println(rs)

	// set the result back into the text input
	setValue(text1, rs)
}

// close the window, also terminate the application
onButton2Click = func() {
	os.Exit(1)
}

// main window loop
loop = func() {

	// set the layout of GUI
	layoutT := []gui.Widget{
		gui.Label("Enter an expression."),
		gui.InputText("", 0, text1),

		// widgets in line layout is aligned left to right
		gui.Line(gui.Button("Calculate", onButton1Click),
			gui.Button("Close", onButton2Click)),
	}

	gui.SingleWindow("Calculator", layoutT)
}

// text1 used to hold the string value of the text input
// notice: text1 is a pointer
// setup the title, size (width and height, 400*200), style and font-loading function of main window,
mainWindow := gui.NewMasterWindow("Calculator", 400, 200, gui.MasterWindowFlagsNotResizable, nil)

// show the window and start the message loop
gui.LoopWindow(mainWindow, loop)


除去註釋,有效代碼不超過20行,就能夠實現一個界面相當不錯的圖形化計算器了,而且可以實現跨平臺!實際效果如下圖所示:

Gox實現的圖形化計算器


那麼,更爲令人歎爲觀止地,一個支持中文、跨平臺、功能齊全而又簡潔的代碼編輯器也不需要多少Gox代碼就可以實現,運行效果如下圖所示,有效代碼僅需不到200行(請參見本文末尾所附代碼)。

簡易全功能代碼編輯器

由於僅是示例,篇幅所限,沒有實現代碼高亮和自動完成功能,但除此以外,一個代碼編輯器所需的所有主要功能都已經實現了,甚至還包括代碼加密、解密和運行功能。


下面看看Gox語言的主要使用方式。

  • 首先,直接去Gox語言官網(gox.topget.org)或其Github/Gitee的release頁面(topxeq/gox)下載Gox語言的單一可執行文件,將其放在一個目錄下(如果是下載的zip壓縮包,需要進行解壓縮);該目錄最好在操作系統可找到的目錄下(即加入PATH環境變量中),以便執行;
    Gox語言官網

  • 直接在命令行模式下(例如Windows的命令提示符)運行gox命令即可進入交互式編程環境,如下圖所示:
    Gox的交互式編程環境

  • 使用類似 gox -edit basic.gox 的命令可以啓動Gox的內置編輯器編輯名爲basic.gox的Gox代碼文件,而如果直接用 gox -edit 不帶文件名,則將打開一個新文件進行編輯;

  • 也可以在Gox交互式編程環境中直接用edit函數來啓動圖形化代碼編輯器:
    REPL中啓動Gox代碼編輯器

  • 運行Gox代碼,只需要執行類似 gox basic.gox 的命令即可,也可以直接執行JavaScript代碼文件,例如 gox test.js。

  • 使用類似 gox -encrypt=mycode basic.gox 的命令來對源代碼進行加密(mycode是密碼,將會生成一個名爲basic.goxe的加密代碼文件),然後可以使用類似 gox -decrypt=mycode basic.goxe 的命令來對代碼進行解密,或者直接用 gox -decrun=mycode basic.goxe 命令來解密執行源代碼,也可以直接 gox basic.goxe 執行源代碼,此時將會要求先輸入密碼後才能執行;

  • 除使用內置的代碼編輯器外,也可以使用功能更爲豐富的其他編輯器,例如Visual Studio Code就是一個很好的選擇,將.gox類型文件的文件的代碼高亮方案設置成與Go語言相同即可;

  • 更多的說明和代碼示例可以參考Github上Gox語言的代碼庫,github.com/topxeq/gox。


當然,Gox語言目前也存在不足,我們要心裏有數,以便在使用Gox語言時清楚它更適合做什麼和不太適合做什麼。

目前,Gox語言最主要的不足就是:作爲解釋型/腳本語言,Gox也具有這類語言的通病,就是執行代碼的速度相對比較慢,比一些具有多年優化積累的解釋型語言如Python、Java等也要更慢。

另外,Gox語言雖然只有一個主程序,但相對來說比較大(有幾十M之多)。但這也比較好理解,畢竟還需要支持圖形化編程並自帶代碼編輯器。考慮到這一點,Gox語言也提供一個去除了圖形化編程功能的純命令行版本,文件大小會小一些,效率也會高一點。另外還有一個Gox Tiny版本,保留了圖形界面編程的能力,但去掉了一些不常用的功能。

第三,Gox語言如果要支持更多的第三方類庫,需要手動編譯源碼,但這點對於程序員來說應該不是大問題。


總的來說,Gox語言雖然具備開發各種各種應用場景的全棧開發語言的潛力,但至少目前仍不太適合開發高密度計算類型和需要極高速相應的程序和系統;但它非常適合作爲“膠水語言”來作爲大型系統之間的粘合劑,也非常適合快速開發一些無需太高速度要求的功能系統(例如可以取代shell腳本的開發、進行各種複雜的文本處理、實現各種網絡編程、編寫一些圖形化操控界面)以及做一些快速原型演示等。


附——全功能簡易代碼編輯器源碼:

argsG = os.Args

editFileNameG = ""
editFileCleanFlagG = ""
editSecureCodeG = new(string)

fcT = ""

aryT = tk.GetAllParameters(argsG)
lenT = len(aryT)

if lenT < 3 {
	editFileNameG = ""
	editFileCleanFlagG = "*"
} else {
	editFileNameG = aryT[2]
	fcT = tk.LoadStringFromFile(editFileNameG)

	if tk.IsErrorString(fcT) {
		gui.SimpleError("錯誤提示", tk.Spr("載入文件時發生錯誤:%v", tk.GetErrorString(fcT)))
		return
	}

	editFileCleanFlagG = ""

}


// hold the text in main edit control
text1 = new(string)

setValue(text1, fcT)

onEditChange = func() {
	editFileCleanFlagG = "*"
}

onButtonLoad = func() {
	if editFileCleanFlagG != "" {
		rs = gui.GetConfirm("請確認", "文件已被修改,確認要打開另一個文件嗎?")

		if rs == false {
			return
		}
	}

	fileNameNewT = gui.SelectFile("請選擇要打開的文件", "所有文件", "*")

	if tk.IsErrorString(fileNameNewT) {
		if tk.EndsWith(fileNameNewT, "Cancelled") {
			gui.MessageBox("信息", tk.Spr("操作被用戶取消"))
			return
		}

		gui.MessageBox("錯誤提示", tk.Spr("選擇文件失敗:%v", tk.GetErrorString(fileNameNewT)))
		return
	}

	fcT = tk.LoadStringFromFile(fileNameNewT)

	if tk.IsErrorString(fcT) {
		gui.MessageBox("錯誤提示", tk.Spr("載入文件內容失敗:%v", tk.GetErrorString(fileNameNewT)))
		return
	}

	editFileNameG = fileNameNewT
	
	setValue(text1, fcT)

	editFileCleanFlagG = ""

}

onButtonRunClick = func() {
	rs = runScript(*text1, "")

	gui.MessageBox("運行結果", tk.Spr("%v", rs))
}

editorSaveAs = func() {
	fileNameNewT = gui.SelectSaveFile("請選擇要保存的文件", "所有文件", "*")

	if tk.IsErrorString(fileNameNewT) {
		if tk.EndsWith(fileNameNewT, "Cancelled") {
			gui.MessageBox("信息", tk.Spr("操作被用戶取消"))
			return
		}

		gui.MessageBox("錯誤提示", tk.Spr("選擇文件失敗:%v", tk.GetErrorString(fileNameNewT)))
		return
	}

	editFileNameG = fileNameNewT

	rs1 = tk.SaveStringToFile(*text1, editFileNameG)

	if rs1 != "" {
		gui.MessageBox("錯誤提示", tk.Spr("保存文件失敗:%v", rs))
		return
	}

	gui.MessageBox("信息", tk.Spr("文件已被保存至:%v", editFileNameG))

	editFileCleanFlagG = ""

}

editorSave = func() {
	if editFileNameG == "" {
		editorSaveAs()

		return
	}

	rs = false

	if tk.IfFileExists(editFileNameG) {
		rs = gui.GetConfirm("請確認", "文件已存在,是否要覆蓋?")
	}

	if rs == true {
		rs1 = tk.SaveStringToFile(*text1, editFileNameG)

		if rs1 != "" {
			gui.MessageBox("錯誤提示", tk.Spr("保存文件失敗:%v", rs))
			return
		}

		gui.MessageBox("信息", tk.Spr("文件已被保存至:%v", editFileNameG))

		editFileCleanFlagG = ""
	}

}

editEncrypt = func() {
	gui.CloseCurrentPopup()

	sourceT = *text1

	encStrT = tk.EncryptStringByTXDEF(sourceT, *editSecureCodeG)

	if tk.IsErrorString(encStrT) {
		gui.SimpleError("錯誤提示", tk.Spr("加密失敗:%v", tk.GetErrorString(encStrT)))
		return
	}

	setValue(text1, "//TXDEF#" + encStrT)
	editFileCleanFlagG = "*"

	setValue(editSecureCodeG, "")
}

editEncryptClick = func() {
	gui.OpenPopup("請輸入密碼##EncryptInputSecureCode")
}

editDecrypt = func() {
	gui.CloseCurrentPopup()

	sourceT = tk.Trim(*text1)

	encStrT = tk.DecryptStringByTXDEF(sourceT, *editSecureCodeG)

	if tk.IsErrorString(encStrT) {
		gui.SimpleError("錯誤提示", tk.Spr("解密失敗:%v", tk.GetErrorString(encStrT)))
		return
	}

	setValue(text1, encStrT)
	editFileCleanFlagG = "*"
	setValue(editSecureCodeG, "")

}

editDecryptClick = func() {
	gui.OpenPopup("請輸入密碼##DecryptInputSecureCode")
}


onButtonCloseClick = func() {
	exit()
}

loop = func() {

	layoutT = make(gui.Layout)

	layoutT = append(layoutT, gui.Label(editFileNameG + editFileCleanFlagG))
	layoutT = append(layoutT, gui.InputTextMultiline("InputTextMultiline001", text1, -1, -30, 0, nil, onEditChange))
	layoutT = append(layoutT, gui.Line(gui.Button("打開", onButtonLoad), gui.Button("保存", editorSave), gui.Button("另存爲", editorSaveAs), gui.Button("加密", editEncryptClick), gui.Button("解密", editDecryptClick), gui.Button("運行", onButtonRunClick), gui.Button("關閉", onButtonCloseClick)))

	layoutT = append(layoutT, gui.PopupModal("請輸入密碼##EncryptInputSecureCode", []gui.Widget{gui.Line(gui.Label("密碼"), gui.InputTextV("", 40, editSecureCodeG, gui.InputTextFlagsPassword, nil, nil)),
		gui.Line(gui.Button("確定", editEncrypt), gui.Button("取消", func() { gui.CloseCurrentPopup() })),
	}))

	layoutT = append(layoutT, gui.PopupModal("請輸入密碼##DecryptInputSecureCode", []gui.Widget{
		gui.Line(gui.Label("密碼"),
			gui.InputTextV("", 40, editSecureCodeG, gui.InputTextFlagsPassword, nil, nil)),
		gui.Line(gui.Button("確定", editDecrypt),
			gui.Button("取消", func() { gui.CloseCurrentPopup() })),
	}))


	// add this to the layout if you would use gui.MessageBox function later
	layoutT = append(layoutT, gui.PrepareMessageBox())

	gui.SingleWindow("Gox編輯器", layoutT)
}

osNameT = tk.GetOSName()

if tk.Contains(osNameT, "darwin") {
	setVar("Font", "/Library/Fonts/Microsoft/SimHei.ttf")
} else {
	setVar("Font", "c:/Windows/Fonts/simsun.ttc")
}

setVar("FontRange", "COMMON")
setVar("FontSize", "15")

mainWindow = gui.NewMasterWindow("Gox編輯器", 800, 600, 0, gui.LoadFont)

gui.LoopWindow(mainWindow, loop)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章