Русский
Русский
English
Статистика
Реклама

Customization

Из песочницы Кастомные методы для массивов в JS

04.10.2020 00:22:46 | Автор: admin

Вступление


Всем доброго времени суток. Я думаю, вы заметили, что в массивах в JS довольно много хороших встроенных методов. Однако, зачастую, даже их не хватает. Например: мне бы хотелось проверять массивы на пересечение. Конечно, я могу писать так:

checkIntersections(arr1, arr2)

но гораздо удобнее было бы писать вот так:

arr1.checkIntersections(arr2)

Особенно это касается ситуаций, когда функция должна изменять массив, например удалять повторяющиеся элементы. В таком случае нам вообще придется записывать что-то вроде этого:

arr1 = deleteDuplicates(arr1);

Мне такая запись вообще не нравится. Она плохо читаема и не логична. Нужно сделать deleteDuplicates встроенным методом массива. Но как нам добавить свой метод? Ведь, как многие считают, массив это неизменяемая часть языка, наподобие операторов. К счастью это не так.

Исследуем массивы


Чтобы понять, как нам взаимодействовать с массивами. Мы для начала должны понять, что из себя представляет сам массив. Для этого давайте выведем его в консоль:

let arr = ["I", "love", "JS"]; console.log(arr);

Вот, что мы увидим:



Как видите, консоль показывает нам, что это массив с 3-я элементами. Но также мы видим, что рядом с ним есть кнопка раскрытия (треугольник). Если мы нажмем на нее, то увидим следующее:



Ничего не напоминает? Это же объект! Мы видим, что его свойствами являются числовые ключи и length. Поэтому мы и записываем: array[0]. Мы обращаемся к свойству объекта array с именем 0. Квадратные скобки нужны, поскольку это имя этого свойства представлено числом. А что это за Array(3)? Это строка показывает, экземпляром какого объекта является массив, то есть, с помощью какого класса он создан. Значит, все массивы это экземпляры класса Array. Обладая этими знаниями, давайте попробуем записать какой-нибудь свой метод.

Добавляем новый метод


Давайте сделаем метод, который будет проверять, есть ли в нашем массиве элемент переданный аргументом. Для этого в цикле переберем все элементы нашего массива и вернем true при обнаружении совпадения. Если по завершении цикла совпадение не будет обнаружено вернем false. Назовем этот метод checkElement. Итак, чтобы добавить его к массиву, обратимся к свойству prototype класса Array.

Array.prototype.checkElement = function(e) {};

Теперь пройдемся циклом по всем элементам. Сделать это очень просто:

for (var i = 0; i < this.length; i++) {this[i];}

Теперь, на каждом шаге цикла будем выполнять проверку на совпадение переданного элемента с элементом текущей итерации.

if (this[i] === e) {    return true;}

И наконец, если по завершении цикла совпадений не было обнаружено, вернем false. Вот, что мы получим в итоге.

Array.prototype.checkElement = function(e) {for (var i = 0; i < this.length; i++) {if (this[i] === e) {return true;}}return false;};

Давайте проверим его в действии.

console.log(arr.checkElement("I"));console.log(arr.checkElement("not"));

Все работает, но если бы все было так просто, было бы подозрительно.

Исправляем появившуюся ошибку


Давайте попробуем сделать следующее: переберем наш массив с помощью цикла for in.

for (let i in arr) {    console.log(i);}

Как видите, произошел косяк: JS стал видит наш новый метод в этом цикле. Разумеется, при использовании такого цикла в коде, это может привести к ошибке. Но почему JS видит наш метод, но не видит например метод push? Давайте вновь обратимся к консоли и выведем все методы нашего массива. Вот так:

console.log(arr.__proto__);

Вот, что мы увидим:



