暇なときにプロトタイプを作成するのが好きです。 これにより、新しいことを学ぶことができます。 このプロトタイプは、リソースhttp://www.nonograms.ru/のクライアントであり、その開発者はCast Iron K.A. / KyberPrizrak /です。 すべてのコードはGiHubで入手できます。 C ++側では、ギャラリーモデルであるHTMLを使用します。 QtQuick視覚化の側面。
今回、私はそれを拾うことにしました:
- Q_GADGETおよびQmlでの使用。
- Qt WebKitのない生活があります。
Qt Labs Controlsを選択します。
行われたこと:
- クロスワードギャラリー。
- クロスワードパズルを解きます。
カットの下で考慮されます:
- スクリーンショット
- Qt WebKitなしでHTMLを取得する方法。
- Canvasなしでクロスワードパズルを作成する方法。
Qt WebKitなしで行う
このサイトでは、クロスワードパズルをマトリックス形式で提供しています。
var d=[[571,955,325,492], [6,53,49,55], [47,18,55,65], ...]]
さらにJSスクリプトはクロスワードhtmlコードを作成します。 WebKitモジュールは非推奨としてマークされています。 代わりに、Chromeプロジェクトに基づいたWeb Engineモジュールを使用することを提案します。
わずかな失望がすぐにあります。 Web Engineには、ページ上のDOMを操作するためのAPIがありません。 HTMLコードを解析するには、サードパーティのツール( C ++およびGumboのParsim HTML )を使用する必要がありました。
しかし、ページをロードし、必要な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メソッドは非同期で、入力パラメーターとしてコールバック関数を取り、結果を取得します。
クロスワードパズルの作成
Crosswordは、できるだけ多くの列と行を表示することにしました。 上部のサイズ3の10列は赤で囲まれ、サイズ3の10行は左に囲まれています。次に、これらの値に対してコードが処理されます。
クロスワードパズルは、いくつかの方法で実行できます。
- 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のサイズとインデントのサイズ( margin )から計算されます
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は、数字でグリッドを記述します。 クロスワードパズルを解決するためのplayingFieldフィールド。
メッシング
このように書く場合:
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 } } } } } }
その後、行が2倍になります
行の二重化を削除するには、サイズがItemおよびRectangleのトリックを使用します。 アイテムのサイズは固定されているため、Repeater( Repeater )ではすべての要素が正確に配置されます。 二重線の必要性に応じて、 長方形は幅が広く、高さが1つ大きくなります。
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 )が計算され、1ずつ増加し、5で割った余りが取られます。 5セルごとにRectangleとItemの幅または高さが同じであるため、線が2倍になります。
クロスワードフィールド
フィールドから、グリッドとマウスクリック処理が必要です。 メッシュセルの状態を紹介します。
- 非アクティブ( デフォルト );
- 影付き( SHADED );
- 空のマーク( CLEAR )。
非アクティブ状態から開始し、次の順序でマウスをクリックして変更します
セル描画コード:
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要素。空としてマークされたセルを表示します。 ここで、私は州を通して様々な州を説明する機会を使っています 。
MouseAreaが移行します。 それがすべてが始まった理由です。 計算(グリッド座標へのマウス座標の変換)、手動の再描画はありません。