Sharing some thoughts on programming languages and gui toolkits.

For anyone using these as examples in the future: This is the same program I posted earlier, except the user interface is compiled inline as a string with the source code (rather than loaded in at runtime from an XML file).

[The easiest way to do this is to generate the UI as a glade {XML} file from glade, then replace all " with \" and remove all newline characters {i used the kate text editor to replace \n with nothing (a literal nothing, not a space or a \0 null character)}. Copy the result into the source code, as seen below.]


C:
//clang `pkg-config --cflags gtk+-3.0` -o gtk_example gtk_example.c `pkg-config --libs gtk+-3.0`
//can also use gcc
//This command won't work in the fish shell, use a different shell.

#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>


GtkBuilder *builder;

static void print_time (GtkWidget *widget, gpointer data)
{
    GObject *label;
    label = gtk_builder_get_object (builder, "label1"); //get the label1 GObject from the builder

    ///////////////////////////////////////Run A Command and Get the Output
    FILE *file_pointer;
    char variable[256];

    /* Open the command for reading. */
    file_pointer = popen("/bin/date \"+%r\"", "r");
    if (file_pointer == NULL) {
        printf("Didn't Work\n" );
        exit(1);
    }

    // Read in one line of file_pointer to variable
    fgets(variable, sizeof(variable), file_pointer);
     /* close */
    pclose(file_pointer);
    ///////////////////////////////////////I found this portion from a bing search.

    //set the label.
    g_object_set (label,"label",variable,NULL);  //a GObject, Property Name, Property Value, NULL indicates no further properties to parse.


}

static void print_hello_label (GtkWidget *widget, gpointer data)
{
    GObject *label;
    label = gtk_builder_get_object (builder, "label1");
    g_object_set (label,"label","Hello World!",NULL);
}

int main (int   argc, char *argv[])
{
  GObject *window;
  GObject *button;
  GError *error = NULL;

///////////////////////////
  const char ui_string[] = "<interface><object id=\"window\" class=\"GtkWindow\"><property name=\"visible\">True</property><property name=\"title\">Grid</property><property name=\"window-position\">center</property><property name=\"default-width\">400</property><property name=\"default-height\">300</property><property name=\"border-width\">5</property><child><object id=\"vbox\" class=\"GtkBox\"><property name=\"homogeneous\">True</property><property name=\"visible\">True</property><property name=\"orientation\">vertical</property><property name=\"vexpand-set\">True</property><property name=\"vexpand\">True</property><child><object id=\"hbox\" class=\"GtkBox\"><property name=\"homogeneous\">True</property><property name=\"visible\">True</property><property name=\"orientation\">horizontal</property><property name=\"hexpand-set\">True</property><property name=\"hexpand\">True</property><child><object id=\"button1\" class=\"GtkButton\"><property name=\"visible\">True</property><property name=\"label\">Get Time</property></object><packing><property name=\"expand\">True</property></packing></child><child><object id=\"button2\" class=\"GtkButton\"><property name=\"visible\">True</property><property name=\"label\">Say Hello</property></object><packing><property name=\"expand\">True</property></packing></child><child><object id=\"quit\" class=\"GtkButton\"><property name=\"visible\">True</property><property name=\"label\">Quit</property></object><packing><property name=\"expand\">True</property></packing></child></object><packing></packing></child><child><object id=\"label1\" class=\"GtkLabel\"><property name=\"visible\">True</property><property name=\"label\">Am I Connected?</property></object></child></object><packing></packing></child></object></interface>";
///////////////////////////

  gtk_init (&argc, &argv);

  /* Construct a GtkBuilder instance and load our UI description */
  builder = gtk_builder_new ();
  //if (gtk_builder_add_from_file (builder, "builder.ui", &error) == 0)
  if (gtk_builder_add_from_string (builder, ui_string, strlen(ui_string), &error) == 0)
    {
      g_printerr ("Error loading ui_string: %s\n", error->message);
      g_clear_error (&error);
      return 1;
    }

  /* Connect signal handlers to the constructed widgets. */
  window = gtk_builder_get_object (builder, "window");
  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);

  button = gtk_builder_get_object (builder, "button1");
  g_signal_connect (button, "clicked", G_CALLBACK (print_time), NULL);

  button = gtk_builder_get_object (builder, "button2");
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello_label), NULL);

  button = gtk_builder_get_object (builder, "quit");
  g_signal_connect (button, "clicked", G_CALLBACK (gtk_main_quit), NULL);

  gtk_main ();

  return 0;
}
 

Attachments

  • Screenshot_2021-05-09_12-06-19.png
    Screenshot_2021-05-09_12-06-19.png
    19.4 KB · Views: 91
Qt only binds to python or C++.
Not true. It also has ruby and perl bindings, and java bindings as well. And this is just cursory knowledge from using makefiles to compile KDE and related ports.

Oh, and is there a difference between a 'binding' and an 'API'? I get the impression that they're used interchangeably, but there's gotta be something that differentiates the two.
 
Ruby binding is discontinued. API is a list of function you can call.
I know what API is... what I want to know is how is that different from a 'binding'.
As a toolkit, Qt was originally written in C++. Can someone please confirm/correct for me what I'm thinking: 'binding' is some extra code inside the Qt toolkit, that needs to be there in compiled form, so that it's possbile for Qt's 'public' API to be called from a program written in a language other than C++.
 
I know what API is... what I want to know is how is that different from a 'binding'.
A "binding" is typically an API adapter closing a "language gap". In case of Qt, the native API is for C++. So, anything offering an API for a different language by "proxying" to the original C++ API would be called a "binding".
 
In practice, everything important is pretty much written in C or C++. As such, if you want to use another language, you need to be able to call into the compiled native C/C++ binary (usually a .so or .dll) and marshall data between your languages inbuilt data types.

Writing and (in particular) maintaining bindings is a very time consuming task so a number of project have ways of generating these bindings from the C/C++ header files or other meta data. Swig, Rust's Bindgen and Perls Xs are common ways.

Some libraries (such as Gtk) actively design their API in a way to help bindings. I.e less callbacks and using opaque data types (pointers to structures are easier to marshall than the structures themselves). I think Qt is attempting to do similar, but the fact it is written in C++ rather than C and has this weird MOC preprocessor layer is making this tricky to maintain.
 
Or wrapper. You call a function in your language X. And the wrapper calls the library function of the toolkit.
Gtk has introspection. Making it easier to write bindings then Qt.
 
I think Qt is attempting to do similar, but the fact it is written in C++ rather than C and has this weird MOC preprocessor layer is making this tricky to maintain.
It's manual work for sure. I'd love to see a "C binding" for Qt ;) Would be perfectly possible, but definitely a lot of work to maintain. I once tried (in a toy project) to define a simple GUI C API myself and had two backends for it, win32 and Qt. Well, it could display something (a window, some buttons, labels, etc), but I abandoned it soon :)

edit, "disclaimer": I'm really not a fan of C++, I think it's an ill-designed language. But the "Qt-flavor" of it is IMHO pretty ok (you can completely disable exceptions without any problems, and it adds something useful with signals/slots – slots playing especially nice with the newer C++-feature of lambdas).
 
It's manual work for sure. I'd love to see a "C binding" for Qt ;) Would be perfectly possible, but definitely a lot of work to maintain. I once tried (in a toy project) to define a simple GUI C API myself and had two backends for it, win32 and Qt. Well, it could display something (a window, some buttons, labels, etc), but I abandoned it soon :)
Let me guess, maintaining bindings for those things is overcomplicated because they have 80% of things that the main developer thought that could be "used later" but in practice will never be used, but is so tangled inside that you're forced to make it happen.
 
Looks like this can go both ways... I've seen Perl and Ruby in Ports as 'having Qt bindings'. But it looks like both Perl and Ruby are actually written in C to begin with, and so is Python... Things you learn while watching ports compile!
 
Let me guess, maintaining bindings for those things is overcomplicated because they have 80% of things that the main developer thought that could be "used later" but in practice will never be used, but is so tangled inside that you're forced to make it happen.
Not exactly. I guess my main problem was this typical "engineering" mindset to create an abstraction for everything, and by chosing win32 and Qt as backends, this became an impossible task very quickly ;) While win32 is as "bare-bones" as you can imagine (and full of weird stuff), Qt is pretty "high-level" abstract. What I wanted to do is to create cross-platform GUI applications in C(!) easily. I think this would be perfectly possible, but, too much work for a single person ;)
 
It's manual work for sure. I'd love to see a "C binding" for Qt ;) Would be perfectly possible, but definitely a lot of work to maintain. I once tried (in a toy project) to define a simple GUI C API myself and had two backends for it, win32 and Qt. Well, it could display something (a window, some buttons, labels, etc), but I abandoned it soon :)
Agreed, I am also fairly sure it would help the binding process to other languages in future too.
Nice. I have looked into doing similar in the past but it is just such a large amount of repetitive work. The worst thing is that it isn't particularly complex so that unfortunately doesn't keep us interested long enough to complete it either!

Perhaps if we can get ~50 developers together, each doing a tiny GUI component... it could get finished! ;)
 
Not exactly. I guess my main problem was this typical "engineering" mindset to create an abstraction for everything, and by chosing win32 and Qt as backends, this became an impossible task very quickly ;) While win32 is as "bare-bones" as you can imagine (and full of weird stuff), Qt is pretty "high-level" abstract. What I wanted to do is to create cross-platform GUI applications in C(!) easily. I think this would be perfectly possible, but, too much work for a single person ;)
I sometimes get lost in which way the abstraction needs to go. I can imagine C as being an abstraction over assembler, which in turn is an abstraction over simple commands like PUSH and ADD, which in turn are an abstraction over the logic gates like NAND and XOR. But when I look at abstraction diagrams for GUI toolkits - I quickly get lost in figuring out what comes first, second, and so forth.
 
The worst thing is that it isn't particularly complex
You should probably scope the project to be "just" C bindings for Qt. With my approach (abstraction layer over Qt and win32), it was complex. And sure enough, I failed (probably for losing interest to do all that crap)

(edit: well, in my very personal view, the ONE weakness of Qt is that it's natively C++. This language is ill-defined. GTK is pure C, but with GTK, I've seen a whole lot of problems, especially with portability to non-POSIX platforms. What I would absolutely love to see is something like Qt, but in plain good-old C)
 
Program which draws a circle,

Code:
import cairo.Context:Context;
import gtk.Widget:Widget;
import gtk.DrawingArea:DrawingArea;
import gtk.Application: GtkApplication=Application;
import gtk.ApplicationWindow:Scoped,ApplicationWindow,GApplicationFlags;
import gio.Application: GioApplication=Application;


import std.stdio:writeln;
import std.process:executeShell;
import std.typecons:Tuple;
import std.array:split;
import std.algorithm.mutation:remove;
import std.string:replace;

class DA:DrawingArea {
public:
    this()
        { addOnDraw(&drawCallback);}
protected:
    bool drawCallback(Scoped!Context c,Widget w){
        c.setSourceRgba(0.5,0.5,0.5,0.5);
        c.setLineWidth(3);
        c.arc(125,125,25,0,6.28);
        c.stroke();
        return true;
    }
}

int main(string [] args){
    GtkApplication application;
    string title="org.MyApp";
    void activateClock(GioApplication app){
        ApplicationWindow win = new ApplicationWindow(application);
        win.setTitle(title);
        win.setDefaultSize( 250, 250 );
        auto da = new DA();
        win.add(da);
        da.show();
        win.showAll();
    }
    application = new GtkApplication(title, GApplicationFlags.FLAGS_NONE);
    application.addOnActivate(&activateClock);
    return application.run(args);
}
 
here is my circle
Code:
clear;L=$(tput li);C=$(tput co);for i in $(jot 360);do res=$(echo "$C/2 + $L/3 *80/25*3/4* s($i*6.28/360);$L/2+ $L/3*c($i*6.28/360)"|bc -l);set -- $res;Y=${1%.*};X=${2%.*};tput cm $Y $X;echo "#";done
radius is a third of the terminal height; aspect ratio correction is assuming 80x25 fills a 4:3 screen
 

Attachments

  • Screen Shot 2021-05-21 at 3.28.56 PM.png
    Screen Shot 2021-05-21 at 3.28.56 PM.png
    32.4 KB · Views: 79
Program which draws a circle,
This dump truck of code is why I get lost. It takes that much just to draw a simple circle, let alone do anything like KTorrent, or kernel bug hunting. And even if it works (as in compiles with no complaints, runs without crashing, exits cleanly), there's still design diagrams after that (all that code needs to follow the logic specified in the design diagrams), and version control. For me, that does get to be a bit too much, even with the divide-and-conquer strategy.

To top it all off, there's lexical parsers that supposedly find bugs and security holes that still require verification and re-compiling - If you enjoy that kind of enchilada, then it's your cup of tea.
 
I've seen a whole lot of problems, especially with portability to non-POSIX platforms

Would you mind going over some of them? I'm just starting to dig into the GTK world (GNOME is growing on me), and I'm quite fond of it all being in C. At least it's something your average kernel developer can creep into. Cairo is in C as well.
 
Would you mind going over some of them? I'm just starting to dig into the GTK world (GNOME is growing on me), and I'm quite fond of it all being in C. At least it's something your average kernel developer can creep into. Cairo is in C as well.
I'd ask upstream devs of emulators/vice about it :D Didn't use GTK myself. But I remember they ran into problems like a file dialog getting stuck on windows because it found a "drive letter" that wasn't accessible. Or some threading scheme not working correctly on windows that worked quite fine on POSIX platforms. I really don't remember all the details 🤷‍♂️

Qt has a slight advantage here because it abstracts more than just the GUI.
 
35 years ago I programmed "sprites" on a C-64.
I still do 🤷‍♂️
View: https://www.youtube.com/watch?v=EaeMbr6TuXQ
(this features a "sprite multiplexer" cause the hardware only allows 8 sprites, but I needed more for the scrolltext)

(edit: graphics by "Almighty God" [aka Domingo], an awesome pixel artist from Spain. Everything else [code, music] by me)
 
Would you mind going over some of them? I'm just starting to dig into the GTK world (GNOME is growing on me), and I'm quite fond of it all being in C. At least it's something your average kernel developer can creep into. Cairo is in C as well.
At work we have a fairly substantial port of Gtk2 which sits on top of SDL1 and SDL2 (Most of the work is actually inside GDK). We haven't even had to gut too much. So it can be very portable.

However by default (and especially the later versions), upstream Gtk is awkward to compile up on Windows. For a long time they even dropped support for the MSVC compiler. Obviously that compiler is fairly underwhelming, unfortunately so many Win32 middleware developers have tied themselves down to it and thus it is generally important to support for GtkWin32.

Things are getting better however. Check out: https://www.collabora.com/news-and-...nd-run-gtk-4-applications-with-visual-studio/

(I am actually a big fan of our internal Gtk2/SDL. It is very portable now and completely cut off from the mad direction and mess that Gnome is going. Gtk2 theme engines are also vastly superior than that CSS nonsense. If I have time, I might see if I can push to get it opened).
 
Back
Top