Как видите: встроенный методы отобразились более тусклым текстом чем наш метод. Это означает, что эти методы обладают особым свойством, которое делает их невидимыми для цикла for in. Но что это за свойство? Если мы прочитаем документацию о технологии Object.defineProperty на MDN (вот она), то заметим там параметр enumerable. Как мы можем узнать, если его значение установить как false, цикл for in не будет видеть свойство с этим параметром. Давайте так и сделаем, а чтобы не писать миллион одинаковых инструкций, применим ее ко всем методам.
Вот так:

Object.keys(Array.prototype).forEach(method => {Object.defineProperty(Array.prototype, method, {enumerable: false,});});

Здесь мы получаем массив ключей нашего объекта Array.prototype и, с помощью Object.defineProperty, делаем их невидимыми для цикла for in. Давайте проверим теперь.

console.log(arr.checkElement("I"));for (let i in arr) {console.log(i);}

Прекрасно! Все работает так, как мы и ожидали.

Итог


Итак, вот код, который у нас получился.

Array.prototype.checkElement = function(e) {for (var i = 0; i < this.length; i++) {if (this[i] === e) {return true;}}return false;};Object.keys(Array.prototype).forEach(method => {Object.defineProperty(Array.prototype, method, {enumerable: false,});});

Как видите, ничего сложного, но может очень сильно облегчить жизнь. Кроме того, можно добавлять кучу других методов под свои нужды.

Я думаю, таким же методом можно изменять и другие технологии в JS. Главное понять как они устроены.
Подробнее..
Категории: Javascript , Array , Customization , Arrays , Objects

Реализация двойной панели инструментов в QT

06.03.2021 18:05:18 | Автор: admin

Ссылка на исходный код

Задача и требования

  • Создание двойного "тулбара" (панели инструментов), положение разделителя которого меняло бы соотношение полей сплиттераа.
    Иными словами разделитель должен перетягиваться мышкой.

  • Положение разделителя тулбаров так же должно зависеть от положения сплиттера.

  • При наведении курсора на разделитель тулбаров должен меняться тип курсора на горизонтальный QT::SizeHorCursor

  • При перетягивании разделителя курсор так же должен менять свой тип на горизонтальный

  • В целом данный тулбар должен соответствовать типичному представлению пользователя о данном типе тулбаров, это сделает интерфейс простым и понятным

  • Приложение должно давать возможность пользователю менять размер окна

Реализация

Первый шаг

Создаем Qt Widgets application. Основы cpp и h файлов, с которыми мы работаем, сгенерируются без нашего участия.

Object Inspector

Выполнение задачи необходимо начать с построения структуры интерфейса.

Для корректного отображения виджета toolbar необходима хотя бы одна иконка, расположенная на нем. Иконку можете выбрать на свой вкус.

Параметры виджетов

У виджетов MainWindow, centralwidget и у обоих тулбаров задаем параметр Mouse tracking, как true. Это позволит перехватывать события нажатия мыши, отпускания мыши и передвижения мыши над тулбарами.

У обоих списков, что лежат в сплиттере, задаем параметр минимальной ширины. В моем случае этот параметр равен 200 пикселям. У сплиттера задаем параметр childrenCollapsible, как false. Эти действия необходимы для того, чтобы не было возможности "схлопнуть" одну из секций сплиттера или увести тулбар за пределы экрана. Ниже покажу: как это связано с реализацией cpp файла.

У тулбаров нужно установить параметр movable, как false, чтобы убрать дефолтное задание размеров тулбаров пользователем.

MainWindow.h

private:    Ui::MainWindow *ui;    int timerId = 0;    bool toolbar_dragged = false;

Под Ui::MainWindow *ui создаем две переменные: timerId и toolbar_dragged.
timerId необходима для хранения времени таймера, которое мы будем использовать.
toolbar_dragged определяет: тянет ли пользователь за разделитель

public:    MainWindow(QWidget *parent = nullptr);    ~MainWindow();private slots:    void on_splitter_splitterMoved(int pos, int index, bool windowResized  = true);    void mouseMoveEvent(QMouseEvent *event);    void mouseReleaseEvent(QMouseEvent *e);    void mousePressEvent(QMouseEvent *event);    void timerEvent(QTimerEvent *event);    void resizeEvent(QResizeEvent* event);

Задаем слоты и указываем входной параметр слота on_splitter_splitterMoved windowResized, как параметр, по умолчанию заданный, как true. В resizeEvent, событии изменения размера окна, будет вызываться on_splitter_splitterMoved при помощи emit. Позиция сплиттера при изменении окна меняется, потому и должна меняться позиция разделителя тулбаров. Но в случае изменения размера окна перетягивания разделителя не происходит, toolbar_dragged == false. Потому для случая, когда окно меняет размер, необходимо данное входное условие. Подробнее об этом в cpp файле.

MainWindow.cpp

В файл необходимо включить две библиотеки:

QMouseEvent нужна для обработки сообщений мыши
QWidget необходима для работы с виджетами приложения

#include <QMouseEvent>#include <QWidget>

Рассмотрим конструктор главного окна:

centralWidget()->layout()->setContentsMargins убирает отступы от границ окна, созданные добавлением layout-а, как центрального виджета.
setSpacing(2) нужен потому, что иконки на этом тулбаре не передают событие передвижения мыши в MainWindow, Отступ в 2 пикселя вполне достаточен, чтобы избавиться от этой проблемы.

MainWindow::MainWindow(QWidget *parent)    : QMainWindow(parent)    , ui(new Ui::MainWindow){    ui->setupUi(this);    centralWidget()->layout()->setContentsMargins(0, 0, 0, 0);    //this is nessesary    this->ui->toolBar_2->layout()->setSpacing(2);}

Функция on_splitter_splitterMoved задает фиксированный размер левого тулбара. Второй тулбар автоматически подстраивается под размер первого. Данный размер можно задавать многократно, что мы и делаем. Обычными resize и move методами это сделать нельзя.

В случае, если размер тулбара становится меньше, чем минимальный размер первого списка, или начинает прижимать второй список к левому краю окна так, что размер второго списка меньше минимального, изменение размера первого тулбара, а следовательно и положения разделителя между тулбарами - не происходит. Это необходимо, не смотря на то, что в QT дизайнере уже заданы минимальные параметры ширины для списков.

void MainWindow::on_splitter_splitterMoved(int pos, int /*index*/, bool windowResized){    if (((pos>this->ui->listWidget->minimumSize().width()) &&        (pos<(this->width() - this->ui->listWidget_2->minimumSize().width()))) || windowResized)    {        this->ui->toolBar->setMaximumSize(pos, ui->toolBar->rect().height());        this->ui->toolBar->setMinimumSize(pos, ui->toolBar->rect().height());    }}

Функция mouseReleaseEvent вызывается, когда пользователь отпускает мышь. После этого нужно приветси курсор к типу Qt::ArrowCursor и задать соответствующую переменную toolbar_dragged, как false.

void MainWindow::mouseReleaseEvent(QMouseEvent* /*e*/){    if (toolbar_dragged)    {        toolbar_dragged = false;        this->setCursor(Qt::ArrowCursor);    }}

mousePressEvent перехватывает событие нажатия мыши пользователем.

При срабатывании данной функции, если мышь находится на промежутке +-20 пикселей от разделителя сплиттера, на высоте тулбаров, toolbar_dragged становится true, а тип курсора меняется на соответствующий перетягиванию.

void MainWindow::mousePressEvent(QMouseEvent *event){    QList<int> currentSizes = this->ui->splitter->sizes();    if ((this->ui->toolBar_2->underMouse()) &&        (event->buttons() == Qt::LeftButton) &&        (event->pos().x() < (currentSizes[0]+20)))    {        this->ui->toolBar_2->movableChanged(true);        toolbar_dragged = true;        this->setCursor(Qt::SizeHorCursor);    }    else if ((this->ui->toolBar->underMouse()) &&             (event->buttons() == Qt::LeftButton) &&             (event->pos().x() > (currentSizes[0]-20)))    {        this->ui->toolBar->movableChanged(true);        toolbar_dragged = true;        this->setCursor(Qt::SizeHorCursor);    }}

mouseMoveEvent вызывается при перемещении мыши. Если происходит перетягивание разделителя (toolbar_dragged), то вызывает функцию on_splitter_splitterMoved для изменения размера тулбара, предварительно поменяв размеры сплиттера.

В противном случае, если перетягивания не происходит, но мышь находится на промежутке -2 +5 пикселей от разделителя сплиттера, на высоте тулбаров, с отступами от горизонтальных границ в два пикселя, то тип курсора меняется на SizeHorCursor. Если эти отступы не сделать, то мышь не будет менять тип с SizeHorCursor на ArrowCursor, даже если задать Mouse tracking параметр, как true, всем другим виджетам.

void MainWindow::mouseMoveEvent(QMouseEvent* event){    QList<int> currentSizes = this->ui->splitter->sizes();    if (toolbar_dragged)    {        QList<int> currentSizes = this->ui->splitter->sizes();        currentSizes[0] = event->pos().x();        currentSizes[1] = this->width() - event->pos().x();        this->ui->splitter->setSizes(currentSizes);        emit on_splitter_splitterMoved(event->pos().x(), 1, false);    }    else if ((event->pos().y() > (2+this->ui->toolBar->y())) &&             (event->pos().y() < (this->ui->toolBar->height()-2+this->ui->toolBar->y())) &&             (event->pos().x() < (currentSizes[0]+5)) &&             (event->pos().x() > (currentSizes[0]-10)))    {        this->setCursor(Qt::SizeHorCursor);    }    else    {        this->setCursor(Qt::ArrowCursor);    }}

resizeEvent - событие изменения размера окна. В этом событии нельзя вызвать on_splitter_splitterMoved, потому необходимо сделать таймер, который будет "выходить за пределы" resizeEvent, работать вне ее.

void MainWindow::resizeEvent(QResizeEvent* event){        if (timerId)        {            killTimer(timerId);            timerId = 0;        }        // delay beetween ends of resize and your action        timerId = startTimer(1);    QMainWindow::resizeEvent(event);}

timerEvent меняет размеры тулбара через on_splitter_splitterMoved. При изменении размера окна, положение разделителя сплиттера определяется автоматически

void MainWindow::timerEvent(QTimerEvent *event){    QList<int> currentSizes = this->ui->splitter->sizes();    this->ui->toolBar_2->adjustSize();    emit on_splitter_splitterMoved(currentSizes[0], 1,true);    killTimer(event->timerId());    timerId = 0;}
Подробнее..

Из песочницы Делаем Linux терминал красивым и удобным

20.08.2020 20:06:34 | Автор: admin
Все дистрибутивы Linux поставляются с функциональным и кастомизируемым эмулятором терминала. В интернете, а порой даже в самом терминале, есть масса готовых тем, чтоб он красиво выглядел. Однако, чтоб сделать из стандартного терминала (в любом DE, любого дистрибутива) нечто красивое и одновременно удобное и простое в использовании я потратил много времени. Итак, как же сделать дефолтный терминал удобным и приятным в использовании?

Добавляем функциональность


Командная оболочка


Большинство дистрибутивов поставляется со встроенным Bash. Используя дополнения можно сделать из него все что захотите, но гораздо проще этого добиться с Zsh. Почему?

  • Продвинутая механика автодополнения команд по нажатию <Tab> или <Arrow Up>. В отличие от Bash, вам не нужно это настраивать, все работает на высшем уровне прямо из коробки.
  • Множество готовых тем, модулей, плагинов и прочего. Настраиваемость через фреймворки (oh-my-zsh, prezto и др.), которые значительно расширяют возможности кастомизации и улучшения терминала. Опять же, всего этого можно добиться и в Bash, но для Zsh масса уже готовых материалов. Для Bash их значительно меньше, а некоторых вовсе нет.

Это основные причины, по которым я перешел с Bash на Zsh. Помимо этого, Zsh имеет много других плюшек.

Настройка Zsh


Для начала, установим Zsh (если он уже установлен, например, как в Manjaro, можете пропустить этот пункт):

sudo apt install zsh

Когда вам предложат установить Zsh как оболочка по-умолчанию, нажмите Y, чтоб подтвердить.

Oh-My-Zsh популярный и активно развивающийся фреймворк Zsh, который позволяет гибко настроить оболочку терминала. Установим его:

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

zsh: command not found: curl
Установите curl:
sudo apt install curl


Подсветка синтаксиса. Гораздо проще ориентироваться по содержимому терминала, когда разные части команд подсвечены разными цветами. Например, директории будут подчеркиваться, а команды выделяться цветом, отличным от обычного текста. Установим плагин zsh-syntax-highlighting:

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting

zsh: command not found: git
Установите git:

sudo apt install git


Чтоб плагин заработал, его надо подключить.

В файле ~/.zshrc меняем строку с plugins=:

plugins=(git zsh-syntax-highlighting)

Если такой строки нет добавьте её.

Готово! Получаем удобный и функциональный терминал. Теперь сделаем его визуально приятным.

Настраиваем внешний вид


Устанавливаем тему PowerLevel10K:

git clone https://github.com/romkatv/powerlevel10k.git $ZSH_CUSTOM/themes/powerlevel10k

Скачиваем и добавляем в систему шрифт JetBrains Mono Nerd (c иконками):
Выберитеодин из списка, в папке шрифт/complete выберите шрифт без Windows Compatible, с окончанием Mono.

Подключаем шрифт и тему.

Редактируем ~/.zshrc.

Если в файле эти строки уже есть замените их.

  • ZSH_THEME="powerlevel10k/powerlevel10k"
    
  • POWERLEVEL9K_MODE="nerdfont-complete"
    

Цвета. Важная часть оформления терминала цветовая схема. Я перебрал много разных схем, редактировал их, остановился на Monokai Dark. Не режет глаза, но при этом приятная и яркая. Список цветов:

[colors]# specialforeground      = #e6e6e6foreground_bold = #e6e6e6cursor          = #fffbackground      = #000# blackcolor0  = #75715ecolor8  = #272822# redcolor1  = #f92672color9  = #f92672# greencolor2  = #a6e22ecolor10 = #a6e22e# yellowcolor3  = #434648color11 = #7ea35f# bluecolor4  = #66d9efcolor12 = #66d9ef# magentacolor5  = #ae81ffcolor13 = #ae81ff# cyancolor6  = #adb3b9color14 = #62ab9d# whitecolor7  = #2AA198color15 = #2AA198

В разных терминалах по-разному меняется цветовая схема (обычно, это делается через настройки терминала), но порядок цветов везде одинаковый. Вы можете импортировать этот шаблон в формате Termite и экспортировать его для вашего терминала через terminal.sexy

Запускаем конфигурацию темы: p10k configure.
Настройте тему, выбирая варианты отображения, которые вам больше нравятся.

Финальный штрих меняем конфиг темы и заменяем встроенные цвета.

Редактируем файл ~/.p10k.zsh.

Если в файле эти строки уже есть замените их. Коды цветов можно получить командой

for i in {0..255}; do print -Pn \"%K{$i}  %k%F{$i}${(l:3::0:)i}%f \" ${${(M)$((i%6)):#3}:+$'\n'}; done

  • Отображение только текущей директории:

    typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_last
    
  • Фон блока с директорией:

    typeset -g POWERLEVEL9K_DIR_BACKGROUND=33
    
  • Цвета стрелок:

    typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=2
    

    и

    typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=1
    

  • Фон ветки Git:

    typeset -g POWERLEVEL9K_VCS_CLEAN_BACKGROUND=15
    


Источники


Документация PowerLevel10K
Онлайн дизайнер цветовых схем терминала
Различия между Bash и Zsh
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru