В свободное время мне нравится создавать прототипы вещей.
Это позволяет узнать что-то новое.
Этот прототип является клиентом ресурса http://www.nonograms.ru/ , разработчик которого Чугунный К.
А.
/ КиберПризрак /.
Весь код доступен по адресу Гихаб .
На стороне C++ работайте с HTML и моделью галереи.
На стороне QtQuick есть визуализация.
На этот раз я решил копнуть глубже:
- Q_GADGET и его использование в Qml;
- есть ли жизнь без Qt WebKit;
- выбирать Элементы управления Qt Labs .
Что сделано:
- галерея кроссвордов;
- решение кроссворда.
- скриншоты;
- как получить HTML без Qt WebKit;
- как составить кроссворд без Canvas.
Обходимся без Qt WebKit
На сайте представлен кроссворд в виде матрицы:Затем JS-скрипты создают html-код кроссворда.var d=[[571,955,325,492], [6,53,49,55], [47,18,55,65], .
]]
Модуль 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 асинхронный и принимает функцию обратного вызова в качестве входного параметра для получения результата.
Создание кроссворда
Я решил представить кроссворд как можно большим количеством столбцов и строк.
Вверху обведены красным 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
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
}
}
}
}
}
}
тогда мы получим удвоение строк
Чтобы убрать удвоение строк, воспользуемся трюком с размерами Элемент И Прямоугольник .
Размер элемента фиксирован, чтобы он находился в ретрансляторе( Репитер ) все элементы были расположены точно.
Прямоугольник шире и выше на единицу, в зависимости от необходимости двойной строчки.
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 совпадают, что удваивает линию.
Поле кроссворда
От поля нам нужна сетка и обработка щелчков мышью.Введем состояние ячейки сетки:
- неактивный( по умолчанию );
- заштрихованный( ЗАТЕНЕННЫЙ );
- помечено пустым( ПРОЗРАЧНЫЙ ).
Код рисования ячеек: 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
-
Какой Ноутбук Предпочитает Хабра-Сообщество?
19 Oct, 24 -
Мамба + Бегун, Немного Цифр
19 Oct, 24