Sample console app script to create a GUI from Glade

Share your advanced PureBasic knowledge/code with the community.
dougmo52usr
User
User
Posts: 67
Joined: Mon Jul 18, 2016 6:43 pm

Sample console app script to create a GUI from Glade

Post by dougmo52usr »

A console app that parses a glade file and generates a boiler plate pb file for experimenting with gtk and glade. Run it in a console as:

./GladeGUI.bin ./Part1.glade > Glade.pb

Code: Select all

; A console app that parses a glade XML file and generates 
; a boiler plate PureBasic file that can be used to test
; the glade file.
;
; As an example, parse Part1.glade and create Glade.pb.
; ./GladeGUI.bin ./Part1.glade > Glade.pb
Global hXml = #Null
Global GladeFile$ = #Null$
Global Globals$ = #Null$
Global Imports$ = #Null$
Global Init$ = #Null$
Global Handlers$ = #Null$

; text of a simple Main() routine to launch gtk
#Main$ = ~"Procedure Main()\n" +
         ~"  gtk_init_(#Null,#Null)\n" +
         ~"  InitGuiFromBuilder(GladeFile$)\n" +
         ~"  gtk_widget_show_all_(*window)\n" +
         ~"  gtk_main_()\n" +
         ~"  ProcedureReturn 0\n" +
         ~"EndProcedure\n\n" +
         ~"Main()\n\n"

; create an imports string, and 
; later replace libdir$ With path To gtk
#imports$ = ~"ImportC \"libdir$\"\n" +  
            ~"  gtk_builder_new()\n" +
            ~"  gtk_builder_add_from_file(builder,file.p-utf8,*error)\n" +
            ~"  gtk_builder_new_from_file(file.p-utf8)\n" + 
            ~"  gtk_builder_get_object(builder,id.p-utf8)\n" +
            ~"EndImport\n"

; someone clicked the "X" close app?
#destroy$ = ~"Procedure gtk_main_quit()\n" + 
            ~"  Debug #PB_Compiler_Procedure\n" +
            ~"  gtk_main_quit_()\n" +
            ~"EndProcedure\n"

; <requires lib="gtk+" version="3.0"/>
; spawn "find" to search for libgtk-3.so
Procedure$ FindLibGtk()
  Protected hPgm = RunProgram("/bin/sh", ~"-c \"/bin/find /usr -name libgtk-3.so\"", 
                              #Null$ , #PB_Program_Open | #PB_Program_Read),
            libgtk$ = #Null$
  If hPgm
    While ProgramRunning(hPgm)
      If AvailableProgramOutput(hPgm)
        libgtk$ = ReadProgramString(hPgm)
      EndIf
    Wend
    CloseProgram(hPgm)
  EndIf
  ProcedureReturn libgtk$
EndProcedure

; recurse through the xml
Procedure EnumerateObjects(hNode)
  Static id$, object$, signal$, handler$
  If hNode
    Protected nodeName$ = GetXMLNodeName(hNode)
    If nodeName$ = "object"
      id$ = GetXMLAttribute(hNode,"id")
      If id$
        object$ = "*" + id$
        Init$ = Init$ + "    " + object$ + 
                ~" = gtk_builder_get_object(*builder," + 
                ~"\"" + id$ + ~"\")\n"
        Globals$ = Globals$ + "Global " + object$ + ~" = #Null\n"
      EndIf
    ElseIf nodeName$ = "signal"
      signal$ = GetXMLAttribute(hNode,"name")
      handler$ = GetXMLAttribute(hNode,"handler")
      If signal$ And handler$
        Init$ = Init$ + Space(2) +  
                "  g_signal_connect_(" + object$ + 
                ~", \"" + signal$ + ~"\", @" + 
                handler$ + ~"(), #Null)\n"
        Handlers$ = Handlers$ + 
                    "Procedure " + handler$ + ~"(*widget,*data)\n" +
                    ~"  Debug #PB_Compiler_Procedure\n"
        If signal$ = "destroy"
          Handlers$ = Handlers$ + ~"  gtk_main_quit_()\n"
        EndIf
        Handlers$ = Handlers$ + ~"EndProcedure\n\n"
      EndIf
    EndIf
    EnumerateObjects(ChildXMLNode(hNode))
    EnumerateObjects(NextXMLNode(hNode))
  EndIf
EndProcedure

; a console app that parses a glade file and creates a rudimentary
; PB app to test it.
Procedure Main()
  Protected hConsole = OpenConsole("Glade Parser",#PB_UTF8)
  If hConsole    
    GladeFile$ = ProgramParameter()
    PrintN(~"Global GladeFile$ = \"" + GladeFile$ + ~"\"")
    PrintN("Global *builder = #Null")
    ; try to load the .glade file for parsing
    hXml = LoadXML(#PB_Any,GladeFile$)
    If hXml And XMLStatus(hXml) = #PB_XML_Success
      ; create a Procedure that will create the widgets and signal handlers
      ; found in the .glade file
      Init$ = ~"Procedure InitGuiFromBuilder(GladeFile$)\n"
      Init$ = Init$ + ~"  *builder = gtk_builder_new_from_file(GladeFile$)\n"
      Init$ = Init$ + ~"  if *builder\n"
      ; recursively parse the .glade file looking for:
      ; "object"s which are widgets, and
      ; "signal"s which are handlers
      EnumerateObjects(MainXMLNode(hXml))
      FreeXML(hXml)
      Init$ = Init$ + ~"    g_signal_connect_(*window1, \"destroy\", @gtk_main_quit(), #Null)\n"
      Init$ = Init$ + ~"  EndIf\nEndProcedure\n"
    ; LoadXML or XMLStatus failed
    Else
      PrintN("LoadXML of " + GladeFile$ + "failed")
      PrintN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
      PrintN("XMLError = " + XMLError(hXml))
      PrintN("XMLErrorLine = " + Str(XMLErrorLine(hXml)))
      PrintN("XMLErrorPosition = " + Str(XMLErrorPosition(hXml)))
      PrintN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
    EndIf  
    PrintN(Globals$)
    PrintN(ReplaceString(#imports$,"libdir$",FindLibGtk()))
    PrintN(#destroy$)
    PrintN(Handlers$)
    PrintN(Init$)
    PrintN(#Main$)
    CloseConsole()
  EndIf
EndProcedure

Main()
An xml file created in Glade.

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.0"/>
  <object class="GtkWindow" id="window1">
    <property name="visible">True</property>
    <property name="can-focus">False</property>
    <property name="window-position">center</property>
    <child>
      <!-- n-columns=3 n-rows=3 -->
      <object class="GtkGrid" id="grid1">
        <property name="visible">True</property>
        <property name="can-focus">False</property>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label" translatable="yes">button1</property>
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="receives-default">True</property>
            <signal name="clicked" handler="on_button1_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button2">
            <property name="label" translatable="yes">button2</property>
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="receives-default">True</property>
            <signal name="clicked" handler="on_button2_clicked" swapped="no"/>
          </object>
          <packing>
            <property name="left-attach">1</property>
            <property name="top-attach">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkEntry" id="entry1">
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="text" translatable="yes">Hello World!</property>
            <signal name="changed" handler="on_entry1_changed" swapped="no"/>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">2</property>
            <property name="width">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button3">
            <property name="label" translatable="yes">button3</property>
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="receives-default">True</property>
            <signal name="clicked" handler="on_button3_clicked" swapped="no"/>
            <signal name="enter" handler="on_button3_enter" swapped="no"/>
            <signal name="leave" handler="on_button3_leave" swapped="no"/>
          </object>
          <packing>
            <property name="left-attach">0</property>
            <property name="top-attach">1</property>
            <property name="width">2</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </object>
</interface>
pb file created by console app which includes widgets and signals found by enumerating the glade file.

Code: Select all

Global GladeFile$ = "./Part1.glade"
Global *builder = #Null
Global *window1 = #Null
Global *grid1 = #Null
Global *button1 = #Null
Global *button2 = #Null
Global *entry1 = #Null
Global *button3 = #Null

ImportC "/usr/lib/x86_64-linux-gnu/libgtk-3.so"
  gtk_builder_new()
  gtk_builder_add_from_file(builder,file.p-utf8,*error)
  gtk_builder_new_from_file(file.p-utf8)
  gtk_builder_get_object(builder,id.p-utf8)
EndImport

Procedure gtk_main_quit()
  Debug #PB_Compiler_Procedure
  gtk_main_quit_()
EndProcedure

Procedure on_button1_clicked(*widget,*data)
  Debug #PB_Compiler_Procedure
EndProcedure

Procedure on_button2_clicked(*widget,*data)
  Debug #PB_Compiler_Procedure
EndProcedure

Procedure on_entry1_changed(*widget,*data)
  Debug #PB_Compiler_Procedure
EndProcedure

Procedure on_button3_clicked(*widget,*data)
  Debug #PB_Compiler_Procedure
EndProcedure

Procedure on_button3_enter(*widget,*data)
  Debug #PB_Compiler_Procedure
EndProcedure

Procedure on_button3_leave(*widget,*data)
  Debug #PB_Compiler_Procedure
EndProcedure


Procedure InitGuiFromBuilder(GladeFile$)
  *builder = gtk_builder_new_from_file(GladeFile$)
  If *builder
    *window1 = gtk_builder_get_object(*builder,"window1")
    *grid1 = gtk_builder_get_object(*builder,"grid1")
    *button1 = gtk_builder_get_object(*builder,"button1")
    g_signal_connect_(*button1, "clicked", @on_button1_clicked(), #Null)
    *button2 = gtk_builder_get_object(*builder,"button2")
    g_signal_connect_(*button2, "clicked", @on_button2_clicked(), #Null)
    *entry1 = gtk_builder_get_object(*builder,"entry1")
    g_signal_connect_(*entry1, "changed", @on_entry1_changed(), #Null)
    *button3 = gtk_builder_get_object(*builder,"button3")
    g_signal_connect_(*button3, "clicked", @on_button3_clicked(), #Null)
    g_signal_connect_(*button3, "enter", @on_button3_enter(), #Null)
    g_signal_connect_(*button3, "leave", @on_button3_leave(), #Null)
    g_signal_connect_(*window1, "destroy", @gtk_main_quit(), #Null)
  EndIf
EndProcedure

Procedure Main()
  gtk_init_(#Null,#Null)
  InitGuiFromBuilder(GladeFile$)
  gtk_widget_show_all_(*window)
  gtk_main_()
  ProcedureReturn 0
EndProcedure

Main()