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

Речь в данной статье пойдёт, о практическом применение расширения gtkPHP7, написанное нами ранее в данной статье, и srcphp (скриншотера на php) Написаного в этой статье. С момента публикации статьи, где мы писали расширение gtkPHP7 прошло несколько дней. И я думал как построить эту библиотеку, что бы она была в духе php. Простой и удобной в использование, а так же могла удовлетворить (пока только мои) потребности в ней. За подробностями прошу под кат…
51ca838e29434b50b543d19289ae6eb0.png

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

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

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

то мы получим, такой результат:
7a5b7ebb834145f199092ff7df39b683.png
и при нажатие на кнопку ok
6d634bbecf75474fb75478f887c984ac.png
т.е. Концепция под стать PHP, запуск→выполнение каких любо последовательных действий→завершение скрипта.
Техническая сторона вопроса
Я решил разбить расширение: на главную часть (main.cpp), и модули каждый из которых реализуют тот или иной виджет для использования в php… Для этого я создал папку src/ где лежит собственно код, и все исходники перенёс в новую директорию. Когда исходники перенесены, мы должны поправить файл Makefile, следующим образом:
SOURCES                         =       $(wildcard src/*.cpp)

указав где лежат файлы с расширение .cpp
Далее приведу пример класса реализующего окно preview:
тот же скрин и в шапке
51ca838e29434b50b543d19289ae6eb0.png

Файл 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
1afd5d11ddc54a0ea5a0e892adc8eafd.png

Создав файл, описывающий положение элементов (или другими словами 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
_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. Алерт который выводит подсказку как сделать скриншот
1837631dc1114d70b8309a2474fae3c4.png
2. После выбора участка и создания скриншота, открываеться preview
33edfdb9dcca466e9ab178d76f6977e8.png
3. После нажатия на кнопку upload появляеться alert с ссылкой
7730dc3e79614954bd42bdf8fcef24a6.png

И в заключение:
Ссылки на репозитории:
1. gtkPHP7
2. srcphp скриншотер
Зависимости:
1. gtkmm3
2. gtk3
3. glade

Комментарии (0)

© Habrahabr.ru