Pull to refresh

GUI для php, или скрещиваем написанное расширение с скриншотером

Reading time 5 min
Views 4K
Речь в данной статье пойдёт, о практическом применение расширения gtkPHP7, написанное нами ранее в данной статье, и srcphp(скриншотера на php) Написаного в этой статье. С момента публикации статьи, где мы писали расширение gtkPHP7 прошло несколько дней. И я думал как построить эту библиотеку, что бы она была в духе php. Простой и удобной в использование, а так же могла удовлетворить (пока только мои) потребности в ней. За подробностями прошу под кат.



Сегодня мы затронем несколько тем:

2. 1. Принцип работы расширения
2. Техническая сторона решения
3. Инструменты дебага расширения

Принцип работы расширения


Главной целью было удобство использования. Как мы все знаем, php работает в одном потоке, есть, конечно, расширения, которые позволяют нам создавать потоки, и работать с ними, но в случае использование потоков рождаются сложности, связанные с управлением ими. Да и в принципе пока в этом нет необходимости. Поэтому для меня приоритетом стала прямолинейность логики работы, т.е. если мы вызываем метод класса:

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

То получим такой результат:


И при нажатии на кнопку ok:


То есть концепция под стать PHP, запуск->выполнение каких любо последовательных действий->завершение скрипта.

Техническая сторона вопроса


Я решил разбить расширение на главную часть (main.cpp), и модули, каждый из которых реализуют тот или иной виджет для использования в php. Для этого я создал папку src/, где лежит собственно код, и все исходники перенёс в новую директорию. Когда исходники перенесены, мы должны поправить файл Makefile следующим образом, указав, где лежат файлы с расширение .cpp:

SOURCES                         =       $(wildcard src/*.cpp)

Далее приведу пример класса реализующего окно preview:

Тот же скрин и в шапке

Файл 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(); // геттер в принципе лишний
};


Далее, загляним в сам файл, а именно — разберём 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();
}


Метод получился довольно длинным, по этому постараемся рассмотреть только ключевые моменты.

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

Этими тремя строчками мы создаём инстанс приложения и builder (что-то вроде фабрики в php). И подгружаем файл picview.glade.

Да, для облегчения создания форм я использовал glade — редактор форм для gtk.

Preview in glade


Создав файл, описывающий положение элементов (или другими словами layouts) в формате xml. Мы просто подгружаем его и берём нужные нам элементы:

    Gtk::Button *buttonCancel = nullptr;
    refBuilder->get_widget("cancel", buttonCancel); // получаем кнопку

    buttonCancel->signal_clicked().connect(
            sigc::mem_fun(*this, &GtkPhpPreviewWindow::cancelClick)  // обрабатываем клик на кнопку
    );

В cancelClick:

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

Мы удаляем mainWindow и ставим status кнопки cancel как нажата. Последним штрихом запускаем приложение (в данном случае отображаем окошко):

app->run(*mainWindow);

В этот момент, создаётся «цикл», который держит окно открытым, и недаёт выполняться дальше. Но в тот момент, когда мы кликаем по кнопке, мы удаляем mainWindow, и программа переходит в метод:

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

который возвращает строку указывающую на то что было нажато.

Инструменты дебага


Наверное каждый «C» программист знает что такое gdb, но не каждый php программист знает про него, хотя он бывает очень полезен, даже если вы не занимаетесь разработкой на «C». К примеру, я как то им узнал почему apache давал segfault и генерировал core dump…

Итак, gdb — gnu debuger дебагер для c и c++ умеет многое: устанавливать точки остановки, читать дампы памяти, строить stack trace. Так вот, представьте, что ваше расширение падает с segfault, а точнее php (так как расширение подключено к интерпретатору). Вроде всё, компилировалось и должно работать. Но нет.

Первым делом нам необходимо перекомпилировать наш проэкт, с опцией -g. Для этого я поправил Makefile:

COMPILER                        =       g++ -g

И скомпилировал с данной опцией. После чего был написан php скрипт, который вызывал core dump, и запущен под gdb:

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

Таким образом, gdb нам показывает ошибку, которая произошла, и backtrace, по которому можно точно определить, в чём проблема (покрайней мере мне пока проект небольшой).

В качестве заключения
После отладки, расширения я интегрировал gui в скриншотер:

Новый код
#!/usr/bin/php
<?php
require_once('classis/autoload.php');

class screenShoter {

    protected $_nameScreenshot = null;
    protected $_config = null;
    protected $_gtk = null;

    public function __construct()
    {
        $this->_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();



Этот скрипт работает в три шага:

1. Алерт, который выводит подсказку как сделать скриншот:



2. После выбора участка и создания скриншота открывается preview:



3. После нажатия на кнопку upload появляется alert с ссылкой



И в заключение.

Ссылки на репозитории:
1. gtkPHP7
2. srcphp скриншотер

Зависимости:
1. gtkmm3
2. gtk3
3. glade
Tags:
Hubs:
+2
Comments 1
Comments Comments 1

Articles