Saturday, 2 March 2019

A Desktop GUI form with the PHP UI extension

Intro


As a follow on to last week's post, this week I am again exploring out the PHP UI extension which lets you create desktop PHP 7 programs. My mission this time is set up a basic form for letting you type some inputs in boxes, push a button and see an output somewhere else.

What's up, doc?


OK, so the only real documentation the UI extension seems to have (at the time of writing) are some pages on php.net website. There is also the "gallery.php" example file from the project itself, which shows you ways to put elements on a GUI window.



Keep It Simple, Stupid


I just want to make a simple window with a form and button. So, after playing around with some of the ways to do things demonstrated in the "gallery.php", I found out a way for beginners to do things but it's not perfect.

A beginner has a choice of trying things with Box, Group, Grid and Form elements. The Grid element is especially useful because it lets you set a size on elements it will contain, and position it in relation to other elements. However, Form is the thing to stick with when you first try things out. The full source code for the example can be found at the end of this blog post.

At the top of your PHP code file, you will need to insert "use" statements for the elements you're going to put in your window, e.g.

use UI\Window;
use UI\Controls\Form;



Showing Good Form


A Form element is created like this, and you can append more elements within it:

$entryForm1 = new Form();
$entryForm1->setPadded(true);
$entryForm1->append("", new Label("Enter text to match with a reg ex"));


I also want to multiline (MultilineEntry) inputs, which I'll append to the form later:

$haystackEntry = new MultilineEntry(MultilineEntry::Wrap);
$outputEntry = new MultilineEntry(MultilineEntry::Wrap);


I further have the need to put "spacing" rows between lines. I've tried various ways to do this but the quick way to this is just to append a blank Label, which act like an empty row, e.g.

$entryForm1->append("", new Label(""));

Push My Button


It's easy to add a button to a form. However, I need a button that will execute an specific action once its event is triggered, i.e. the button is clicked. To do this, it looks like we have extend the UI's Button class and put in the functionality we need it to do for us. Here's my effort:

/**
 * The class extends the UI's Button class, so there is an onClick method we can
  * use as an event handler.
 */
class ButtonSubmit extends Button {
   
    public function __construct(string $text,
        MultilineEntry $inputHaystackCtrl,
        MultilineEntry $inputRegexCtrl,
        MultilineEntry $outputCtrl
    ) {
        $this->inputHaystackCtrl = $inputHaystackCtrl;
        $this->inputRegexCtrl = $inputRegexCtrl;
        $this->outputCtrl = $outputCtrl;

        parent::__construct($text);
    }
   
    /**
     * When the button is clicked, take the "haystack" text from one entry input
     * and try to match it with the regular expression in the other entry input.
     * Show a result in the output input.
     */
    protected function onClick() {
        $matches = "";
        $haystackString = $this->inputHaystackCtrl->getText();
        $regExToMatch = $this->inputRegexCtrl->getText();
        preg_match($regExToMatch, $haystackString, $matches, PREG_OFFSET_CAPTURE);
        $outputMessages = ["You entered: " . $haystackString . "."];
        $outputMessages[] = "Number of matches: " . count($matches);

        $this->outputCtrl->setText(implode("\n", $outputMessages));
    }

    private $inputHaystackCtrl;
    private $inputRegexCtrl;
    private $outputCtrl;
};


Then add my button to the form:

$entryForm1->append("", $btnSubmit);

Open the Window


$window = new Window("Regex form", new Size(640, 480), true);
$window->add($entryForm1);

Then call the window show() method, and start the UI interaction loop:

UI\run();

When you run your php program, you'll see your form window appear. You'll also probably notice that the button element is unusually wide; we can't resize it. I did say the Form element was good to learn but not perfect ;-)


Get Trigger Happy


Enter some text in the "Text" mulitline input. The click the Submit button to see if the event gets triggered.


So, there you have it - a simple form with a button that executes an event. It might not be a competitor for Windows Forms, but it's a start.

Full Source Code


<?php
use UI\Size;
use UI\Window;
use UI\Controls\Form;
use UI\Controls\Button;
use UI\Controls\MultilineEntry;
use UI\Controls\Label;


// Lets make a button with custom features to handle inputs
/**
 * The class extends the UI's Button class, so there is an onClick method we can
  * use as an event handler.
 */
class ButtonSubmit extends Button {
   
    public function __construct(string $text,
        MultilineEntry $inputHaystackCtrl,
        MultilineEntry $inputRegexCtrl,
        MultilineEntry $outputCtrl
    ) {
        $this->inputHaystackCtrl = $inputHaystackCtrl;
        $this->inputRegexCtrl = $inputRegexCtrl;
        $this->outputCtrl = $outputCtrl;

        parent::__construct($text);
    }
   
    /**
     * When the button is clicked, take the "haystack" text from one entry input
     * and try to match it with the regular expression in the other entry input.
     * Show a result in the output input.
     */
    protected function onClick() {
        $matches = "";
        $haystackString = $this->inputHaystackCtrl->getText();
        $regExToMatch = $this->inputRegexCtrl->getText();
        preg_match($regExToMatch, $haystackString, $matches, PREG_OFFSET_CAPTURE);
        $outputMessages = ["You entered: " . $haystackString . "."];
        $outputMessages[] = "Number of matches: " . count($matches);

        $this->outputCtrl->setText(implode("\n", $outputMessages));
    }

    private $inputHaystackCtrl;
    private $inputRegexCtrl;
    private $outputCtrl;
};

/**
 * Let's build a Wdindow with a form in it
 */
function getRegexFormWindow() {
    $haystackEntry = new MultilineEntry(MultilineEntry::Wrap);
    $regExEntry = new MultilineEntry(MultilineEntry::Wrap);
    $regExEntry->setText("/(foo)(bar)(baz)/");
    $outputEntry = new MultilineEntry(MultilineEntry::Wrap);
    $btnSubmit = new ButtonSubmit("Submit", $haystackEntry, $regExEntry, $outputEntry);

    $entryForm1 = new Form();
    $entryForm1->setPadded(true);

    $entryForm1->append("", new Label("Enter text to match with a reg ex"));
    $entryForm1->append("", new Label(""));
    $entryForm1->append("Text", $haystackEntry, true);

    $entryForm1->append("", new Label(""));
    $entryForm1->append("Reg ex", $regExEntry, true);
   
    $entryForm1->append("", new Label(""));
    $entryForm1->append("", $btnSubmit);

    $entryForm1->append("", new Label(""));
    $entryForm1->append("Output", $outputEntry, true);
    $entryForm1->append("", new Label(""));

    $window = new Window("Regex form", new Size(640, 480), true);
    $window->setMargin(true);
    $window->add($entryForm1);
    return $window;
}

$regExWindow = getRegexFormWindow();
$regExWindow->show();

UI\run();



Thursday, 28 February 2019

Desktop PHP 7 with the UI extension & libui

Introduction


You may be like me and want to write your own desktop GUI programs with PHP 7 (i.e. not web programs in a browser). There are various options out there but many seem to have been abandoned or stuck with support only at PHP 5. In my search for desktop goodness I found the PHP extension "UI", which is a wrapper around a neat C library for creating desktop programs, "libui". The UI extension has the advantage of working with PHP 7.x.

The libui project can be found here.

 The PHP UI extension project can be found here.

What follows are instructions on how to first install libui, then the UI extension for PHP. I'm assuming you've got a bog standard PHP 7.2 set up already. I've tested this all out in Ubuntu 18.04 (and Lubuntu). I'm using the vim editor here, but you can try whichever editor suits you.

The C library, libui


On the command line, install packages:

sudo apt-get install build-essential cmake pkg-config libgtk-3-dev re2c git php7.2-dev


Then let's clone the libui project:

cd /opt
git clone https://github.com/andlabs/libui.git
cd libui
git checkout alpha3.5


And then compile the project:



mkdir build
cd build
cmake ..
make


Try it out:

View one of the example source C files that generates a windows with controls:

vim ../examples/controlgallery/main.c

Compile the example libui programs:


make examples
ls out

--> see the compiled files in the out directory

To run one:
out/controlgallery
--> see a window appear with example controls





Make the libui library available elsewhere:

sudo install ui.h /usr/local/include/;
sudo install ui_unix.h /usr/local/include/;
sudo install build/out/libui.so /usr/lib/;
sudo install build/out/libui.so.0 /usr/lib/;

 

The PHP UI extension

I had problems with errors when trying to install this as a PECL extension. So instead, let's manually clone the UI project:

cd /opt
git clone git://github.com/krakjoe/ui;
cd ui


There's a fix we have to make. Change a line in a file:

vim classes/box.c

Perform a line alteration near this:

#if PHP_VERSION_ID >= 70200
-ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ui_box_is_padded_info, 0, 0, _IS_BOOL, NULL, 0)
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(php_ui_box_is_padded_info, 0, 0, _IS_BOOL, 0)


Configure & compile the extension:

phpize
./configure
make


Find your PHP extensions directory:

php-config --extension-dir

--> e.g. see:
/usr/lib/php/20170718

Copy the compiled file to the PHP extensions directory:

cp modules/ui.so /usr/lib/php/20170718/

Configure PHP to use the extension:

Add an "ini" file for the extension:
vim /etc/php/7.2/mods-available/ui.ini
enter:
extension=ui.so

sudo phpenmod ui

php --ri ui

-see is enabled

See example PHP code to show a window with some controls:

vim examples/gallery.php

Run the example PHP file and try stuff out with your new PHP desktop greatness!

php examples/gallery.php




Next time I'll post an entry on writing a desktop app with UI. See the next post.

Note, thanks to this forum post below for help on getting things going:
https://github.com/krakjoe/ui/issues/43