GUI for php, or cross the written extension with a screenshot

    This article will focus on the practical application of the gtkPHP7 extension, which we wrote earlier in this article , and srcphp (a screenshot reader in php), written in this article . Several days have passed since the publication of the article where we wrote the gtkPHP7 extension. And I thought how to build this library so that it would be in the spirit of php. Simple and convenient to use, and also could satisfy (so far only mine) needs for it. I ask for details under cat.



    Today we will cover several topics:

    2. 1. The principle of expansion work
    2. Technical side of the solution
    3. Extension debug tools

    The principle of the extension


    The main goal was ease of use. As we all know, php works in one thread, there are, of course, extensions that allow us to create threads and work with them, but in the case of using threads, the difficulties associated with managing them are created. And in principle, while this is not necessary. Therefore, for me, the straightforwardness of the logic of work, i.e. if we call the class method:

    $result = $gtk->alert("Привет хабрахабр!");
    var_dump($result);
    

    Then we get the following result:


    And when you click on the ok button:


    That is, the concept is similar to PHP, launch-> execution of any sequential actions-> completion of the script.

    The technical side of the issue


    I decided to break the extension into the main part (main.cpp), and modules, each of which implements one or another widget for use in php. To do this, I created the src / folder, where the code itself lies, and transferred all the sources to a new directory. When the sources are migrated, we must correct the Makefile as follows, indicating where the files with the .cpp extension are located:

    SOURCES                         =       $(wildcard src/*.cpp)
    

    The following is an example of a class that implements the preview window:

    The same screen and in a hat

    File previewWindow.h

    class GtkPhpPreviewWindow  {
    private:
      Gtk::Window *mainWindow = nullptr; // это указатель на наше окно
    protected:
      int statusUpload = 0; // флаг нажатия кнопки upload
      int statusCancel = 0; // флаг нажатия на cancel
    public:
      std::string preview(char *fileSrc); // метод реализующий рэндэр окна
      void uploadClick(); // callback функции
      void cancelClick();
      std::string getStatusUpload(); // геттер в принципе лишний
    };
    

    Next, take a look at the file itself, namely, analyze the preview.

    listing preview
    /**
     * run preview window
     */
    std::string GtkPhpPreviewWindow::preview(char *fileSrc) {
        int argc = 0;
        char **argv = NULL;
        auto app = Gtk::Application::create(argc, argv, "org.gtkmm.gtkphp7");
        auto refBuilder = Gtk::Builder::create();
        try {
            refBuilder->add_from_file("picview.glade");
        } catch (const Glib::FileError &ex) {
            std::cerr << "FileError: " << ex.what() << std::endl;
        } catch (const Gtk::BuilderError &ex) {
            std::cerr << "BuilderError: " << ex.what() << std::endl;
        }
        refBuilder->get_widget("window1",mainWindow);
        Gtk::Image *image = nullptr;
        refBuilder->get_widget("preview", image);
        Gtk::Button *buttonUpload = nullptr;
        refBuilder->get_widget("upload", buttonUpload);
        buttonUpload->signal_clicked().connect(
                sigc::mem_fun(*this, &GtkPhpPreviewWindow::uploadClick)
        );
        Gtk::Button *buttonCancel = nullptr;
        refBuilder->get_widget("cancel", buttonCancel);
        buttonCancel->signal_clicked().connect(
                sigc::mem_fun(*this, &GtkPhpPreviewWindow::cancelClick)
        );
        image->set(fileSrc);
        app->run(*mainWindow);
        return getStatusUpload();
    }
    


    The method turned out to be quite long, so we will try to consider only key points.

        auto app = Gtk::Application::create(argc, argv, "org.gtkmm.gtkphp7");
        auto refBuilder = Gtk::Builder::create();
        refBuilder->add_from_file("picview.glade");
    

    With these three lines, we create the application instance and builder (something like a factory in php). And load the picview.glade file.

    Yes, I used glade, a form editor for gtk, to make forms easier.

    Preview in glade


    By creating a file that describes the position of the elements (or in other words layouts) in xml format. We just load it and take the elements we need:

        Gtk::Button *buttonCancel = nullptr;
        refBuilder->get_widget("cancel", buttonCancel); // получаем кнопку
        buttonCancel->signal_clicked().connect(
                sigc::mem_fun(*this, &GtkPhpPreviewWindow::cancelClick)  // обрабатываем клик на кнопку
        );
    

    In cancelClick:

    /**
     * callback cancel button
     */
    void GtkPhpPreviewWindow::cancelClick() {
        statusCancel = 1;
        delete mainWindow;
    }
    

    We remove the mainWindow and set the status of the cancel button as pressed. The final touch is to launch the application (in this case, display the window):

    app->run(*mainWindow);
    

    At this moment, a “loop” is created that keeps the window open and no longer runs. But at that moment when we click on the button, we delete mainWindow, and the program goes into the method:

    /**
     * get status upload
     * @return
     */
    std::string GtkPhpPreviewWindow::getStatusUpload() {
        if (statusUpload == 1) {
            return "upload";
        } else {
            return "cancel";
        }
    }
    

    which returns a string indicating what was clicked.

    Debug tools


    Probably every "C" programmer knows what gdb is, but not every php programmer knows about it, although it can be very useful, even if you are not involved in the development of "C". For example, I somehow found out why apache gave segfault and generated core dump ...

    So, gdb - gnu debuger debugger for c and c ++ can do a lot: set breakpoints, read memory dumps, build stack trace. So, imagine that your extension crashes with segfault, or rather php (since the extension is connected to the interpreter). It seems that everything compiled and should work. But no.

    First of all, we need to recompile our project, with the -g option. For this, I fixed the Makefile:

    COMPILER                        =       g++ -g

    And compiled with this option. After that, a php script was written that called core dump, and was run under gdb:

    $ gdb php
    $ /.. вывод gdb ../
    (gdb) run ./test.php  # так мы запускаем наш скрипт
    $ .... тут где то произошел coredump
    (gdb) bt # вызываем backtrace который и анализируем
    

    Thus, gdb shows us the error that occurred, and the backtrace, by which you can determine exactly what the problem is (at least I still have a small project).

    As a conclusion
    After debugging the extension, I integrated gui into the screenshoter:

    New code
    #!/usr/bin/php
    _gtk = new GtkPhp();
            $request = new Request();
            if(isset($argv[1]) && $argv[1] == '--getToken') {
                echo $request->getOauthLink();die;
            }
            $home = $_SERVER['HOME'];
            $this->_config = include($home . '/.config/scrphp/config.php');
            $this->_nameScreenshot = date('Y_m_d_G_i_s_') . 'screen.png';
            $this->scrot();
        }
        public function scrot() {
            $this->_gtk->alert("Здравствуйте, для того чтобы сделать скриншот нажмите Ok и выберите область, на экране");
            system('scrot -s /tmp/'.$this->_nameScreenshot);
            $resultPrev = $this->_gtk->preview('/tmp/'.$this->_nameScreenshot);
            if($resultPrev == 'upload') {
                $this->upload();
            } else {
                $this->scrot();
            }
        }
        public function upload() {
            $request = new Request();
            $result = $request
                ->setToken($this->_config['token'])
                ->setFileNameOnDisk($this->_nameScreenshot)
                ->setPathToFile('/tmp/'.$this->_nameScreenshot)
                ->upload()
                ->publicateFile();
            $url = $result['public_url'];
            $this->_gtk->alert("Ссылка на скрин:".$url);
        }
    }
    new screenShoter();
    


    This script works in three steps:

    1. Alert, which displays a hint on how to take a screenshot:



    2. After selecting a site and creating a screenshot, preview opens:



    3. After clicking on the upload button, an alert appears with the



    And link in conclusion.

    Links to repositories:
    1. gtkPHP7
    2. srcphp screenshot

    Dependencies:
    1. gtkmm3
    2. gtk3
    3. glade

    Also popular now: