Японские Кроссворды На Qtquick



Японские кроссворды на QtQuick

В свободное время мне нравится создавать прототипы вещей.

Это позволяет узнать что-то новое.

Этот прототип является клиентом ресурса http://www.nonograms.ru/ , разработчик которого Чугунный К.

А.

/ КиберПризрак /.

Весь код доступен по адресу Гихаб .

На стороне C++ работайте с HTML и моделью галереи.

На стороне QtQuick есть визуализация.

На этот раз я решил копнуть глубже:

  • Q_GADGET и его использование в Qml;
  • есть ли жизнь без Qt WebKit;
  • выбирать Элементы управления Qt Labs .

    Что сделано:

  • галерея кроссвордов;
  • решение кроссворда.

Ниже разреза мы рассмотрим:
  • скриншоты;
  • как получить HTML без Qt WebKit;
  • как составить кроссворд без Canvas.
Скриншоты

Японские кроссворды на QtQuick



Японские кроссворды на QtQuick



Обходимся без Qt WebKit

На сайте представлен кроссворд в виде матрицы:
  
  
  
  
  
  
  
   

var d=[[571,955,325,492], [6,53,49,55], [47,18,55,65], .

]]

Затем JS-скрипты создают html-код кроссворда.

Модуль WebKit был помечен как устарел .

Вместо него предлагается использовать модуль Web Engine на базе проекта Chrome. Сразу вас ждет легкое разочарование.

Web Engine не имеет API для работы с DOM на странице.

Для парсинга HTML-кода мне пришлось использовать сторонние инструменты( Парсинг HTML в C++ и Gumbo ).

Но мы можем загрузить страницу, отрендерить ее и получить необходимый HTML.

QString getHtml(const QUrl& url) { QWebEnginePage page; QEventLoop loop; QObject::connect(&page, &QWebEnginePage::loadFinished, &loop, &QEventLoop::quit); page.load(url); loop.exec(); QTimer::singleShot(1000, &loop, &QEventLoop::quit); QString html; page.toHtml([&html, &loop](const QString& data){ html = data; loop.quit(); }); loop.exec(); return html; }

QTimer::singleShot здесь он используется для ожидания завершения страницы.

Метод toHtml асинхронный и принимает функцию обратного вызова в качестве входного параметра для получения результата.



Создание кроссворда



Японские кроссворды на QtQuick

Я решил представить кроссворд как можно большим количеством столбцов и строк.

Вверху обведены красным 10 столбцов размером 3. Слева обведены 10 строк размером 3. Далее код будет оперировать этими значениями.

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

  • рисовать на C++;
  • рисовать на JS и Canvas;
  • построить из базовых элементов (Item, Rectangle, MouseArea и т. д.)
Я выбрал последний вариант. Полный код

import QtQuick 2.5 import Qt.labs.controls 1.0 Item { clip:true property int margin: 20 property int fontSize: 12 property int ceilSize: 20; property int incCeilSize: ceilSize + 1 property color borderColor: "#424242" property int rows: 0; property int rowSize: 0; property int column: 0; property int columnSize: 0; implicitHeight : crossGrid.height+margin*2 implicitWidth : crossGrid.width+margin*2 function loadFromNonogramsOrg(url) { console.log("Load:"+url); crossword.formNanogramsOrg(url); } function showOnlyNaturalNumber(val) { return val > 0 ? val: " "; } function drawCrossword(){ var csize = crossword.size; if(csize.column() === 0 || csize.rows() === 0){ return; } console.log(csize.column() + "x" + csize.rows()); hRepeater.model = 0; rRepeater.model = 0; rowSize = crossword.rowSize(); columnSize = crossword.columnSize(); rows = csize.rows(); column = csize.column(); hRepeater.model = crossword.columnSize()*csize.column(); rRepeater.model = crossword.rowSize()*csize.rows(); bgImg.visible = true; } Image{ id: bgImg asynchronous: true visible: false height: parent.height width: parent.width source:"qrc:/wall-paper.jpg" } Grid { id: crossGrid anchors.centerIn: parent columns: 2 spacing: 2 rowSpacing: 0 columnSpacing: 0 Rectangle{ id:topLeftItm width: rowSize * ceilSize height:columnSize * ceilSize border.width: 1 border.color: borderColor color: "transparent" } Grid { id: cGrid rows: columnSize columns: column Repeater { id: hRepeater model: 0 Item { width: ceilSize; height: ceilSize property int rw : Math.floor(index/column) property int cn : Math.floor(index%column) property int prw: rw+1 property int pcm: cn+1 Rectangle{ height: (prw % 5 == 0) || (prw == columnSize) ? ceilSize : incCeilSize width: (pcm % 5 == 0) ? ceilSize : incCeilSize color: "transparent" border.width: 1 border.color: borderColor Text { anchors.centerIn: parent text:showOnlyNaturalNumber( crossword.columnValue(cn,rw)); font{ family: mandarinFont.name pixelSize: fontSize } } } } } } Grid { id: rGrid rows: rows columns: rowSize Repeater { id: rRepeater model: 0 Item { width: ceilSize; height: ceilSize property int rw : Math.floor(index/rowSize) property int cn : Math.floor(index%rowSize) property int prw: rw+1 property int pcn: cn+1 Rectangle{ height: prw % 5 == 0 ? ceilSize : incCeilSize width: (pcn % 5 == 0) || (pcn == rowSize) ? ceilSize : incCeilSize color: "transparent" border.width: 1 border.color: borderColor Text { anchors.centerIn: parent text:showOnlyNaturalNumber( crossword.rowValue(rw,cn)); font{ family: mandarinFont.name pixelSize: fontSize } } } } } } Rectangle{ id: playingField width: column * ceilSize height:rows * ceilSize border.width: 1 border.color: borderColor color: "transparent" Grid{ rows: rows columns:column Repeater { id: bRepeater model: rows * column Item { id: ceilItm width: ceilSize; height: ceilSize property int rw : Math.floor(index/column) property int cn : Math.floor(index%column) state: "default" Rectangle{ id: itmRec height: (rw+1) % 5 == 0 ? ceilSize : incCeilSize width: (cn+1) % 5 == 0 ? ceilSize : incCeilSize color: "transparent" border.width: 1 border.color: borderColor } Text{ id: itmTxt visible:false height: parent.height width: parent.width font.pixelSize: ceilSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text:"+" rotation:45 } MouseArea { anchors.fill: parent onClicked: { if(parent.state == "default"){ parent.state = "SHADED"; }else if(parent.state == "SHADED"){ parent.state = "CLEAR"; }else{ parent.state = "default"; } } } states: [ State{ name:"SHADED" PropertyChanges { target: itmRec; color: "black"; } PropertyChanges { target: itmTxt; visible: false; } }, State{ name:"CLEAR" PropertyChanges { target: itmRec; color: "transparent"; } PropertyChanges { target: itmTxt; visible: true; } } ] } } } } } Text{ visible: bgImg.visible anchors{ right: parent.right rightMargin: 10 bottom: parent.bottom } text:qsTr("Source: ")+"www.nonograms.ru" font{ family: hanZiFont.name pixelSize: 12 } } Connections { target: crossword onLoaded: { drawCrossword(); } } }

Основу представляет Item, размер которого рассчитывается из размера CrossGrid и размер отступа( допуск )

Item { clip:true implicitHeight : crossGrid.height+margin*2 implicitWidth : crossGrid.width+margin*2 /* .

*/ Image{ id: bgImg asynchronous: true visible: false height: parent.height width: parent.width source:"qrc:/wall-paper.jpg" } Grid { id: crossGrid anchors.centerIn: parent columns: 2 spacing: 2 /* .

*/ } }

Ээлемент CrossGrid

Японские кроссворды на QtQuick



Grid { id: crossGrid anchors.centerIn: parent columns: 2 spacing: 2 rowSpacing: 16 columnSpacing: 16 Rectangle{ id:topLeftItm color: "transparent" border.width: 1 border.color: borderColor /* .

*/ } Grid { id: cGrid /* .

*/ } Grid { id: rGrid /* .

*/ } Rectangle{ id: playingField /* .

*/ } }

topLeftItm прямоугольник, заполняющий пространство.

cGrid И rGrid описать сетку с числами.

игровая площадка поле для решения кроссворда.



Сетка

Если вы напишете это так:

Grid { id: cGrid rows: columnSize columns: column Repeater { id: hRepeater /* .

*/ Item { width: ceilSize; height: ceilSize Rectangle{ height: ceilSize width: ceilSize color: "transparent" border.width: 1 border.color: borderColor Text { anchors.centerIn: parent text: index font{ family: mandarinFont.name pixelSize: fontSize } } } } } }

тогда мы получим удвоение строк

Японские кроссворды на QtQuick

Чтобы убрать удвоение строк, воспользуемся трюком с размерами Элемент И Прямоугольник .

Размер элемента фиксирован, чтобы он находился в ретрансляторе( Репитер ) все элементы были расположены точно.

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



Repeater { id: hRepeater model: 0 Item { width: ceilSize; height: ceilSize property int rw : Math.floor(index/column) property int cn : Math.floor(index%column) property int prw: rw+1 property int pcm: cn+1 Rectangle{ height: (prw % 5 == 0) || (prw == columnSize) ? ceilSize : incCeilSize width: (pcm % 5 == 0) ? ceilSize : incCeilSize color: "transparent" border.width: 1 border.color: borderColor Text { anchors.centerIn: parent text:showOnlyNaturalNumber( crossword.columnValue(cn,rw)); font{ family: mandarinFont.name pixelSize: fontSize } } } } }

Здесь на основе индекса вычисляется строка ( RW ) и столбец( CN ), увеличиваются на единицу и берется остаток от деления на 5. То есть.

каждые 5 ячеек ширина или высота Rectangle и Item совпадают, что удваивает линию.



Поле кроссворда

От поля нам нужна сетка и обработка щелчков мышью.

Введем состояние ячейки сетки:

  • неактивный( по умолчанию );
  • заштрихованный( ЗАТЕНЕННЫЙ );
  • помечено пустым( ПРОЗРАЧНЫЙ ).

Мы начнем с неактивного состояния и изменим его, щелкнув мышью в следующей последовательности.



Японские кроссворды на QtQuick

Код рисования ячеек:

Item { id: ceilItm width: ceilSize; height: ceilSize property int rw : Math.floor(index/column) property int cn : Math.floor(index%column) state: "default" Rectangle{ id: itmRec height: (rw+1) % 5 == 0 ? ceilSize : incCeilSize width: (cn+1) % 5 == 0 ? ceilSize : incCeilSize color: "transparent" border.width: 1 border.color: borderColor } Text{ id: itmTxt visible:false height: parent.height width: parent.width font.pixelSize: ceilSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text:"+" rotation:45 } MouseArea { anchors.fill: parent onClicked: { if(parent.state == "default"){ parent.state = "SHADED"; }else if(parent.state == "SHADED"){ parent.state = "CLEAR"; }else{ parent.state = "default"; } } } states: [ State{ name:"SHADED" PropertyChanges { target: itmRec; color: "black"; } PropertyChanges { target: itmTxt; visible: false; } }, State{ name:"CLEAR" PropertyChanges { target: itmRec; color: "transparent"; } PropertyChanges { target: itmTxt; visible: true; } } ] }

itmTxt элемент, который добавляет к ячейке крестик и отображает ее как пустую.

Здесь появляется возможность описывать различные состояния через состояния .

МышьОбласть делает переход. Вот с чего все началось.

Никаких вычислений (преобразование координат мыши в ячейки сетки), никакой ручной перерисовки.

Теги: #Qt #qtquick #C++ #программирование #C++ #Qt

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.