Skip to content

Instantly share code, notes, and snippets.

@caiorss
Last active December 31, 2022 18:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save caiorss/a59d0f1b17d286dad19842932c931e92 to your computer and use it in GitHub Desktop.
Save caiorss/a59d0f1b17d286dad19842932c931e92 to your computer and use it in GitHub Desktop.
C-wrapper for Qt5 C++ GUI library
_build/*
bin/*
obj/*
*.so
*.o
*.obj
*.exe
*.bin
*.pyc
CMakeFiles/*
build/*
#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);
}
#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;
}
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 )
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;
}
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."
#!/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()
# 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/
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
# 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."
# 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."
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. ");
}
}
}
#!/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. ")
#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); }
);
}
// 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) );
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;
}
;; ---- 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)
@kassane
Copy link

kassane commented Dec 31, 2022

My contribution

@caiorss

Zig Client

File Content
//! all declarations and functions are order-independent

// C-API (single header)
const qtwrapper = @cImport(@cInclude("qtwrapper.h"));
// or
// Zig translate API
// const qtwrapper = @import("qtwrapper.zig");

pub fn main() void {

    // const argv = std.os.argv;
    // const qapp: ?*qtwrapper.QApplication = qtwrapper.qt_app_new(@intCast(c_int, argv.len), @ptrCast([*c][*c]u8, argv.ptr));
    const qapp: ?*qtwrapper.QApplication = qtwrapper.qt_app_new2();

    // -------- Create GUI - Graphical User Interface ------//

    const win: ?*qtwrapper.QPushButton = qtwrapper.qt_widget_new(null, WIDGET_WINDOW_MAIN);
    qtwrapper.qt_widget_setText(win, "Zig: Window Title");
    std.debug.assert(win != null);

    const form: ?*qtwrapper.QFormLayout = qtwrapper.qt_layout_new(win, LAYOUT_QFORM_LAYOUT);
    std.debug.assert(form != null);

    const lin: ?*qtwrapper.QLineEdit = qtwrapper.qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLINE_EDIT, "Input");
    const btn1: ?*qtwrapper.QPushButton = qtwrapper.qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "");
    const btn2: ?*qtwrapper.QPushButton = qtwrapper.qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "");
    const disp: ?*qtwrapper.QLabel = qtwrapper.qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLABEL, "Display label");

    qtwrapper.qt_widget_setText(btn1, "Button 1");
    qtwrapper.qt_widget_setText(btn2, "Button 2");
    // qtwrapper.qt_widget_setToolTip(btn1, "Click at this button to play!");

    qtwrapper.qt_qobject_print(win);

    // -------- Install Event Handlers --------------//
    //

    qtwrapper.qt_QLineEdit_onTextChanged(lin, disp, &lineEdit_Callback);
    qtwrapper.qt_button_onClicked(btn1, lin, &onclick_msgbox_callback);

    var ctx: CallbackContext = undefined;
    ctx.counter = 0;
    ctx.lin = lin.?;
    qtwrapper.qt_button_onClicked(btn2, &ctx, &onclicked_callback);

    // ---- Run QT event loop - blocking main thread  ---//
    _ = qtwrapper.qt_app_exec(qapp);

    // ----- Dispose objects ---------------------------//
    qtwrapper.qt_qobject_del(win);
    qtwrapper.qt_qobject_del(qapp);

    std.debug.print("\n [TRACE] Terminate application Ok.\n", .{});
}

// struct based C API
const CallbackContext = extern struct {
    lin: *qtwrapper.QLineEdit,
    counter: c_int,
};

// All callback (closure) state should be passed as arguments
fn onclicked_callback(context: ?*anyopaque) callconv(.C) void {

    //* pointer != ?* optional pointer (value or null)

    var ctx: ?*CallbackContext = @ptrCast(?*CallbackContext, @alignCast(std.meta.alignment(?*CallbackContext), context));
    ctx.?.*.counter += 1;
    const str: [*:0]const u8 = qtwrapper.qt_QLineEdit_text(ctx.?.*.lin);

    std.debug.print(" [TRACE] Button clicked => Counter incremented to: {d} \n", .{ctx.?.*.counter});
    std.debug.print(" [TRACE] QLineEdit Value = {s} \n", .{str});

    // qtwrapper.qt_msgbox_info(null, "Message Info", "Button clicked Ok");
}

fn lineEdit_Callback(ctx: ?*anyopaque, self: ?*qtwrapper.QLineEdit) callconv(.C) void {
    const str: [*:0]const u8 = qtwrapper.qt_QLineEdit_text(self);
    var label: ?*qtwrapper.QLabel = @ptrCast(?*qtwrapper.QLabel, ctx);
    qtwrapper.qt_widget_setText(label, str);
    std.debug.print(" [LineEdit Callback] Text changed to {s} \n", .{str});
}

fn onclick_msgbox_callback(context: ?*anyopaque) callconv(.C) void {
    const lin: *qtwrapper.QLineEdit = @ptrCast(*qtwrapper.QLineEdit, context);
    const str: [*:0]const u8 = qtwrapper.qt_QLineEdit_text(lin);
    qtwrapper.qt_msgbox_info(null, "Message Info", str);
}

const WIDGET_WINDOW: c_int = 1;
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;

const std = @import("std"); // order independent

How to Build

Tested on zig v0.10 or higher

# build
$> zig build-exe -O Release-Safe zig-client.zig -Llib/path -lqtwrapper -lc # (all flags are needed) -lc (link to libc OS)
# build & run
$> zig run -O Release-Safe zig-client.zig -Llib/path -lqtwrapper -lc # (all flags are needed) -lc (link to libc OS)

Translate qtwrapper.h to qtwrapper.zig

$> zig translate-c qtwrapper.h > qtwrapper.zig
File Content

Cleaned version:

pub const ANY = anyopaque;
pub const QObject = anyopaque;
pub const QApplication = anyopaque;
pub const QPushButton = anyopaque;
pub const QWidget = anyopaque;
pub const QAbstractButton = anyopaque;
pub const QLineEdit = anyopaque;
pub const QLayout = anyopaque;
pub const QFormLayout = anyopaque;
pub const QLabel = anyopaque;
pub extern fn qt_qobject_del(self: ?*QObject) void;
pub extern fn qt_qobject_dumpObjectInfo(self: ?*QObject) void;
pub extern fn qt_qobject_dumpObjectTree(self: ?*QObject) void;
pub extern fn qt_qobject_print(self: ?*QObject) void;
pub extern fn qt_app_new(argc: c_int, argv: [*c][*c]u8) ?*QApplication;
pub extern fn qt_app_new2() ?*QApplication;
pub extern fn qt_widget_new(parent: ?*QWidget, @"type": c_int) ?*QWidget;
pub extern fn qt_widget_setText(self: ?*QWidget, text: [*c]const u8) void;
pub extern fn qt_app_exec(self: ?*QApplication) c_int;
pub extern fn qt_QWidget_show(self: ?*QWidget) void;
pub extern fn qt_QWidget_setToolTip(self: ?*QWidget, tooltip: [*c]const u8) void;
pub extern fn qt_QPushButton_new(parent: ?*QWidget, label: [*c]const u8) ?*QPushButton;
pub extern fn qt_QPushButton_onClicked_test(self: ?*QPushButton) void;
pub extern fn qt_button_onClicked(self: ?*QAbstractButton, ctx: ?*anyopaque, callback: ?*const fn (?*anyopaque) callconv(.C) void) void;
pub extern fn qt_button_setText(self: ?*QAbstractButton, text: [*c]const u8) void;
pub extern fn qt_QLineEdit_text(self: ?*QLineEdit) [*c]const u8;
pub extern fn qt_layout_new(parent: ?*QWidget, @"type": c_int) ?*QLayout;
pub extern fn qt_QFormLayout_addWidgetAndLabel(self: ?*QFormLayout, @"type": c_int, label: [*c]const u8) ?*QObject;
pub extern fn qt_QFormLayout_addLabel1(self: ?*QFormLayout, label_text: [*c]const u8) ?*QLabel;
pub extern fn qt_msgbox_info(parent: ?*QWidget, title: [*c]const u8, text: [*c]const u8) void;
pub extern fn qt_QLineEdit_onTextChanged(self: ?*QLineEdit, ctx: ?*anyopaque, callback: ?*const fn (?*anyopaque, ?*QLineEdit) callconv(.C) void) void;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment