-
-
Save caiorss/a59d0f1b17d286dad19842932c931e92 to your computer and use it in GitHub Desktop.
C-wrapper for Qt5 C++ GUI library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_build/* | |
bin/* | |
obj/* | |
*.so | |
*.o | |
*.obj | |
*.exe | |
*.bin | |
*.pyc | |
CMakeFiles/* | |
build/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <functional> | |
#include <cassert> | |
// Indicates that symbols from this header file have C linkage (C ABI) | |
// rather than C++ ABI. | |
extern "C" { | |
#include "qtwrapper.h" | |
} | |
const int WIDGET_WINDOW = 1; | |
const int WIDGET_WINDOW_MAIN = 2; | |
const int WIDGET_QPUSH_BUTTON = 3; | |
const int WIDGET_QLABEL = 4; | |
const int WIDGET_QLINE_EDIT = 5; | |
const int WIDGET_QDOUBLE_SPINBOX = 6; | |
const int LAYOUT_QFORM_LAYOUT = 3; | |
using ButtonCallck = std::function<void ()>; | |
// Implementation with type erasure and dynamic allocations | |
// Note: std::functions<> uses dynamic allocaiton underneath | |
void onClicked(QPushButton* btn, ButtonCallck func) | |
{ | |
qt_button_onClicked(btn, &func, [](void* ctx){ | |
auto callback = reinterpret_cast<ButtonCallck*>(ctx); | |
callback->operator()(); | |
}); | |
} | |
// Implementation without dynamic allocation | |
// DOES NOT WORK: segmentation fault | |
template<typename Callback> | |
void onClicked_tpl(QPushButton* btn, Callback func) | |
{ | |
qt_button_onClicked(btn, &func, [](void* ctx){ | |
// auto callback = (Callback*) ctx; | |
auto callback = reinterpret_cast<Callback*>(ctx); | |
//callback(); | |
callback->operator()(); | |
}); | |
} | |
int main(int argc, char** argv) | |
{ | |
QApplication* qapp = qt_app_new2(); | |
QPushButton* win = qt_widget_new(NULL, WIDGET_WINDOW_MAIN); | |
qt_widget_setText(win, "Window Title"); | |
assert(win != NULL); | |
QFormLayout* form = qt_layout_new(win, LAYOUT_QFORM_LAYOUT); | |
assert(form != NULL); | |
QPushButton* btn1 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, ""); | |
qt_widget_setText(btn1, "Button 1"); | |
QLabel* disp = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLABEL, "Result"); | |
// qt_widget_setText(btn1, "Button 1"); | |
int counter = 0; | |
onClicked(btn1, [&counter, disp]{ | |
std::cout << " [TRACE] (*) Counter = " << counter++ << "\n"; | |
auto text = std::string(" Counter set to = ") + std::to_string(counter); | |
qt_widget_setText(disp, text.c_str()); | |
if( counter > 10 ){ | |
qt_msgbox_info(nullptr, "Notification", "Counter greater than 10"); | |
} | |
}); | |
// Blocks current thread until window is closed. | |
qt_app_exec(qapp); | |
// Dispose QT objects | |
qt_qobject_del(win); | |
qt_qobject_del(qapp); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include "qtwrapper.h" | |
typedef struct CallbackContext | |
{ | |
QLineEdit* lin; | |
int counter; | |
} CallbackContext; | |
// All callback (closure) state should be passed as arguments | |
void onclicked_callback(void* context) | |
{ | |
CallbackContext* ctx = (CallbackContext*) context; | |
ctx->counter += 1; | |
const char* str = qt_QLineEdit_text(ctx->lin); | |
printf(" [TRACE] Button clicked => Counter incremented to: %d \n", ctx->counter); | |
printf(" [TRACE] QLineEdit Value = %s \n", str); | |
// free(str); | |
// qt_msgbox_info(NULL, "Message Info", "Button clicked Ok"); | |
} | |
void lineEdit_Callback(void* ctx, QLineEdit* self) | |
{ | |
const char* str = qt_QLineEdit_text(self); | |
QLabel* label = (QLabel*) ctx; | |
qt_widget_setText(label, str); | |
printf(" [LineEdit Callback] Text changed to %s \n", str); | |
} | |
void onclick_msgbox_callback(void* context) | |
{ | |
QLineEdit* lin = (QLineEdit*) context; | |
const char* str = qt_QLineEdit_text(lin); | |
qt_msgbox_info(NULL, "Message Info", str); | |
// free(str); | |
} | |
const int WIDGET_WINDOW = 1; | |
const int WIDGET_WINDOW_MAIN = 2; | |
const int WIDGET_QPUSH_BUTTON = 3; | |
const int WIDGET_QLABEL = 4; | |
const int WIDGET_QLINE_EDIT = 5; | |
const int WIDGET_QDOUBLE_SPINBOX = 6; | |
const int LAYOUT_QFORM_LAYOUT = 3; | |
int main(int argc, char** argv) | |
{ | |
// QApplication* qapp = qt_app_new(argc, argv); | |
QApplication* qapp = qt_app_new2(); | |
// -------- Create GUI - Graphical User Interface ------// | |
QPushButton* win = qt_widget_new(NULL, WIDGET_WINDOW_MAIN); | |
qt_widget_setText(win, "Window Title"); | |
assert(win != NULL); | |
QFormLayout* form = qt_layout_new(win, LAYOUT_QFORM_LAYOUT); | |
assert(form != NULL); | |
QLineEdit* lin = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLINE_EDIT, "Input"); | |
QPushButton* btn1 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, ""); | |
QPushButton* btn2 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, ""); | |
QLabel* disp = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLABEL, "Display label"); | |
qt_widget_setText(btn1, "Button 1"); | |
qt_widget_setText(btn2 ,"Button 2"); | |
// qt_widget_setToolTip(btn1, "Click at this button to play!"); | |
qt_qobject_print(win); | |
// -------- Install Event Handlers --------------// | |
// | |
qt_QLineEdit_onTextChanged(lin, disp, lineEdit_Callback); | |
qt_button_onClicked(btn1, lin, &onclick_msgbox_callback); | |
CallbackContext ctx; | |
ctx.counter = 0; | |
ctx.lin = lin; | |
qt_button_onClicked(btn2, &ctx, &onclicked_callback); | |
// ---- Run QT event loop - blocking main thread ---// | |
qt_app_exec(qapp); | |
// ----- Dispose objects ---------------------------// | |
qt_qobject_del(win); | |
qt_qobject_del(qapp); | |
puts("\n [TRACE] Terminate application Ok. "); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.9) | |
project(Qt5_Widgets_Template) | |
#====== Global Configurations ==================# | |
set(CMAKE_CXX_STANDARD 17) | |
set(CMAKE_CXX_STANDARD_REQUIRED ON) | |
set(CMAKE_VERBOSE_MAKEFILE ON) | |
set(CMAKE_INCLUDE_CURRENT_DIR ON) | |
set(CMAKE_AUTOUIC ON) | |
set(CMAKE_AUTOMOC ON) | |
set(CMAKE_AUTORCC ON) | |
# Export ALL DLLs symbols on Windows without __declspec(xxxx) annotations. | |
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true) | |
find_package(Qt5 COMPONENTS Core Widgets Network REQUIRED) | |
#=============== Target Configurations ============# | |
include_directories(.) | |
# --------------------------------------------------# | |
add_library( qtwrapper SHARED qtwrapper.cpp ) | |
target_link_libraries( qtwrapper PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets ) | |
add_executable( client1 client1.c ) | |
target_link_libraries( client1 qtwrapper ) | |
add_executable( client2 client.cpp) | |
target_link_libraries( client2 qtwrapper ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import core.stdc.stdio; | |
import std.range: empty; | |
import str = std.string; | |
// Import dlopen | |
import DLL = core.sys.posix.dlfcn; | |
alias QLayout = void; | |
alias QObject = void; | |
alias QWidget = void; | |
alias QLabel = void; | |
alias QApplication = void; | |
alias QPushButton = void; | |
alias QFormLayout = void; | |
alias QAbstractButton = void; | |
const int WIDGET_WINDOW = 1; | |
const int WIDGET_WINDOW_MAIN = 2; | |
const int WIDGET_QPUSH_BUTTON = 3; | |
const int WIDGET_QLABEL = 4; | |
const int WIDGET_QLINE_EDIT = 5; | |
const int WIDGET_QDOUBLE_SPINBOX = 6; | |
const int LAYOUT_QFORM_LAYOUT = 3; | |
alias qapp_new_t = extern(C) QApplication* function (); | |
alias qapp_exec_t = extern(C) int function(QApplication* self); | |
alias qobject_del_t = extern(C) void function (QObject*); | |
alias qt_widget_new_t = extern(C) QWidget* function(QWidget* parent, int type); | |
alias qt_window_main_t = extern(C) QWidget* function(); | |
alias qt_widget_setText_t = extern(C) void function(QWidget* self, const char* text); | |
alias qt_layout_new_t = extern(C) QLayout* function (QWidget* parent, int type); | |
alias qt_msgbox_info_t = extern(C) void function(QWidget* parent, const char* title, const char* text); | |
// QObject* qt_QFormLayout_addWidgetAndLabel(QFormLayout* self, int type, const char* label) | |
alias qt_QFormLayout_addWidgetAndLabel_t = | |
extern(C) QPushButton* function (QFormLayout* self, int type, const char* label); | |
alias qt_button_onClicked_t = extern(C) void function ( QAbstractButton* self | |
, void* ctx | |
, void function(void* self) ); | |
struct callback_state | |
{ | |
int counter = 0; | |
QLabel* label = null; | |
qt_widget_setText_t qt_widget_setText = null; | |
qt_msgbox_info_t qt_msgbox_info = null; | |
}; | |
extern(C) void button_callback1(void* ctx) | |
{ | |
import std.conv: to; | |
auto pstate = cast(callback_state*) ctx; | |
pstate.counter = pstate.counter + 1; | |
printf(" [TRACE] Button click event happened => state = %d. \n", pstate.counter); | |
string text = "Button clicked / counter = "; | |
text = text ~ to!string(pstate.counter); | |
pstate.qt_widget_setText(pstate.label, str.toStringz(text)); | |
if(pstate.counter > 20){ | |
pstate.qt_msgbox_info(null, "QT Event => Button Clicked", str.toStringz(text)); | |
} | |
} | |
int main() | |
{ | |
// ------------- Load Symbols from Shared Library -------------------// | |
void* dll = DLL.dlopen("./build/libqtwrapper.so", DLL.RTLD_GLOBAL | DLL.RTLD_LAZY); | |
if (!dll) | |
{ | |
fprintf(stderr, " [ERROR] dlopen error: %s\n", DLL.dlerror()); | |
return 1; | |
} | |
auto qapp_new = cast(qapp_new_t) DLL.dlsym(dll, "qt_app_new2"); | |
auto qapp_exec = cast(qapp_exec_t) DLL.dlsym(dll, "qt_app_exec"); | |
auto qt_qobject_del = cast(qobject_del_t) DLL.dlsym(dll, "qt_qobject_del"); | |
auto qt_widget_new = cast(qt_widget_new_t) DLL.dlsym(dll, "qt_widget_new"); | |
auto qt_layout_new = cast(qt_widget_new_t) DLL.dlsym(dll, "qt_layout_new"); | |
auto qt_window_main = cast(qt_window_main_t) DLL.dlsym(dll, "qt_window_main"); | |
auto qt_widget_setText = cast(qt_widget_setText_t) DLL.dlsym(dll, "qt_widget_setText"); | |
auto qt_button_onClicked = cast(qt_button_onClicked_t) DLL.dlsym(dll, "qt_button_onClicked"); | |
auto qt_msgbox_info = cast(qt_msgbox_info_t) DLL.dlsym(dll, "qt_msgbox_info"); | |
auto form_add_item = cast(qt_QFormLayout_addWidgetAndLabel_t) DLL.dlsym(dll, "qt_QFormLayout_addWidgetAndLabel"); | |
assert(form_add_item); | |
// ---------- Create QT GUI -----------------------------// | |
// | |
// Create an instance of class QApplication | |
auto qapp = qapp_new(); | |
auto window = qt_widget_new(null, WIDGET_WINDOW_MAIN) ; | |
assert( window ); | |
qt_widget_setText(window, "QT Widgets GUI in D Language"); | |
auto form = qt_layout_new(window, LAYOUT_QFORM_LAYOUT); | |
auto btn = form_add_item(form, WIDGET_QPUSH_BUTTON, ""); | |
auto label = form_add_item(form, WIDGET_QLABEL, "Display"); | |
qt_widget_setText(btn, "Click ME NOW!!"); | |
callback_state state; | |
state.counter = 10; | |
state.label = label; | |
state.qt_msgbox_info = qt_msgbox_info; | |
state.qt_widget_setText = qt_widget_setText; | |
// Install button event handler | |
qt_button_onClicked(btn, &state, &button_callback1); | |
// ------ Run QT Event Loop blocking main thread ------// | |
qapp_exec(qapp); | |
// ------ Dipose QT Objects ---------------------------// | |
// | |
qt_qobject_del(window); | |
qt_qobject_del(qapp); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std/dynlib | |
import os | |
type | |
gchar = cchar # char | |
gshort = cshort # short | |
glong = clong # long | |
gint = cint # int | |
gboolean = gint | |
guchar = cuchar # unsigned char | |
gushort = cushort # unsigned short | |
gulong = culong # unsigned long | |
guint = cint # unsigned int | |
gfloat = cfloat | |
gdouble = cdouble | |
gpointer = pointer # void* (Opaque pointer) | |
gconstpointer = pointer # const void* | |
type | |
GtkWidget = pointer | |
GConnectFlags = cint | |
GtkDialogFlags = cint | |
GtkMessageType = cint | |
GtkButtonsType = cint | |
# Enumeration: GtkWindowType | |
const | |
GTK_WINDOW_TOPLEVEL = 0 | |
GTK_WINDOW_POPUP = 1 | |
# Enumeration: GConnectFlags | |
G_CONNECT_AFTER = 1 | |
G_CONNECT_SWAPPED = 2 | |
# Enumeration: GtkMessageType | |
const | |
GTK_MESSAGE_INFO = 0 | |
GTK_MESSAGE_WARNING = 1 | |
GTK_MESSAGE_QUESTION = 2 | |
GTK_MESSAGE_ERROR = 3 | |
GTK_MESSAGE_OTHER = 4 | |
# Enumeration: GtkDialogFlags | |
const | |
GTK_DIALOG_MODAL = 1 | |
GTK_DIALOG_DESTROY_WITH_PARENT = 2 | |
GTK_DIALOG_USE_HEADER_BAR = 4 | |
# Enumeration: GtkButtonsType | |
const | |
GTK_BUTTONS_NONE = 0 | |
GTK_BUTTONS_OK = 1 | |
GTK_BUTTONS_CLOSE = 2 | |
GTK_BUTTONS_CANCEL = 3 | |
GTK_BUTTONS_YES_NO = 4 | |
GTK_BUTTONS_OK_CANCEL = 5 | |
#-------------------------------------------------# | |
# GTK Function Pointers Definitions # | |
#-------------------------------------------------# | |
type | |
# using gtk_init_check_t = gboolean (*) (int* argc, char*** argv); | |
# See: https://stackoverflow.com/questions/55646129/nim-argv-equivalent | |
gtk_init_check_t = proc (argc: ptr cint, argv: cstringArray): gboolean {.gcsafe, stdcall.} | |
# using gkt_window_new_t = GtkWidget* (*) (int); | |
gtk_window_new_t = proc(param: cint): GtkWidget {.gcsafe, stdcall.} | |
# void (*) (GtkWidget*) | |
gtk_widget_show_t = proc(widget: GtkWidget): void {.gcsafe, stdcall.} | |
# void (*) () | |
gtk_main_t = proc(): void {.gcsafe, stdcall.} | |
# using gtk_window_set_title_t = void (*) (GtkWidget* window, const gchar* title); | |
gtk_window_set_title_t = proc(window: GtkWidget, title: cstring): void {.gcsafe, stdcall.} | |
# using gtk_widget_set_size_t = void (*) (GtkWidget*, gint, gint); | |
gtk_widget_set_size_t = proc(widget: GtkWidget, width: gint, height: gint): void {.gcsafe, stdcall.} | |
# void gtk_widget_show_all ( GtkWidget* widget) | |
gtk_widget_show_all_t = proc (widget: GtkWidget): void {.gcsafe, stdcall.} | |
# GtkWidget* gtk_button_new_with_label ( const gchar* label) | |
gtk_button_new_with_label_t = proc(label: cstring): GtkWidget {.gcsafe, stdcall.} | |
# void gtk_container_add ( GtkContainer* container, GtkWidget* widget) | |
# See: https://docs.gtk.org/gtk3/method.Container.add.html | |
gtk_container_add_t = proc(container: GtkWidget, widget: GtkWidget ): void {.gcsafe, stdcall.} | |
# void gtk_widget_set_size_request ( GtkWidget* widget, gint width, gint height) | |
# See: https://docs.gtk.org/gtk3/method.Widget.set_size_request.html | |
gtk_widget_set_size_request_t = proc( widget: GtkWidget | |
, width: gint | |
, height: gint) {.gcsafe, stdcall.} | |
# void (*) () | |
gtk_main_quit_t = proc(): void {.gcsafe, stdcall.} | |
# void* (void pointer - opaque pointer) | |
GClosure = pointer | |
# void (*) (void); | |
GCallback = proc(): void {.gcsafe, stdcall.} | |
# void (*) (gpointer data, GClosure* closure); | |
GClosureNotify = proc(data: gpointer, closure: GClosure): void | |
ClickedCallback = proc(): void {.gcsafe, stdcall.} | |
g_signal_connect_data_t = proc( instance: gpointer | |
, detailed_signal: cstring | |
, c_handler: GCallback | |
, data: gpointer | |
, destroy_data: GClosureNotify | |
, connect_flag: GConnectFlags | |
): gulong {.gcsafe, stdcall.} | |
gtk_message_dialog_new_t = proc( parent: GtkWidget | |
, flags: GtkDialogFlags | |
, ttype: GtkMessageType | |
, buttons: GtkButtonsType | |
, message_format: cstring | |
): GtkWidget {.gcsafe, stdcall.} | |
# gint (*) (void* dialog); | |
gtk_dialog_run_t = proc(dialog: GtkWidget): gint {.gcsafe, stdcall.} | |
# using gtk_widget_destroy_t = void (*) (GtkWidget* widget); | |
gtk_widget_destroy_t = proc(widget: GtkWidget): void {.gcsafe, stdcall.} | |
# guint gtk_get_major_version (void) | |
gtk_get_major_version_t = proc(): void {.gcsafe, stdcall.} | |
#---------------------------------------------------# | |
# Load Function GTK Pointers from shared library # | |
# # | |
# GTK functions' type signature can be searched at: # | |
# - https://docs.gtk.org/gtk3 # | |
#---------------------------------------------------# | |
proc loadSym(lib: dynlib.LibHandle, symbol: string): pointer = | |
echo " [TRACE] Loading symbol = " & symbol | |
let p = lib.symAddr(symbol) | |
assert p != nil, ("Cannot load symbol = " & symbol) | |
return p | |
echo "\n\n ======== Program started Ok ==============" | |
let dll = os.getEnv("GTK_LIB", "libgtk-3.so.0") | |
let lib = dynlib.loadLib(dll) | |
assert lib != nil, "Cannot load shared library" | |
echo " [TRACE] Shared library " & dll & " loaded Ok." | |
let gtk_init_check = cast[gtk_init_check_t]( lib.loadSym("gtk_init_check") ) | |
let gtk_window_new = cast[gtk_window_new_t]( lib.loadSym("gtk_window_new") ) | |
let gtk_widget_show = cast[gtk_widget_show_t]( lib.loadSym("gtk_widget_show") ) | |
let gtk_main = cast[gtk_main_t]( lib.loadSym("gtk_main") ) | |
let gtk_window_set_title = cast[gtk_window_set_title_t]( lib.loadSym("gtk_window_set_title") ) | |
let gtk_widget_set_size = cast[gtk_widget_set_size_t]( lib.loadSym("gtk_widget_set_size_request") ) | |
let gtk_main_quit = cast[gtk_main_quit_t]( lib.loadSym("gtk_main_quit") ) | |
let g_signal_connect_data = cast[g_signal_connect_data_t]( lib.loadSym("g_signal_connect_data") ) | |
let gtk_widget_show_all = cast[gtk_widget_show_all_t]( lib.loadSym("gtk_widget_show_all") ) | |
let gtk_button_new_with_label = cast[gtk_button_new_with_label_t]( lib.loadSym("gtk_button_new_with_label") ) | |
let gtk_container_add = cast[gtk_container_add_t]( lib.loadSym("gtk_container_add") ) | |
let gtk_widget_set_size_request = cast[gtk_widget_set_size_request_t]( lib.loadSym("gtk_widget_set_size_request") ) | |
let gtk_dialog_run = cast[gtk_dialog_run_t]( lib.loadSym("gtk_dialog_run") ) | |
let gtk_message_dialog_new = cast[gtk_message_dialog_new_t]( lib.loadSym("gtk_message_dialog_new") ) | |
let gtk_widget_destroy = cast[gtk_widget_destroy_t]( lib.loadSym("gtk_widget_destroy") ) | |
let gtk_get_major_version = cast[gtk_get_major_version_t]( lib.symAddr("gtk_get_major_version") ) | |
if gtk_get_major_version == nil: | |
echo " [TRACE] Running Gtk - 2.X" | |
else: | |
echo " [TRACE] Running Gtk - 3.X" | |
# gtk_main_quit() wrapper for logging | |
proc gtk_app_quit(): void {.gcsafe, stdcall.} = | |
echo " [TRACE] Quit GTK - Shutdown GTK event loop and unblocks current thread." | |
gtk_main_quit() | |
proc on_clicked(button: GtkWidget, callback: ClickedCallback): void = | |
echo "I was clicked" | |
discard g_signal_connect_data(button, "clicked", callback, nil, nil, cint 0) | |
proc msgbox_info(parent: GtkWidget, title: cstring, text: cstring): void = | |
let dialog = gtk_message_dialog_new( parent, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO | |
, GTK_BUTTONS_OK, text) | |
assert dialog != nil | |
gtk_window_set_title dialog, title | |
discard gtk_dialog_run dialog | |
gtk_widget_destroy dialog | |
# Start GTK widgets | |
proc gtk_start(): void = | |
var nargv = newSeq[string](paramCount()) | |
var x = 0 | |
while x < paramCount(): | |
nargv[x] = paramStr(x+1) # first is program name | |
x += 1 | |
var argv: cStringArray = nargv.allocCStringArray() | |
var argc: cint = cint paramCount() | |
# Call gboolean gtk_init_check (int* argc, char*** argv); | |
discard gtk_init_check(addr argc, argv) | |
echo " [TRACE] Gtk started. Ok." | |
#---------------------------------------------------# | |
# Create GTK GUI # | |
#---------------------------------------------------# | |
echo " [TRACE] Shared library library loaded Ok." | |
# Encapsulates gtk_init_check(int &argc, char **argv) | |
# gtk_init_check must always be called before calling any other GTK | |
# function, otherwise a runtime error will happen. | |
gtk_start() | |
let window: GtkWidget = gtk_window_new( GTK_WINDOW_TOPLEVEL ) | |
assert window != nil | |
# Alternative ways to write the same code in the next non-comment line: | |
# =>> gtk_widget_set_size( window, 400, 500) | |
# =>> gtk_widget_set_size window, 400, 500 | |
# =>> window.gtk_widget_set_size( 400, 500 ) | |
# =>> window.gtk_widget_set_size 400, 500 | |
window.gtk_widget_set_size 400, 500 | |
window.gtk_window_set_title "My GTK Windows called from Nim" | |
let button = gtk_button_new_with_label("Click me now!") | |
assert button != nil, "Failed to create button" | |
button.gtk_widget_set_size_request 70, 30 | |
gtk_container_add(window, button) | |
# gtk_widget_show( window ) ->> UFS - Universal Function Call | |
# window.gtk_widget_show() | |
gtk_widget_show_all(window) | |
# Discard annotation discards the function return value | |
discard g_signal_connect_data( window, "destroy", gtk_app_quit, nil, nil, cint 0) | |
var counter = 0 | |
# Set button callback using a lambda syntax sugar | |
# for better code asthetics and readability | |
button.on_clicked do: | |
counter = counter + 1 | |
let message = " [INFO] Button was clicked " & $counter & " times. " | |
echo message | |
msgbox_info(nil, "Notification", message) | |
# Run GTK event loop and block current thread | |
gtk_main() | |
echo " [TRACE] GTK application shutdown OK." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env julia | |
# Refers to file libqexport.so | |
# | |
# On Linux: | |
# It is assumed that the LD_LIBRARY_PATH environment variable of current | |
# process contains the directory where libqtexport is located. | |
# | |
# On Windows: | |
# It is made the assumption that the PATH environmenrt variable of current | |
# process contains the directory where libqtexport is located. | |
# | |
# | |
const shlib = "libqtwrapper" | |
const QObject = Ptr{Cvoid} | |
const QWidget = Ptr{Cvoid} | |
const QLayout = Ptr{Cvoid} | |
const QApp = Ptr{Cvoid} | |
const QButton = Ptr{Cvoid} | |
const QDoubleSpinbox = Ptr{Cvoid} | |
const WIDGET_WINDOW = 1; | |
const WIDGET_WINDOW_MAIN = 2; | |
const WIDGET_QPUSH_BUTTON = 3; | |
const WIDGET_QLABEL = 4; | |
const WIDGET_QLINE_EDIT = 5; | |
const WIDGET_QDOUBLE_SPINBOX = 6; | |
const LAYOUT_QFORM_LAYOUT = 3; | |
# Wrapper to function: | |
# QWidget* qt_widget_new(QWidget* parent, const char* name) | |
function qt_widget_new(type::Int)::QWidget | |
return ccall( (:qt_widget_new, shlib) | |
# Function type signatrue | |
, QWidget, (QWidget, Cint) | |
# Function arguments (parent = null and name = name) | |
, C_NULL, type | |
) | |
end | |
# Delete any instance of QObject (QWidget, QApplictation, QLayout) and so on | |
function qt_qobject_del(obj::QObject) | |
ccall( (:qt_qobject_del, shlib) | |
, Cvoid, ( QObject, ) | |
, obj ) | |
end | |
function qt_qobject_print(self::QObject) | |
ccall( (:qt_qobject_print, shlib) | |
, Cvoid, (QObject,) | |
, self ) | |
end | |
## => void qt_widget_setText(QWidget* self, const char* text); | |
function qt_widget_setText(self::QWidget, text::String) | |
ccall( (:qt_widget_setText, shlib) | |
# Function type signatrue | |
, Cvoid, (QWidget, Cstring) | |
# Function arguments (parent = null and name = name) | |
, self, text ) | |
end | |
# extern QLayout* qt_layout_new(QWidget* parent, const char* name); | |
function qt_layout_new(type::Int, parent::QWidget)::QLayout | |
return ccall( (:qt_layout_new, shlib) | |
# Function type signatrue | |
, QLayout, (QWidget, Cint) | |
# Function arguments (parent = null and name = name) | |
, parent, type ) | |
end | |
# QPushButton* qt_QFormLayout_addButton(QFormLayout* self, const char* label) | |
function qt_QFormLayout_addWidgetAndLabel(form::QLayout, type::Int, label::String)::QWidget | |
return ccall( (:qt_QFormLayout_addWidgetAndLabel, shlib) | |
# Function type signatrue | |
, QWidget, (QLayout, Cint, Cstring) | |
# Function arguments | |
, form, type, label ) | |
end | |
function qt_qobject_ClassName(self::QObject)::String | |
res = ccall( (:qt_qobject_ClassName, shlib) | |
, Cstring, (QObject, ) | |
, self ) | |
return unsafe_string(res) | |
end | |
function QApplication_new() | |
# return ccall( (:qt_app_new2, shlib), Ptr{Cvoid}, ()) | |
return ccall((:qt_app_new2, shlib), QApp, ()) | |
end | |
function QApplication_exec(self) | |
ccall((:qt_app_exec, shlib), Cvoid, ( QApp, ), self ) | |
end | |
function QPushButton_new(label::String) | |
# return ccall((:qt_QPushButton_new, shlib), ( Ptr{Cvoid}, Cstring ), C_NULL, label ) | |
return btn = ccall( (:qt_QPushButton_new, shlib) | |
, Ptr{Cvoid}, (Ptr{Cvoid}, Cstring) | |
, C_NULL, label | |
) | |
end | |
function QWidget_show(self) | |
return ccall((:qt_QWidget_show, shlib), Cvoid, ( Ptr{Cvoid}, ), self) | |
end | |
function QPushButton_onClicked_test(self::QButton) | |
ccall((:qt_QPushButton_onClicked_test, shlib), Cvoid, ( QButton, ), self) | |
end | |
function qt_button_onClicked(self::QButton, handler) | |
callback = @cfunction $handler Cvoid ( Ptr{Cvoid}, ) | |
ccall( (:qt_button_onClicked, shlib) | |
# Function return type signature | |
, Cvoid | |
# Function arguments | |
, ( QButton, Ptr{Cvoid}, Ptr{Cvoid} ) | |
# Arguments passed to function | |
, self, C_NULL, callback | |
) | |
end | |
function qt_QDoubleSpinBox_value(self::QDoubleSpinbox) | |
return ccall( (:qt_QDoubleSpinBox_value, shlib) | |
, Cdouble, ( QDoubleSpinbox,) | |
, self) | |
end | |
# Wrapper: | |
# void qt_msgbox_info(QWidget* parent, const char* title, const char* text); | |
function qt_msgbox_info(title::String, text::String) | |
ccall( (:qt_msgbox_info, shlib) | |
, Cvoid, (Ptr{Cvoid}, Cstring, Cstring) | |
, C_NULL, title, text | |
) | |
end | |
# void qt_QDoubleSpinBox_onValueChanged( | |
# QDoubleSpinBox* self | |
# , void* ctx, void (* callback)(void* ctx)) | |
function qt_QDoubleSpinBox_onValueChanged(self::QDoubleSpinbox, handler) | |
callback = @cfunction $handler Cvoid ( Ptr{Cvoid}, ) | |
ccall( (:qt_QDoubleSpinBox_onValueChanged, shlib) | |
# Function return type signature | |
, Cvoid | |
# Function arguments | |
, ( QButton, Ptr{Cvoid}, Ptr{Cvoid} ) | |
# Arguments passed to function | |
, self, C_NULL, callback | |
) | |
end | |
function demo_qt_gui_form() | |
qapp = QApplication_new() | |
window = qt_widget_new( WIDGET_WINDOW_MAIN ) | |
qt_widget_setText(window, "Sample QT GUI in Julia Language") | |
form = qt_layout_new(LAYOUT_QFORM_LAYOUT, window) | |
entry1 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QDOUBLE_SPINBOX, "Speed in m/s") | |
entry2 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QDOUBLE_SPINBOX, "Acceleration m/s^2") | |
btn_run = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "") | |
btn_clean = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "") | |
label = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLABEL, "") | |
qt_widget_setText(btn_run, "Run calculations"); | |
qt_widget_setText(btn_clean, "Clean"); | |
println(" [INFO] class of form object = ", qt_qobject_ClassName(form)) | |
println(" [INFO] class of btn_run object = ", qt_qobject_ClassName(btn_run)) | |
println(" [INFO] class of entry1 object = ", qt_qobject_ClassName(entry1)) | |
println(" [INFO] class of entry2 object = ", qt_qobject_ClassName(entry2)) | |
qt_widget_setText(btn_run, "RUN GUI") | |
qt_widget_setText(btn_clean, "CLEAN GUI") | |
qt_qobject_print(window) | |
update_calculations = (ctx) -> begin | |
speed = qt_QDoubleSpinBox_value(entry1) | |
accel = qt_QDoubleSpinBox_value(entry2) | |
z = 4.51 * speed + 9.81 * accel^2 / speed | |
out = string(" [JULIA] Result = ", round(z, digits = 4)) | |
println(" speed = ", speed, " ; accel = ", accel) | |
println(out) | |
println(" --------------------------------- ") | |
qt_widget_setText(label, out) | |
end | |
qt_QDoubleSpinBox_onValueChanged(entry1, update_calculations) | |
qt_QDoubleSpinBox_onValueChanged(entry2, update_calculations) | |
n = 1 | |
qt_button_onClicked(btn_run, (ctx) -> begin | |
n = n + 1 | |
println(" [ JULIA ] I was clicked Ok. n = ", n, " times. ") | |
qt_msgbox_info("Notification", "Button was clicked Ok") | |
end) | |
qt_button_onClicked(btn_clean, (ctx) -> begin | |
println(" [TRACE] Button clean clicked Ok") | |
qt_widget_setText(label, "Button clean clicked") | |
end) | |
# --- Block main thread and run QT event loop ---// | |
QApplication_exec(qapp) | |
# ----- Dispose objects ---------- | |
# Only the root widget (window) need to be removed | |
qt_qobject_del(window) | |
qt_qobject_del(qapp) | |
end | |
demo_qt_gui_form() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Build C wrapper to Qt library | |
wrapper: | |
cmake -H. -B_build -DCMAKE_BUILD_TYPE=Debug | |
cmake --build _build --target | |
# Run python client code | |
python: | |
env LD_LIBRARY_PATH=$(PWD)/_build ./pywrapper.py | |
# Build and run dotnet client code | |
dotnet: | |
env LD_LIBRARY_PATH=$(PWD)/_build dotnet build myapp.csproj | |
env LD_LIBRARY_PATH=$(PWD)/_build ./bin/myapp | |
# Build rust client | |
rust: | |
rustc rustclient.rs -l qtwrapper -L_build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>net6.0</TargetFramework> | |
</PropertyGroup> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Necessary for defining lambdas in Nim, otherwise compilation errors happen. | |
import sugar | |
# For converting number to string | |
import strutils | |
const qlib = "libqtwrapper.so" | |
type | |
QApplication = pointer | |
QObject = pointer | |
QLineEdit = pointer | |
QWidget = pointer | |
QPushButton = pointer | |
QFormLayout = pointer | |
QDoubleSpinBox = pointer | |
Context = pointer | |
QLayout = pointer | |
type | |
callback_clicked = proc(ctx: Context): void | |
callback_onValueChanged = proc(ctx: Context): void | |
const | |
WIDGET_WINDOW = 1 | |
WIDGET_WINDOW_MAIN = 2 | |
WIDGET_QPUSH_BUTTON = 3 | |
WIDGET_QLABEL = 4 | |
WIDGET_QLINE_EDIT = 5 | |
WIDGET_QDOUBLE_SPINBOX = 6 | |
LAYOUT_QFORM_LAYOUT = 3 | |
proc qt_app_new2(): QApplication {.importc: "qt_app_new2", dynlib: qlib} | |
proc qt_app_exec2(qapp: QApplication): void {.importc: "qt_app_exec", dynlib: qlib} | |
proc qt_qobject_del(self: QWidget): void {.importc: "qt_qobject_del", dynlib: qlib} | |
# Set text and label | |
proc qt_widget_setText(self: QWidget, text: cstring): void | |
{.importc: "qt_widget_setText", dynlib: qlib} | |
# Factory function for instantiating any QT widget | |
proc qt_widget_new(parent: QWidget, ntype: cint): QPushButton | |
{.importc: "qt_widget_new", dynlib: qlib} | |
proc qt_widget_show(self: QWidget): void | |
{.importc: "qt_widget_show", dynlib: qlib} | |
# void qt_button_onClicked(QAbstractButton* self, void* ctx, void (* callback) (void*) ); | |
proc qt_button_onClicked(self: QPushButton, ctx: Context, callback: callback_clicked ): void | |
{.importc: "qt_button_onClicked", dynlib: qlib} | |
proc qt_msgbox_info(parent: QWidget, title: cstring, text: cstring): void | |
{.importc: "qt_msgbox_info", dynlib: qlib} | |
# Retrieves text from QLineEdit widget | |
proc qt_QLineEdit_text(self: QLineEdit): cstring | |
{.importc: "qt_QLineEdit_text", dynlib: qlib} | |
proc qt_layout_new(parent: QWidget, ntype: cint): QFormLayout | |
{.importc: "qt_layout_new", dynlib: qlib} | |
proc qt_QFormLayout_addWidgetAndLabel(self: QFormLayout, nype: cint, label: cstring): QObject | |
{.importc: "qt_QFormLayout_addWidgetAndLabel", dynlib: qlib} | |
proc qt_QDoubleSpinBox_value(self: QDoubleSpinBox): cdouble | |
{.importc: "qt_QDoubleSpinBox_value", dynlib: qlib} | |
proc qt_QDoubleSpinBox_setValue(self: QDoubleSpinBox, value: cdouble): void | |
{.importc: "qt_QDoubleSpinBox_setValue", dynlib: qlib} | |
proc qt_QDoubleSpinBox_onValueChanged( self: QDoubleSpinBox | |
,ctx: Context | |
,callback: callback_onValueChanged | |
) | |
{.importc: "qt_QDoubleSpinBox_onValueChanged", dynlib: qlib} | |
#-------------------------------------------------# | |
# QT Application # | |
#-------------------------------------------------# | |
echo " [TRACE] Staring application OK." | |
var app = qt_app_new2() | |
# The window is immediately shown after created. | |
var window = qt_widget_new(nil, WIDGET_WINDOW_MAIN) | |
# UFS - Universal function call => the object is the first function parameter | |
## Same as: qt_widget_setText(window, "NIM + Qt5-Widgets Application") | |
window.qt_widget_setText("NIM + Qt5-Widgets Application") | |
# Instantiate QT widgets | |
var form = qt_layout_new(window, LAYOUT_QFORM_LAYOUT) | |
var buttonA = form.qt_QFormLayout_addWidgetAndLabel(WIDGET_QPUSH_BUTTON, "ButtonA's label") | |
var buttonB = form.qt_QFormLayout_addWidgetAndLabel(WIDGET_QPUSH_BUTTON, "ButtonB's label") | |
var buttonC = form.qt_QFormLayout_addWidgetAndLabel(WIDGET_QPUSH_BUTTON, "ButtonC's label") | |
var spinbox = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QDOUBLE_SPINBOX, "Price") | |
var entry = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLINE_EDIT, "User entry") | |
qt_widget_setText(buttonC, "ButtonC") | |
# Set button text using (UFS) universal function call | |
buttonA.qt_widget_setText("ButtonA") | |
buttonB.qt_widget_setText("ButtonB") | |
#--------- Install event handlers (callbacks) -----------# | |
var counter = 0 | |
qt_button_onClicked(buttonA, nil, (ctx: Context) => (block: | |
counter = counter + 1 | |
echo " [INFO] ButtonA clicked => counter = " & counter.intToStr | |
entry.qt_widget_setText "Counter changed to " & counter.intToStr | |
)) | |
buttonB.qt_button_onClicked(nil, (ctx: Context) => (block: | |
counter = counter - 1 | |
echo " [INFO] ButtonB clicked => counter = " & counter.intToStr | |
entry.qt_widget_setText "Counter changed to " & counter.intToStr | |
)) | |
buttonC.qt_button_onClicked(nil) do (ctx: Context): | |
counter = 100 | |
qt_msgbox_info( nil | |
, "Button A notification" | |
, "[INFO] ButtonC => reset counter to = " & counter.intToStr) | |
entry.qt_widget_setText " [BUTTON C] Counter changed to " & counter.intToStr | |
echo " [BUTTON C] Counter changed to " & counter.intToStr | |
spinbox.qt_QDoubleSpinBox_onValueChanged(nil) do (ctx: Context) -> void: | |
let value = spinbox.qt_QDoubleSpinBox_value | |
# echo " [INFO] Spbinbox Value changed to ", value | |
entry.qt_widget_setText "Spinbox value changed to = " & $value | |
#---------- Run QT applicaton ---------------------------# | |
# | |
# Display root widget | |
qt_widget_show(window) | |
# Run QT application and block current thread | |
qt_app_exec2(app) | |
# ---- Dispose QT objects -------------# | |
# | |
qt_qobject_del(window) | |
qt_qobject_del(app) | |
echo " [TRACE] Application terminated. Ok." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Necessary for defining lambdas in Nim, otherwise compilation errors happen. | |
import sugar | |
# For converting number to string | |
import strutils | |
const qlib = "libqtwrapper.so" | |
type | |
QApplication = pointer | |
QLineEdit = pointer | |
QWidget = pointer | |
QPushButton = pointer | |
QFormLayout = pointer | |
Context = pointer | |
type | |
callback_clicked = proc(ctx: Context): void | |
const | |
WIDGET_WINDOW = 1 | |
WIDGET_WINDOW_MAIN = 2 | |
WIDGET_QPUSH_BUTTON = 3 | |
WIDGET_QLABEL = 4 | |
WIDGET_QLINE_EDIT = 5 | |
WIDGET_QDOUBLE_SPINBOX = 6 | |
proc qt_app_new2(): QApplication {.importc: "qt_app_new2", dynlib: qlib} | |
proc qt_app_exec2(qapp: QApplication): void {.importc: "qt_app_exec", dynlib: qlib} | |
proc qt_qobject_del(self: QWidget): void {.importc: "qt_qobject_del", dynlib: qlib} | |
# Set text and label | |
proc qt_widget_setText(self: QWidget, text: cstring): void | |
{.importc: "qt_widget_setText", dynlib: qlib} | |
# Factory function for instantiating any QT widget | |
proc qt_widget_new(parent: QWidget, ntype: cint): QPushButton | |
{.importc: "qt_widget_new", dynlib: qlib} | |
proc qt_widget_show(self: QWidget): void | |
{.importc: "qt_widget_show", dynlib: qlib} | |
# void qt_button_onClicked(QAbstractButton* self, void* ctx, void (* callback) (void*) ); | |
proc qt_button_onClicked(self: QPushButton, ctx: Context, callback: callback_clicked ): void | |
{.importc: "qt_button_onClicked", dynlib: qlib} | |
proc qt_msgbox_info(parent: QWidget, title: cstring, text: cstring): void | |
{.importc: "qt_msgbox_info", dynlib: qlib} | |
# Retrieves text from QLineEdit widget | |
proc qt_QLineEdit_text(self: QLineEdit): cstring | |
{.importc: "qt_QLineEdit_text", dynlib: qlib} | |
#-------------------------------------------------# | |
# QT Application # | |
#-------------------------------------------------# | |
echo " [TRACE] Staring application OK." | |
var app = qt_app_new2() | |
# Create a button QPushButton object | |
var button = qt_widget_new(nil, WIDGET_QPUSH_BUTTON) | |
# qt_widget_setText(button, "Click to run robotic arm.") | |
var counter: int = 0 | |
# Using UFS - Universal function call | |
button.qt_widget_setText("Click me now") | |
# qt_button_onClicked(button, nil, (ctx: Context) => (block: | |
# counter = counter + 1 | |
# echo (" [INFO] Button was clicked by user. Ok. =>> Counter = " & strutils.intToStr(counter)) | |
# qt_msgbox_info(button, "Information", " Button was clicked => Counter = " & counter.intToStr() ) | |
# )) | |
# qt_button_onClicked(button, nil) do (ctx: Context): | |
# counter = counter + 1 | |
# echo (" [INFO] Button was clicked by user. Ok. =>> Counter = " & strutils.intToStr(counter)) | |
# qt_msgbox_info(button, "Information", " Button was clicked => Counter = " & counter.intToStr() ) | |
## Syntax sugar | |
## It is the same a: qt_button_onClicked(button, nil, callback_here) | |
button.qt_button_onClicked(nil) do (ctx: Context): | |
counter = counter + 1 | |
echo (" [INFO] Button was clicked by user. Ok. =>> Counter = " & strutils.intToStr(counter)) | |
qt_msgbox_info(button, "Information", " Button was clicked => Counter = " & counter.intToStr() ) | |
# Display root widget | |
qt_widget_show(button) | |
# Run QT application and block current thread | |
qt_app_exec2(app) | |
# ---- Dispose QT objects -------------# | |
# | |
qt_qobject_del(button) | |
qt_qobject_del(app) | |
echo " [TRACE] Application terminated. Ok." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
namespace myapp | |
{ | |
class Wrapper | |
{ | |
public const int WIDGET_WINDOW = 0; | |
public const int WIDGET_WINDOW_MAIN = 2; | |
public const int WIDGET_QPUSH_BUTTON = 3; | |
public const int WIDGET_QLABEL = 4; | |
public const int WIDGET_QLINE_EDIT = 5; | |
public const int WIDGET_QDOUBLE_SPINBOX = 6; | |
public const int LAYOUT_QFORM_LAYOUT = 3; | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_app_new2(); | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_app_exec(IntPtr self); | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_qobject_del(IntPtr self); | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_widget_new(IntPtr parent, int type); | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_window_main(); | |
[DllImport("qtwrapper")] | |
public static extern void qt_widget_setText(IntPtr self, string text); | |
[DllImport("qtwrapper")] | |
public static extern void qt_msgbox_info(IntPtr parent, string title, string text); | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_layout_new(IntPtr parent, int type); | |
[DllImport("qtwrapper")] | |
public static extern IntPtr qt_QFormLayout_addWidgetAndLabel(IntPtr parent, int type, string label); | |
// A C# delegate is similar to a C++ function pointer type. | |
public delegate void button_callback(IntPtr ctx); | |
[DllImport("qtwrapper")] | |
public static extern void qt_button_onClicked(IntPtr self, IntPtr ctx, button_callback callback); | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Console.WriteLine(" [TRACE] Program started Ok. "); | |
// Create a QApplication* C++ object | |
// This object must be instantiated before any other Qt Widgets object. | |
IntPtr qapp = Wrapper.qt_app_new2(); | |
Console.WriteLine(" [TRACE] qapp (IntPtr) value = {0} ", qapp); | |
// Create a Qt Window object QWidget* | |
IntPtr qwindow = Wrapper.qt_window_main(); | |
Wrapper.qt_widget_setText(qwindow, "My QtWidget window in C# dotnet core on Linux!"); | |
IntPtr form = Wrapper.qt_layout_new(qwindow, Wrapper.LAYOUT_QFORM_LAYOUT); | |
IntPtr btn1 = Wrapper.qt_QFormLayout_addWidgetAndLabel(form , Wrapper.WIDGET_QPUSH_BUTTON , ""); | |
Wrapper.qt_widget_setText(btn1, "Increment"); | |
IntPtr btn2 = Wrapper.qt_QFormLayout_addWidgetAndLabel(form, Wrapper.WIDGET_QPUSH_BUTTON, ""); | |
Wrapper.qt_widget_setText(btn2, "Messagebox"); | |
IntPtr text_entry = Wrapper.qt_QFormLayout_addWidgetAndLabel( form, Wrapper.WIDGET_QLINE_EDIT, "Result"); | |
// ------------ Set event handlers --------------// | |
//-----------------------------------------------// | |
int counter = 0; | |
// Set button 1 event handler | |
Wrapper.qt_button_onClicked(btn1, IntPtr.Zero, (IntPtr ctx) => | |
{ | |
String s = String.Format("Button clicked {0} times.", counter++); | |
Console.WriteLine(" [TRACE] QtPushButton cliked Ok. => " + s); | |
Wrapper.qt_widget_setText(text_entry, s); | |
}); | |
Wrapper.qt_button_onClicked(btn2, IntPtr.Zero, (IntPtr ctx) => | |
{ | |
Wrapper.qt_msgbox_info(IntPtr.Zero , "System Notification" , "Batch workload finished Ok." ); | |
}); | |
// Run application and block current thread. | |
Wrapper.qt_app_exec(qapp); | |
// ---- Dispose C++ objects -------------------// | |
// --------------------------------------------// | |
Wrapper.qt_qobject_del(qwindow); | |
Wrapper.qt_qobject_del(qapp); | |
Console.WriteLine(" [TRACE] Program terminated successfully Ok. "); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import ctypes | |
from ctypes import (c_int, c_int, c_char, c_void_p) | |
# ===== Aliases for making life easier ===========# | |
#-------------------------------------------------# | |
c_void = None # void | |
c_pconst_char = ctypes.POINTER(c_char) # const char* | |
c_pnull = c_void_p(0) # Null pointer | |
QObject = c_void_p # void* - void pointer | |
Qapp = c_void_p | |
QWidget = c_void_p | |
QLayout = c_void_p | |
# =========== Load shared library ================# | |
#--------------------------------------------------# | |
# Arguments of: ctypes.cdll.LoadLibrary(<LIBRARY>) | |
# | |
# On Linux, the operating system linker looks for | |
# the library at directories listed in $LD_LIBRARY_PATH | |
# environment variable or in default set of directories | |
# such as /lib, /usr/lib and so on. | |
# | |
# On Windows, the operating system looks for the library | |
# in C:\\Windows\System32 directory or in directories | |
# listed in %PATH% environment variable. | |
# | |
# The argument of LoadLibrary() can also be the absolute | |
# path to the shared library. | |
# | |
lib = ctypes.cdll.LoadLibrary("libqtwrapper.so") | |
#======= Set types of C subroutines ===================# | |
#------------------------------------------------------# | |
# -- Note: There is no keyword const in Python | |
# it is assumed that the user or calling code | |
# will never modify the following definitions. | |
WIDGET_WINDOW = 0 | |
WIDGET_WINDOW_MAIN = 2 | |
WIDGET_QPUSH_BUTTON = 3 | |
WIDGET_QLABEL = 4 | |
WIDGET_QLINE_EDIT = 5 | |
WIDGET_QDOUBLE_SPINBOX = 6 | |
LAYOUT_QFORM_LAYOUT = 3 | |
lib.qt_app_new2.argtypes = [] | |
lib.qt_app_new2.restype = Qapp | |
lib.qt_app_exec.argtypes = [ Qapp ] | |
lib.qt_app_exec.restype = c_void | |
lib.qt_qobject_del.argtypes = [ QObject ] | |
lib.qt_qobject_del.restype = c_void | |
lib.qt_window_main.argtypes = [] | |
lib.qt_window_main.restype = QWidget | |
lib.qt_widget_setText.argtypes = [ QWidget, c_pconst_char ] | |
lib.qt_widget_setText.restype = c_void | |
lib.qt_layout_new.argytpes = [ QWidget, c_int ] | |
lib.qt_layout_new.restype = QLayout | |
lib.qt_msgbox_info.argtypes = [ QWidget, c_pconst_char, c_pconst_char ] | |
lib.qt_msgbox_info.restype = c_void | |
lib.qt_QFormLayout_addWidgetAndLabel.argtypes = [QWidget, c_int, c_pconst_char ] | |
lib.qt_QFormLayout_addWidgetAndLabel.restype = QWidget | |
# Callback type: | |
FnButtonCallback = ctypes.CFUNCTYPE( c_void # Return tupe | |
, c_void_p # Type of first parameter (void*) | |
) | |
lib.qt_button_onClicked.argtypes = [ QWidget, c_void_p, FnButtonCallback ] | |
lib.qt_button_onClicked.restype = c_void | |
#===== Create the Qt5 Widgets - GUI - Graphics User ==========# | |
#-------------------------------------------------------------# | |
qapp = lib.qt_app_new2() | |
window = lib.qt_window_main() | |
lib.qt_widget_setText(window, "Qt5 Widgets Window from Python3 FFI".encode("UTF-8")) | |
form = lib.qt_layout_new(window, LAYOUT_QFORM_LAYOUT) | |
label_output = lib.qt_QFormLayout_addWidgetAndLabel( | |
form, WIDGET_QLABEL, b"Result" ) | |
btnA = lib.qt_QFormLayout_addWidgetAndLabel( | |
form, WIDGET_QPUSH_BUTTON, b"Label A" ) | |
lib.qt_widget_setText(btnA, "Button A".encode("UTF-8") ) | |
btnB = lib.qt_QFormLayout_addWidgetAndLabel( | |
form, WIDGET_QPUSH_BUTTON, b"Label B") | |
lib.qt_widget_setText(btnB, b"Button B" ) | |
entry = lib.qt_QFormLayout_addWidgetAndLabel( | |
form, WIDGET_QLINE_EDIT, b"Information:") | |
spinbox = lib.qt_QFormLayout_addWidgetAndLabel( | |
form, WIDGET_QDOUBLE_SPINBOX, b"Price:") | |
print(" [PYTHON] Qt5 GUI Created Ok. ") | |
#===== Install callbacks (event handlers) ===================# | |
#------------------------------------------------------------# | |
counter = 0 | |
# Note: Python lacks multi-line lambdas | |
def btnA_callback(ctx: c_void_p) -> c_void: | |
global counter | |
counter = counter + 1 | |
text = f" [PYTHON] Button A Clicked => Counter = {counter} " | |
text = text.encode("UTF-8") | |
print(text) | |
lib.qt_widget_setText(label_output, text) | |
lib.qt_widget_setText(entry, text) | |
obj_btnA_callback = FnButtonCallback(btnA_callback) | |
lib.qt_button_onClicked(btnA, c_pnull, obj_btnA_callback) | |
def btnB_callback(ctx: c_void_p) -> c_void: | |
global counter | |
counter = counter - 1 | |
text = f" [PYTHON] Button B Clicked => Counter = {counter} " | |
text = text.encode("UTF-8") | |
print(text) | |
lib.qt_widget_setText(label_output, text) | |
lib.qt_widget_setText(entry, text) | |
lib.qt_msgbox_info( window, b"Information:", text) | |
obj_btnB_callback = FnButtonCallback(btnB_callback) | |
lib.qt_button_onClicked(btnB, c_pnull, obj_btnB_callback) | |
#====== Run QT event loop blocking current thread ============# | |
#-------------------------------------------------------------# | |
print(" [PYTHON] Running QT event loop => Main thread blocked!. ") | |
lib.qt_app_exec(qapp) | |
#===== Dispose C++ objects loaded via FFI =====================# | |
#--------------------------------------------------------------# | |
# Note: Only the root object (wihout any parent element) needs | |
# to be disposed (aka released from memory). | |
lib.qt_qobject_del(window) | |
lib.qt_qobject_del(qapp) | |
print(" [PYTHON] Application terminated gracefully. Ok. ") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <QtWidgets> | |
#include <QApplication> | |
// #include <QtUiTools/QtUiTools> | |
#include <QSysInfo> | |
#include <QtConcurrent/QtConcurrent> | |
#include <functional> | |
#include <iostream> | |
#include <fstream> | |
#include <string> | |
#include <map> | |
/** extern "C" => Remove C++ name mangling and makes the function | |
* compatible with C. It enforces C linkage to the annotated symbol. | |
**-----------------------------------------------------------------*/ | |
#define EXPORT_C extern "C" | |
/** Delete any instance of a derived class of QObject | |
* Note: It can be used for deleting instances QWidget, QApplication or QLayout. | |
*/ | |
EXPORT_C | |
void qt_qobject_del(QObject* self) | |
{ | |
delete self; | |
} | |
EXPORT_C | |
const char* qt_qobject_ClassName(QObject* self) | |
{ | |
return self->metaObject()->className(); | |
} | |
EXPORT_C | |
void qt_qobject_dumpObjectInfo(QObject* self) | |
{ | |
self->dumpObjectInfo(); | |
} | |
EXPORT_C | |
void qt_qobject_dumpObjectTree(QObject* self) | |
{ | |
self->dumpObjectTree(); | |
} | |
EXPORT_C | |
void qt_qobject_print(QObject* self) | |
{ | |
qDebug() << " [QOBjec] " << self; | |
} | |
enum class WidgetType | |
{ | |
Window = 1 | |
, Window_main = 2 | |
, QPushButton = 3 | |
, QLabel = 4 | |
, QLineEdit = 5 | |
, QDoubleSpinBox = 6 | |
}; | |
using CtorFunction = std::function<QWidget* (QWidget* parent)>; | |
using CtorDatabase = std::map<int, CtorFunction>; | |
template<typename T> void register_ctor(CtorDatabase& db, int type) | |
{ | |
db[type] = [](QWidget* parent){ return new (std::nothrow) T(parent); }; | |
} | |
// Create an object of a QWidget given class, given its name | |
EXPORT_C | |
QWidget* qt_widget_new(QWidget* parent, int type) | |
{ | |
// 'static' => Initialize static object only once. | |
static const CtorDatabase ctordb = []{ | |
auto db = CtorDatabase{}; | |
register_ctor<QWidget>(db, (int) WidgetType::Window); | |
register_ctor<QPushButton>(db, (int) WidgetType::QPushButton); | |
register_ctor<QLabel>(db, (int) WidgetType::QLabel); | |
register_ctor<QLineEdit>(db, (int) WidgetType::QLineEdit); | |
register_ctor<QDoubleSpinBox>(db, (int) WidgetType::QDoubleSpinBox); | |
db[(int) WidgetType::Window_main] = [](QWidget* parent){ | |
QWidget* w = new (std::nothrow) QWidget(parent); | |
w->resize(500, 400); | |
w->setWindowTitle("MainWindow"); | |
w->show(); | |
return w; | |
}; | |
return db; | |
}(); | |
if(auto it = ctordb.find(type); it != ctordb.end()) | |
{ return it->second(parent); } | |
return nullptr; | |
} | |
EXPORT_C | |
QWidget* qt_window_main() | |
{ | |
QWidget* w = new (std::nothrow) QWidget{}; | |
w->resize(500, 400); | |
w->setWindowTitle("MainWindow"); | |
w->show(); | |
return w; | |
} | |
EXPORT_C | |
QLayout* qt_layout_new(QWidget* parent, int type) | |
{ | |
if(type == 1) return new (std::nothrow) QVBoxLayout(parent); | |
if(type == 2) return new (std::nothrow) QHBoxLayout(parent); | |
if(type == 3) return new (std::nothrow) QFormLayout(parent); | |
return nullptr; | |
} | |
EXPORT_C | |
QObject* qt_QFormLayout_addWidgetAndLabel(QFormLayout* self, int type, const char* label) | |
{ | |
QWidget* wdg = qt_widget_new(nullptr, type); | |
if(wdg == nullptr){ return nullptr; } | |
self->addRow(label, wdg); | |
return wdg; | |
} | |
EXPORT_C | |
void qt_QFormLayout_addRowItem(QFormLayout* self, QWidget* field) | |
{ | |
self->addRow(field); | |
} | |
EXPORT_C | |
QLabel* qt_QFormLayout_addLabel1(QFormLayout* self, const char* label_text) | |
{ | |
auto btn = new QLabel(label_text); | |
self->addRow(btn); | |
return btn; | |
} | |
EXPORT_C | |
QApplication* qt_app_new(int argc, char** argv) | |
{ | |
std::cout << " [TRACE] Create QAppliction Object Ok" << std::endl; | |
return new QApplication(argc, argv); | |
} | |
EXPORT_C | |
QApplication* qt_app_new2() | |
{ | |
std::cout << " [TRACE] Create QAppliction Object Ok" << std::endl; | |
static int argc = 1; | |
static const char* argv [] = { "dummy_app" }; | |
return new QApplication(argc, (char**) argv); | |
} | |
EXPORT_C | |
int qt_app_exec(QApplication* self) | |
{ | |
return self->exec(); | |
} | |
// -------- Wrappers for QWidget Class ------------------// | |
EXPORT_C | |
void qt_widget_show(QWidget* self) | |
{ | |
self->show(); | |
} | |
template<typename T> | |
static bool set_text(QWidget* self, const char* text) | |
{ | |
auto obj = qobject_cast<T>(self); | |
// Early return on Error. | |
if(obj == nullptr ){ return false; } | |
obj->setText(text); | |
return true; | |
} | |
EXPORT_C | |
void qt_widget_setText(QWidget* self, const char* text) | |
{ | |
if( set_text<QLabel*>(self, text)) return; | |
if( set_text<QLineEdit*>(self, text)) return; | |
if( set_text<QTextEdit*>(self, text)) return; | |
if( set_text<QMessageBox*>(self, text)) return; | |
if( set_text<QAbstractButton*>(self, text) ) return; | |
// logger().log() << " [TRACE] Called function " << __FUNCTION__ << std::endl; | |
self->setWindowTitle(text); | |
} | |
// -------- Wrappers for QPushButton Class ------------------// | |
// Install event handler (callback) for button clicked event | |
EXPORT_C | |
void qt_button_onClicked( // Pointer to button | |
QAbstractButton* self | |
// Context of the callback. Note: C does not support closures | |
// or stateful function pointer. All data needs to be passed | |
// as arguments | |
, void* ctx | |
// Pointer to callback function pointer. | |
, void (* callback) (void* ctx) ) | |
{ | |
QObject::connect(self, &QPushButton::clicked, [=]{ | |
callback(ctx); | |
}); | |
} | |
// Install event handler for | |
EXPORT_C | |
void qt_QLineEdit_onTextChanged( QLineEdit* self | |
, void* ctx | |
, void (* callback) (void* ctx, QLineEdit* self) ) | |
{ | |
QObject::connect(self, &QLineEdit::textChanged, [=]{ | |
callback(ctx, self); | |
}); | |
} | |
EXPORT_C | |
void qt_msgbox_info(QWidget* parent, const char* title, const char* text) | |
{ | |
QMessageBox::information(parent, title, text); | |
} | |
// Note: The string has to be released after with free() function. | |
EXPORT_C | |
const char* qt_QLineEdit_text(QLineEdit* self) | |
{ | |
return self->text().toLocal8Bit().constData(); | |
} | |
EXPORT_C | |
double qt_QDoubleSpinBox_value(QDoubleSpinBox* self) | |
{ | |
return self->value(); | |
} | |
EXPORT_C | |
void qt_QDoubleSpinBox_setValue(QDoubleSpinBox* self, double value) | |
{ | |
self->setValue(value); | |
} | |
EXPORT_C | |
void qt_QDoubleSpinBox_onValueChanged( | |
QDoubleSpinBox* self | |
, void* ctx | |
, void (* callback)(void* ctx) ) | |
{ | |
QObject::connect( self, qOverload<double>(&QDoubleSpinBox::valueChanged) | |
, [=](double x){ callback(ctx); } | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Non standard directive, but supported by most compiler to includer the | |
// header only once | |
#pragma once | |
// Type aliases | |
typedef void ANY; | |
typedef void QObject; | |
typedef void QApplication; | |
typedef void QPushButton; | |
typedef void QWidget; | |
typedef void QAbstractButton; | |
typedef void QLineEdit; | |
typedef void QLayout; | |
typedef void QFormLayout; | |
typedef void QLabel; | |
void qt_qobject_del(QObject* self); | |
void qt_qobject_dumpObjectInfo(QObject* self); | |
void qt_qobject_dumpObjectTree(QObject* self); | |
void qt_qobject_print(QObject* self); | |
QApplication* qt_app_new(int argc, char** argv); | |
QApplication* qt_app_new2(); | |
// Instantiate any QT widget by name | |
QWidget* qt_widget_new(QWidget* parent, int type); | |
void qt_widget_setText(QWidget* self, const char* text); | |
// QApplication | |
int qt_app_exec(QApplication* self); | |
// Any QtWidget | |
void qt_QWidget_show(QWidget* self); | |
void qt_QWidget_setToolTip(QWidget* self, const char* tooltip); | |
// QPushButton wrappers | |
QPushButton* qt_QPushButton_new(QWidget* parent, const char* label); | |
void qt_QPushButton_onClicked_test(QPushButton* self ); | |
// Abstract PushbButton | |
void qt_button_onClicked(QAbstractButton* self, void* ctx, void (* callback) (void*) ); | |
void qt_button_setText(QAbstractButton* self, const char* text); | |
const char* qt_QLineEdit_text(QLineEdit* self); | |
QLayout* qt_layout_new(QWidget* parent, int type); | |
QObject* qt_QFormLayout_addWidgetAndLabel(QFormLayout* self, int type, const char* label); | |
QLabel* qt_QFormLayout_addLabel1(QFormLayout* self, const char* label_text); | |
void qt_msgbox_info(QWidget* parent, const char* title, const char* text); | |
void qt_QLineEdit_onTextChanged( QLineEdit* self | |
, void* ctx | |
, void (* callback) (void* ctx, QLineEdit* self) ); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::os; | |
use std::path::Path; | |
use std::os::raw::{c_int, c_char, c_long, c_void, c_uint, c_short}; | |
use std::ffi::CString; | |
use std::mem; | |
type c_str = *const c_char; | |
type LPVOID = *mut c_void; | |
type QObject = LPVOID; | |
type QWidget = LPVOID; | |
type QLabel = LPVOID; | |
type QApplication = LPVOID; | |
type QPushButton = LPVOID; | |
// void* null pointer | |
const null_ptr: LPVOID = std::ptr::null_mut(); | |
const WIDGET_WINDOW: c_int = 0; | |
const WIDGET_WINDOW_MAIN: c_int = 2; | |
const WIDGET_QPUSH_BUTTON: c_int = 3; | |
const WIDGET_QLABEL: c_int = 4; | |
const WIDGET_QLINE_EDIT: c_int = 5; | |
const WIDGET_QDOUBLE_SPINBOX: c_int = 6; | |
const LAYOUT_QFORM_LAYOUT: c_int = 3; | |
// Function pointer type alias | |
// type qt_object_del_t = unsafe extern "C" fn(this: QObject) -> c_void; | |
#[no_mangle] | |
extern "C" fn btn_callback1(this: QWidget, ctx: LPVOID) -> LPVOID | |
{ | |
unsafe { | |
let mut& counter = ctx as *mut c_int; | |
*counter = *counter + 1; | |
println!(" [TRACE] Button clicked Ok => counter = {}", *counter); | |
} | |
return null_ptr; | |
} | |
fn main(){ | |
println!(" Hello world!"); | |
let qapp: QApplication = app_new(); | |
let button: QWidget = widget_new(null_ptr, WIDGET_QPUSH_BUTTON); | |
widget_set_text(button, "Click me now!"); | |
unsafe{ qt_widget_show(button) }; | |
let mut counter: c_int = 0; | |
unsafe{ qt_button_onClicked(button, counter as *mut _, btn_callback1); } | |
// Run QT event loop and block current thread | |
app_exec(qapp); | |
} | |
// void (* callback) (void* ctx) ) | |
type button_callback = unsafe extern "C" fn(this: QWidget, ctx: LPVOID) -> LPVOID; | |
#[link(name = "qtwrapper")] | |
extern "C" { | |
fn qt_object_del(this: QObject) -> c_void; | |
fn qt_app_new2() -> QApplication; | |
fn qt_app_exec(this: QApplication) -> c_void; | |
fn qt_widget_new(parent: QWidget, typ: c_int) -> QWidget; | |
fn qt_widget_show(this: QWidget) -> c_void; | |
fn qt_QPushButton_new(parent: QWidget, label: c_str) -> QPushButton; | |
fn qt_msgbox_info(parent: QWidget, title: c_str, text: c_str) -> c_void; | |
// void qt_widget_setText(QWidget* self, const char* text) | |
fn qt_widget_setText(this: QWidget, text: c_str); | |
// void qt_button_onClicked(QAbstractButton* self, void* ctx, void (* callback) (void*) ); | |
fn qt_button_onClicked(this: QWidget, ctx: LPVOID, callback: button_callback) -> c_void; | |
} | |
// -------- Safe wrappers to unsafe operations ------------// | |
fn app_new() -> QApplication | |
{ | |
let qapp: QApplication = unsafe { qt_app_new2() }; | |
return qapp; | |
} | |
fn app_exec(qapp: QApplication) | |
{ | |
unsafe{ qt_app_exec(qapp) }; | |
} | |
fn widget_set_text(this: QWidget, text: &str) | |
{ | |
let text = CString::new(text).unwrap(); | |
unsafe { | |
qt_widget_setText(this, text.as_ptr() ); | |
} | |
} | |
fn widget_new(parent: LPVOID, typ: c_int) -> QWidget | |
{ | |
let wdg: QWidget = unsafe{ qt_widget_new(parent, typ) }; | |
return wdg; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; ---- Experimental binding to Qt5 GUI Framework ----------;; | |
;; | |
(require :cffi) | |
(cffi:define-foreign-library qtw (:unix "libqtwrapper.so" )) | |
(cffi:use-foreign-library qtw ) | |
;; ------- Define bindings ---------------------------.. | |
(cffi:defcfun "qt_app_new2" :pointer) | |
(cffi:defcfun "qt_widget_new" :pointer (parent :pointer) (type :int)) | |
;; Signature: void qt_widget_show(QWidget* self) | |
(cffi:defcfun "qt_widget_show" :void (self :pointer)) | |
;; Signature: int qt_app_exec(QApplication* self) | |
(cffi:defcfun "qt_app_exec" :int (self :pointer)) | |
;; Signature: void qt_qobject_del(QObject* self) | |
(cffi:defcfun "qt_qobject_del" :void (self :pointer)) | |
;; Signature: void qt_widget_setText(QWidget* self, const char* text) | |
(cffi:defcfun "qt_widget_setText" :void (self :pointer) (text :string)) | |
;; Signature: QLayout* qt_layout_new(QWidget* parent, int type) | |
(cffi:defcfun "qt_layout_new" :pointer (self :pointer) (type :int)) | |
;; Signature: QObject* qt_QFormLayout_addWidgetAndLabel( | |
;; QFormLayout* self | |
;; , int type, const char* label) | |
(cffi:defcfun "qt_QFormLayout_addWidgetAndLabel" | |
:pointer (self :pointer) (type :int)) | |
;; Callback | |
;; --------------------------------------------------- | |
;; Signature: | |
;; ----------------------------------------------------- | |
;; void qt_button_onClicked( | |
;; QAbstractButton* self | |
;; , void* ctx | |
;; , void (* callback) (void* self) ) | |
(cffi:defcfun "qt_button_onClicked" | |
:void (self :pointer) (ctx :pointer) (callback :pointer)) | |
;; Signature: void qt_msgbox_info( QWidget* parent | |
;; , const char* title | |
;; , const char* text ) | |
(cffi:defcfun "qt_msgbox_info" | |
:void (parent :pointer) (title :string) (text :string)) | |
(defvar WIDGET-WINDOW-MAIN 2) | |
(defvar WIDGET-BUTTON 3) | |
(defvar WIDGET-LINE-EDIT 5) | |
(defvar LAYOUT-QFORM 3) | |
;; ----- Define variables --------------------------- ;; | |
;; | |
;; This function must always be called | |
;; before creating any widget. | |
(defvar qapp (qt-app-new2)) | |
;; Create main window | |
(defvar window (qt-widget-new (cffi:null-pointer) WIDGET-WINDOW-MAIN)) | |
(cffi:with-foreign-string (title "My LISP QT Window") | |
(qt-widget-settext window title)) | |
(defvar form (qt-layout-new window LAYOUT-QFORM)) | |
;; Note: Common Lisp is case insensitive | |
(defvar button (qt-QFormLayout-addWidgetAndLabel form WIDGET-BUTTON)) | |
(defvar line-edit (qt-QFormLayout-addWidgetAndLabel form WIDGET-LINE-EDIT)) | |
;; Set button label | |
(cffi:with-foreign-string (title "click me") | |
(qt-widget-settext button title)) | |
(defvar counter 0) | |
;; Define button callback | |
;; void (* callback) (void* self) ) | |
(cffi:defcallback button-callback | |
;; Return type of callback | |
:void | |
;; List containing callback arguments | |
( (ctx :pointer) ) | |
;; Function main body | |
(progn | |
;; Increment counter | |
(setf counter (+ counter 1)) | |
(cffi:with-foreign-string | |
(text (format nil "counter set to ~a" counter )) | |
(qt-widget-settext button text)) | |
(cffi:with-foreign-string | |
(title "User notification") | |
(cffi:with-foreign-string | |
(text "Counter clicked") | |
(qt-msgbox-info window title text))) | |
(print (format t "Counter set to = ~D \n" counter)) | |
)) | |
;; --- End of button callback() ----- ;; | |
(qt-button-onClicked button (cffi:null-pointer) | |
(cffi:callback button-callback)) | |
;; Run QT event loop and blocks current thread. | |
(qt-app-exec qapp) | |
;; Dispose C++ objects calling destructors | |
(qt-qobject-del window) | |
(qt-qobject-del qapp) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My contribution
@caiorss
Zig Client
File Content
How to Build
Tested on zig v0.10 or higher
Translate
qtwrapper.h
toqtwrapper.zig
File Content
Cleaned version: